// happy octopus 
// input: transmit-receive (step response) 9600 baud rate 
// output: yellow LED from RGB LED
// this is a node board that communicate with a bridge board through serial 

// Xin Wen 

#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <string.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define input(directions,pin) (directions &= (~pin)) // set port direction for input
#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 102 // bit delay for 9600 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 settle_delay() _delay_us(100) // settle delay
#define char_delay() _delay_ms(10) // char delay
#define nloop 100 // loops to accumulate
#define PWM_delay() _delay_us(25) // PWM delay

// serial 
#define serial_port PORTA
#define serial_direction DDRA
#define serial_pin_out (1 << PA2)
#define serial_pin_in (1 << PA3)
#define serial_pins PINA

// input: txrx sensor 
#define transmit_port PORTA
#define transmit_direction DDRA
#define transmit_pin (1 << PA6)

//output: RGBLEDs 
#define led1_port PORTA
#define led1_direction DDRA 
#define led1_blue (1 << PA0)
#define led1_green (1 << PA1)
#define led1_red (1 << PA4)

#define led2_port PORTB
#define led2_direction DDRB 
#define led2_red (1 << PB0)
#define led2_green (1 << PB1)
#define led2_blue (1 << PB2)

#define node_id '2'

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte) {
   //
   // read character into rxbyte on pins pin
   //    assumes line driver (inverts bits)
   //
   *rxbyte = 0;
   while (pin_test(*pins,pin))
      //
      // wait for start bit
      //
      ;
   //
   // delay to middle of first data bit
   //
   half_bit_delay();
   bit_delay();
   //
   // unrolled loop to read data bits
   //
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 0);
   else
      *rxbyte |= (0 << 0);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 1);
   else
      *rxbyte |= (0 << 1);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 2);
   else
      *rxbyte |= (0 << 2);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 3);
   else
      *rxbyte |= (0 << 3);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 4);
   else
      *rxbyte |= (0 << 4);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 5);
   else
      *rxbyte |= (0 << 5);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 6);
   else
      *rxbyte |= (0 << 6);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 7);
   else
      *rxbyte |= (0 << 7);
   //
   // wait for stop bit
   //
   bit_delay();
   half_bit_delay();
   }

   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, PGM_P str) {
   //
   // send character in txchar on port pin
   //    assumes line driver (inverts bits)
   //
   static char chr;
   static int index;
   index = 0;
   do {
      chr = pgm_read_byte(&(str[index]));
      put_char(&serial_port, serial_pin_out, chr);
      ++index;
      } while (chr != 0);
   }

// display yellow light 
void displayLight(void){
    unsigned char count, pwm; 
    for (count = 0; count <40; ++count){
        clear(led1_port, led1_red); 
        clear(led1_port, led1_green); 
        for (pwm = count; pwm < 255; ++pwm)
            PWM_delay();
        // clear(led1_port, led1_blue); 
        // for (pwm = 0; pwm < count; ++pwm)
        //  PWM_delay(); 
        //set(led1_port, led1_red); 
        set(led1_port, led1_green); 
        set(led1_port, led1_red); 
        for (pwm = 0; pwm < count; ++pwm)
            PWM_delay(); 
    }
}

void turnOffLight(void){
    clear(led1_port, led1_red); 
    clear(led1_port, led1_green); 
    clear(led1_port, led1_blue); 
}

// void put_string(volatile unsigned char *port, unsigned char pin, PGM_P str) {
//    //
//    // send character in txchar on port pin
//    //    assumes line driver (inverts bits)
//    //
//    static char chr;
//    static int index;
//    index = 0;
//    do {
//       chr = pgm_read_byte(&(str[index]));
//       put_char(&serial_port, serial_pin_out, chr);
//       ++index;
//       } while (chr != 0);
//    }


int main(void){

    // variables setup
   // static unsigned char count;
   // static uint16_t up, down;
   // static int16_t lastup; 
   // static int16_t deltaup;// to track changes between loops
   // static int16_t diff;
   // static uint16_t upS; 
    static char chr;
    unsigned char count, pwm;
    
    // set clock divider to /1 (how does this code work?)
    CLKPR  = (1 << CLKPCE); 
    CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);

    // init LED pins 
    set(led1_port, led1_red); 
    output(led1_direction, led1_red); 
    set(led1_port, led1_green); 
    output(led1_direction, led1_green); 
    set(led1_port, led1_blue); 
    output(led1_direction, led1_blue); 

    set(led2_port, led2_red); 
    output(led2_direction, led2_red); 
    set(led2_port, led2_green); 
    output(led2_direction, led2_green); 
    set(led2_port, led2_blue); 
    output(led2_direction, led2_blue); 

    // initialize output pins
   //
   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);
   clear(transmit_port, transmit_pin);
   output(transmit_direction, transmit_pin);

   // init A/D
   //
   // ADMUX = (0 << REFS1) | (0 << REFS0) // Vcc ref
   //    | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (1 << MUX0) | (0 << MUX4) | (0 << MUX5); // PB3
   // ADCSRA = (1 << ADEN) // enable
   //    | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128
    // main loop

   while(1){
    // for (count = 0; count < 255; ++count) {
    //      clear(led1_port,led1_red);
    //      //clear(led2_port,led2_blue);
 
    //      for (pwm = count; pwm < 255; ++pwm)
    //         PWM_delay();
    //      set(led1_port,led1_red);
    //      //set(led2_port,led2_blue);

    //      for (pwm = 0; pwm < count; ++pwm)
    //         PWM_delay();
    //      }
    get_char(&serial_pins, serial_pin_in, &chr);
    if (chr == node_id){
        output(serial_direction, serial_pin_out);
         static const char message[] PROGMEM = "node ";
         put_string(&serial_port, serial_pin_out, (PGM_P) message);
         put_char(&serial_port, serial_pin_out, chr);
         put_char(&serial_port, serial_pin_out, 10); // new line
         for (count = 0; count < 255; ++count) {
         clear(led1_port,led1_red);
         clear(led2_port,led1_red);
         clear(led1_port,led1_green);
         clear(led2_port,led1_green);
         //clear(led2_port,led2_blue);
 
         for (pwm = count; pwm < 255; ++pwm)
            PWM_delay();
         set(led1_port,led1_red);
         set(led2_port,led1_red);
         set(led1_port,led1_green);
         set(led2_port,led1_green);
         //set(led2_port,led2_blue);

         for (pwm = 0; pwm < count; ++pwm)
            PWM_delay();
         }
         input(serial_direction, serial_pin_out);
    }
    // // accumulate
    //   //
    //   up = 0;
    //   down = 0;
    //   for (count = 0; count < nloop; ++count) { 
    //      //
    //      // settle, charge
    //      //
    //      settle_delay();
    //      set(transmit_port, transmit_pin);
    //      //
    //      // initiate conversion
    //      //
    //      ADCSRA |= (1 << ADSC);
    //      //
    //      // wait for completion
    //      //
    //      while (ADCSRA & (1 << ADSC))
    //         ;
    //      //
    //      // save result
    //      //
    //      up += ADC;
    //      //
    //      // settle, discharge
    //      //
    //      settle_delay();
    //      clear(transmit_port, transmit_pin);
    //      //
    //      // initiate conversion
    //      //
    //      ADCSRA |= (1 << ADSC);
    //      //
    //      // wait for completion
    //      //
    //      while (ADCSRA & (1 << ADSC))
    //         ;
    //      //
    //      // save result
    //      //
    //      down += ADC;
    //      }

    //   deltaup = up-down;
    //   diff = deltaup - lastup;
    //   if (diff<0) {upS = -diff;}
    //   else {upS = diff;}
    //   lastup = deltaup;

    //   if(diff>1000){
    //      output(serial_direction, serial_pin_out);
    //      put_char(&serial_port, serial_pin_out, 35); // send a # 
    //      put_char(&serial_port, serial_pin_out, node_id);
    //      put_char(&serial_port, serial_pin_out, 10); // new line
    //      PWM_delay();
    //      displayLight(); 
    //      input(serial_direction, serial_pin_out);
    //   }
   }


}