//
//
// serial_button.c
//
// 115200 baud FTDI connection that outputs '0' or '1' depending
// on the state of a physical button
//
// set lfuse to 0x5E for 20 MHz xtal
//
// Neil Gershenfeld
// 12/8/10
// Erik Strand
// 11/26/2018
//
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#include "OneWire.h"

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 8.5 // bit delay for 115200 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define char_delay() _delay_ms(10) // char delay
#define delay(duration) _delay_ms(duration); // drop-in replacement for Arduino delay

#define serial_port PORTA
#define serial_direction DDRA
#define serial_pins PINA
#define serial_pin_in (1 << PA0)
#define serial_pin_out (1 << PA1)

#define led_pin (1 << PB2)
#define button_pin (1 << PA7)

#define max_buffer 25

void put_char(volatile unsigned char *port, unsigned char pin, char txchar) {
    //
    // send character in txchar on port pin
    //    assumes line driver (inverts bits)
    //
    // start bit
    //
    clear(*port,pin);
    bit_delay();
    //
    // unrolled loop to write data bits
    //
    if bit_test(txchar,0)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,1)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,2)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,3)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,4)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,5)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,6)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    if bit_test(txchar,7)
    set(*port,pin);
    else
    clear(*port,pin);
    bit_delay();
    //
    // stop bit
    //
    set(*port,pin);
    bit_delay();
    //
    // char delay
    //
    bit_delay();
}

void put_string(volatile unsigned char *port, unsigned char pin, char *str) {
    //
    // print a null-terminated string
    //
    static int index;
    index = 0;
    do {
        put_char(port, pin, str[index]);
        ++index;
    } while (str[index] != 0);
}

OneWire  ds(11);  // on pin 10 (a 4.7K resistor is necessary)

//void setup(void) {
//    Serial.begin(9600);
//}

void loop(void) {
    byte i;
    byte present = 0;
    byte type_s;
    byte data[12];
    byte addr[8];
    float celsius, fahrenheit;

    if ( !ds.search(addr)) {
        put_string(&serial_port, serial_pin_out, "No more addresses.");
        ds.reset_search();
        delay(250);
        return;
    }

    //Serial.print("ROM =");
    put_string(&serial_port, serial_pin_out, "ROM=");
    for( i = 0; i < 8; i++) {
        put_char(&serial_port, serial_pin_out, ' ');
        //Serial.print(addr[i], HEX);
        put_string(&serial_port, serial_pin_out, "xxx");
        //put_string(&serial_port, serial_pin_out, addr[i])
    }

    if (OneWire::crc8(addr, 7) != addr[7]) {
        put_string(&serial_port, serial_pin_out, "CRC is not valid!");
        return;
    }
    //Serial.println();

    // the first ROM byte indicates which chip
    switch (addr[0]) {
        case 0x10:
        put_string(&serial_port, serial_pin_out, "  Chip = DS18S20");  // or old DS1820
        type_s = 1;
        break;
        case 0x28:
        put_string(&serial_port, serial_pin_out, "  Chip = DS18B20");
        type_s = 0;
        break;
        case 0x22:
        put_string(&serial_port, serial_pin_out, "  Chip = DS1822");
        type_s = 0;
        break;
        default:
        put_string(&serial_port, serial_pin_out, "Device is not a DS18x20 family device.");
        return;
    }

    ds.reset();
    ds.select(addr);
    ds.write(0x44, 1);        // start conversion, with parasite power on at the end

    delay(1000);     // maybe 750ms is enough, maybe not
    // we might do a ds.depower() here, but the reset will take care of it.

    present = ds.reset();
    ds.select(addr);
    ds.write(0xBE);         // Read Scratchpad

    put_string(&serial_port, serial_pin_out, "  Data = ");
    put_string(&serial_port, serial_pin_out, "xxx");
    //put_string(&serial_port, serial_pin_out, present, HEX);
    put_string(&serial_port, serial_pin_out, " ");
    for ( i = 0; i < 9; i++) {           // we need 9 bytes
        data[i] = ds.read();
        //Serial.print(data[i], HEX);
        put_string(&serial_port, serial_pin_out, " thing");
    }
    put_string(&serial_port, serial_pin_out, " CRC=");
    //put_string(&serial_port, serial_pin_out, OneWire::crc8(data, 8), HEX);

    // Convert the data to actual temperature
    // because the result is a 16 bit signed integer, it should
    // be stored to an "int16_t" type, which is always 16 bits
    // even when compiled on a 32 bit processor.
    int16_t raw = (data[1] << 8) | data[0];
    if (type_s) {
        raw = raw << 3; // 9 bit resolution default
        if (data[7] == 0x10) {
            // "count remain" gives full 12 bit resolution
            raw = (raw & 0xFFF0) + 12 - data[6];
        }
    } else {
        byte cfg = (data[4] & 0x60);
        // at lower res, the low bits are undefined, so let's zero them
        if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
        else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
        else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
        //// default is 12 bit resolution, 750 ms conversion time
    }
    celsius = (float)raw / 16.0;
    fahrenheit = celsius * 1.8 + 32.0;
    put_string(&serial_port, serial_pin_out, "  Temperature = ");
    //put_string(&serial_port, serial_pin_out, celsius);
    put_string(&serial_port, serial_pin_out, " Celsius, ");
    //put_string(&serial_port, serial_pin_out, fahrenheit);
    put_string(&serial_port, serial_pin_out, " Fahrenheit");
}

int main(void) {
    // Set clock divider to 1.
    CLKPR = (1 << CLKPCE);
    CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

    // Initialize output pins.
    set(serial_port, serial_pin_out);
    output(serial_direction, serial_pin_out);

    // Configure led pin as an output.
    DDRB |= led_pin;

    // Configure button_pin as an input.
    DDRA &= ~button_pin;

    // Activate button_pin's pullup resistor.
    PORTA |= button_pin;

    while (1) {
        // Turn on the LED when the button is pressed.
        if (PINA & button_pin) {
            // Turn off the LED.
            PORTB &= ~led_pin;
            //put_char(&serial_port, serial_pin_out, '0');
        } else {
            PORTB |= led_pin;
            //put_char(&serial_port, serial_pin_out, '1');
        }
        //_delay_us(10000);
        loop();
    }
}