/*
 * bus.node.45.sz.with2leds.c
 *
 * Created: 4/27/2014 1:41:10 PM
 *  Author: Scott Zitek
 */ 
// based on hello.bus.45.c
//
// 9600 baud serial bus hello-world
//
// Neil Gershenfeld
// 11/24/10
//
// (c) Massachusetts Institute of Technology 2010
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//

#define F_CPU 8000000UL
#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 led_delay() _delay_ms(100) // LED flash delay

//on my board there is a pushbutton connected to PA3
//#define input_port PORTA
//#define input_direction DDRA
//#define input_pins PINA
//#define input_pin (1 << PA3)

// on my board the green LED is PB0
#define led_port_a PORTB
#define led_direction_a DDRB
#define led_pin_a (1 << PB0)

// on my board the red LED is PB1
#define led_port_b PORTB
#define led_direction_b DDRB
#define led_pin_b (1 << PB1)


// sz - on my board, pin PB3 is bus TX and PB4 is bus RX (on example hello.bus.44, PB3 is bus TX and PB4 is bus RX
#define serial_port PORTB
#define serial_direction DDRB
#define serial_pins PINB
#define serial_pin_in (1 << PB3)
#define serial_pin_out (1 << PB4)

#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);
}

void flash() {
	//
	// LED flash delay
	//
	set(led_port_a, led_pin_a);
	set(led_port_b, led_pin_b);
	led_delay();
	clear(led_port_a, led_pin_a);
	clear(led_port_b, led_pin_b);
	led_delay();

}

void flash_a() {
	//
	// LED flash delay
	//
	set(led_port_a, led_pin_a);
	led_delay();
	clear(led_port_a, led_pin_a);
	led_delay();
}

void flash_b() {
	//
	// LED flash delay
	//
	set(led_port_b, led_pin_b);
	led_delay();
	clear(led_port_b, led_pin_b);
	led_delay();
}



int main(void) {
	//
	// main
	//
	static char chr;
	//
	// set clock divider to /1
	//
	CLKPR = (1 << CLKPCE);
	CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
	
	//
	// initialize input pins
	//
	
	//input(input_direction,input_pin); //makes PA3 (pin 10) an input to monitor my pushbutton - I don’t need pull-up resistor
	
	//
	// initialize output pins
	//
	set(serial_port, serial_pin_out);
	input(serial_direction, serial_pin_out);
	
	output(led_direction_a, led_pin_a);
	output(led_direction_b, led_pin_b);
	
	//
	// main loop
	//
	while (1) {
		   get_char(&serial_pins, serial_pin_in, &chr);
		   flash_a(); //flash green LED each time a character is received
		   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
			   led_delay();
			   flash_b();
			   input(serial_direction, serial_pin_out);
			   }
			else {
				if (chr == '5') {
				   output(serial_direction, serial_pin_out);
				   static const char message[] PROGMEM = "flash both";
				   put_string(&serial_port, serial_pin_out, (PGM_P) message);
				   put_char(&serial_port, serial_pin_out, 10); // new line
				   led_delay();
				   flash();
				   flash();
				   flash();
				   input(serial_direction, serial_pin_out);
			       }
			   }
   }
}