// http://www.sensirion.com/en/pdf/product_information/Sample_Code_humidity_sensor_SHTxx.pdf

/*
+++
ATAP2
ATWR
ATCN

+++
ATRE
ATSM1
ATMY0001
ATWR
ATCN

TODO: when PIN is shortened -- verbose = 1, and no power save

volt.regulator steady current: 87.5 uA
whole circuit: 94 uA sleep

active current @ 3.3V:
	RC@1MHz: 0.6 mA
	RC@8MHz: 3.0 mA
	idle @ RC@1MHz: 0.17 mA
	idle @ RC@8MHz: 0.70 mA
	pwrd + watchdog: 4 uA

sleep on every _delay_ms():
	sensor read wait: usual wait_time
		TEMP = 230 ms
		HUMI = 66 ms
		so just sleep for 500 ms and be safe ;)
	x-bee wake-up (15 ms) -- we can't sleep too much here, because the XBee is already consuming power
		-- any way to poll if XBee is ready and just not wait always for 15 ms?
	flush uart -- only IDLE sleep is supported (for 15 ms)
*/

#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>

#include <stdlib.h>
#include <string.h>

uint8_t crcdata[4];
uint8_t wdt_resets __attribute__ ((section (".noinit")));
uint8_t backoff __attribute__ ((section (".noinit")));
uint8_t xbee_enabled = 0;

#define SAVE_BATTERY
//#define VERBOSE

#ifdef PRINTF_TO_USART
#include <stdio.h>
#endif

// http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=791759
#ifdef __AVR_ATmega168__
	// F_OSC=1MHz, U2X0=1, rate=9600bps, error=0.2%
	#define UBRR0_VALUE	12U
	#define MCU_UCSRA UCSR0A
	#define MCU_UDR UDR0
	#define MCU_RXC RXC0
	#define MCU_UDRE UDRE0
#else
	#define BAUD 9600
	#include <util/setbaud.h>
	#define MCU_UCSRA UCSRA
	#define MCU_UDR UDR
	#define MCU_RXC RXC
	#define MCU_UDRE UDRE
#endif

#define DATA_PORT PORTB /* PORTx - register for button output */
#define DATA_PIN PINB	/* PINx - register for button input */
#define DATA_PBIT PB1	/* bit for button input/output */
#define DATA_DDR DDRB
#define DATA_DDB DDB1

#define SCK_PORT PORTB
#define SCK_PBIT PB0
#define SCK_DDR DDRB
#define SCK_DDB DDB0

#define DTR_PORT PORTB
#define DTR_PBIT PB2
#define DTR_DDR DDRB
#define DTR_DDB DDB2

#define CLK_DELAY_MS 0

#define noACK 0 
#define ACK   1

enum {TEMP,HUMI};

#define STATUS_REG_W 0x06
#define STATUS_REG_R 0x07
#define MEASURE_TEMP 0x03
#define MEASURE_HUMI 0x05
#define RESET        0x1e

void XBee_wakeup();
void XBee_sleep();
void put_into_sleep(uint8_t disable_peripherals, uint8_t sleep_mode, uint8_t sleep_time);
void s_connectionreset(void);

#ifdef __AVR_ATmega168__
void USART_init()
{
	// Set the baud rate
	UBRR0H = (UBRR0_VALUE >> 8);
	UBRR0L = UBRR0_VALUE;
	// set the framing to 8N1
	UCSR0C = (3 << UCSZ00);
	// Enable 2x speed change 
    UCSR0A = (1<<U2X0);
	// Engage!
	UCSR0B = (1 << RXEN0) | (1 << TXEN0);
}
#else
void USART_init() {
	UBRRH = UBRRH_VALUE;
	UBRRL = UBRRL_VALUE;	
	#if USE_2X
		// we expect that U2X is always used for our 1MHz @ 9600 baud rate
		UCSRA |= (1 << U2X);
	#else
		#error Not using U2X
		UCSRA &= ~(1 << U2X);
	#endif

	//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
	UCSRC=(1<<URSEL)|(0<<UMSEL)|(0<<UPM1)|(0<<UPM0)|(0<<USBS)|(0<<UCSZ2)|(1<<UCSZ1)|(1<<UCSZ0);
	
	//Enable Transmitter and Receiver
	UCSRB=(1<<RXEN)|(1<<TXEN);
}
#endif

void USART_tx(uint8_t c) {
	XBee_wakeup();
	//wait for empty transmit buffer
	while (!(MCU_UCSRA&(1<<MCU_UDRE))){};
	//send received data back
	MCU_UDR=c;
}

uint8_t USART_rx() {
	XBee_wakeup();
	//wait until data is received
	while(!(MCU_UCSRA&(1<<MCU_RXC))){};
	//store received data to temp
	return MCU_UDR;
}

void simple_puts(const char *s) {
	while (*s) {
		USART_tx(*s);
		++s;
	}
}

// ==========================================================================
// CRC Generation Unit - Linear Feedback Shift Register implementation
// (c) Kay Gorontzi, GHSi.de, distributed under the terms of LGPL
// ==========================================================================
char *MakeCRC(char *BitString) {
	static char Res[9];                                 // CRC Result
	char CRC[8];
	int  i;
	char DoInvert;

	for (i=0; i<8; ++i)  CRC[i] = 0;                    // Init before calculation

	for (i=0; i<strlen(BitString); ++i) {
		DoInvert = ('1'==BitString[i]) ^ CRC[7];        // XOR required?

		CRC[7] = CRC[6];
		CRC[6] = CRC[5];
		CRC[5] = CRC[4] ^ DoInvert;
		CRC[4] = CRC[3] ^ DoInvert;
		CRC[3] = CRC[2];
		CRC[2] = CRC[1];
		CRC[1] = CRC[0];
		CRC[0] = DoInvert;
	}

	for (i=0; i<8; ++i) Res[7-i] = CRC[i] ? '1' : '0'; // Convert binary to ASCII
	Res[8] = 0;                                        // Set string terminator

	return(Res);
}

const char *byte_to_binary(int x) {
	static char b[10];
	int z;

	b[0] = '\0';

	for (z = 256; z > 0; z >>= 1) {
		strcat(b, ((x & z) == z) ? "1" : "0");
	}

	return b+1;
}

// expected size of *s = 10 bytes, including the \0
// precision=2; maximum digits before the "." is 4 (-ddddd.xx)
void dddd_xx_double_to_string(char *s, double val) {
	if (val > (double)99999 || val < (double)-99999) {
		simple_puts("dddd_xx_double_to_string(): overflow\r\n");
		val = 0;
	}
	dtostrf(val, 0, 2, s);
}

#ifdef PRINTF_TO_USART
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

static int uart_putchar(char c, FILE *stream) {
	USART_tx(c);
	return 0;
}
#endif

#if CLK_DELAY_MS == 0
// we are so slow @ 1MHz...
#define delay_sdata() {}
#else
#define delay_sdata() _delay_ms(CLK_DELAY_MS);
#endif

//#define PIN_set_input(port, bit) port &= (~_BV(bit))
//#define PIN_set_output(port, bit) port |= (_BV(bit))
//#define PIN_set_high(port, bit) port |= (_BV(bit))
//#define PIN_set_low(port, bit) port &= (~_BV(bit))

void DTR_high() {
	DTR_PORT |= _BV(DTR_PBIT);
	delay_sdata();
}

void DTR_low() {
	DTR_PORT &= (~_BV(DTR_PBIT));
	delay_sdata();
}

void SCK_high() {
	SCK_PORT |= _BV(SCK_PBIT);
	delay_sdata();
}

void SCK_low() {
	SCK_PORT &= (~_BV(SCK_PBIT));
	delay_sdata();
}

void DATA_set_output() {
	DATA_DDR |= (_BV(DATA_DDB));
	delay_sdata();
}

void DATA_set_input() {
	DATA_DDR &= (~_BV(DATA_DDB));
	delay_sdata();
}

void DATA_high() {
	DATA_set_input(); // the pull-up resistor will make it high
	delay_sdata();
}

void DATA_low() {
	DATA_set_output();
	DATA_PORT &= (~_BV(DATA_PBIT));
	delay_sdata();
}

uint8_t DATA_read() { // 0 -> PIN is low; 1 -> PIN is high
	DATA_set_input();
	if (bit_is_set(DATA_PIN, DATA_PBIT)) {
		return 1;
	} else {
		return 0;
	}
}

void XBee_wakeup() {
	if (xbee_enabled) return;
	xbee_enabled = 1;
	
	power_usart0_enable();
	USART_init();
	
	DTR_low(); // enable XBee
	put_into_sleep(0, SLEEP_MODE_PWR_DOWN, WDTO_15MS); // don't disable peripherals, we are in the middle of the program
	//_delay_ms(15); // XBee wake-up; wake-up time from SM=1: 13.2 ms
}

void XBee_sleep() {
	if (!xbee_enabled) return;
	xbee_enabled = 0;
	
	power_usart0_disable();
	
	DTR_high(); // disable XBee
}

void IO_init() {
	power_all_disable();
	sei(); // enable interrupts
	
	SCK_DDR |= (_BV(SCK_DDB)); // SCK is output
	DTR_DDR |= (_BV(DTR_DDB)); // DTR is output
	SCK_high();
	xbee_enabled = 1; XBee_sleep(); // force XBee sleep
	DATA_set_input();
}

// http://atmel.com/dyn/resources/prod_documents/doc2551.pdf
// https://www.mainframe.cx/~ckuethe/avr-c-tutorial/lesson13.c
ISR(WDT_vect, ISR_NAKED){
	_WD_CONTROL_REG = _BV(WDIE); // must reset interrupt after trigger
	reti();
}

// also puts XBee to sleep
void put_into_sleep(uint8_t disable_peripherals, uint8_t sleep_mode, uint8_t sleep_time) {
	if (disable_peripherals) {
		XBee_sleep();
	
		// make some PINs input, so that they drain less power
		SCK_DDR &= (~_BV(SCK_DDB)); // SCK is input
		DATA_set_input();
	}
	
	set_sleep_mode(sleep_mode);
	wdt_enable(sleep_time); // http://www.eit.lth.se/fileadmin/eit/courses/edi021/AVR-libc-1.6.4/group__avr__watchdog.html
	_WD_CONTROL_REG = _BV(WDIE); // generate watchdog interrupts
	sleep_mode();
}

uint8_t s_write_byte(unsigned char value) {
	uint8_t i;
	uint8_t error = 0;
	
	//printf("s_write_byte(): value=%d (%s)\r\n", value, byte_to_binary(value));
	
	for (i = 0x80; i > 0; i >>= 1) {
		if (i & value) {
			DATA_high();
		} else {
			DATA_low();
		}
		SCK_high();
		SCK_low();
	}

	DATA_set_input();
	SCK_high(); // wait for ACK
	
	if (DATA_read()) {
		simple_puts("s_write_byte(): got error\r\n");
		error = 1;
	} else {
		//simple_puts("s_write_byte(): got ACK\r\n");
		error = 0;
	}
	SCK_low();
	
	return error;
}

uint8_t s_read_byte(uint8_t ack) {
	uint8_t i;
	uint8_t val = 0;
	
	//simple_puts("s_read_byte(): Start\r\n");
	DATA_high();
	for (i = 0x80; i > 0; i >>= 1) {
		SCK_high();
		if (DATA_read()) val = (val | i);
		SCK_low();
	}
	
	if (ack) {
		DATA_low();
	} else {
		DATA_high();
	}
	SCK_high();
	SCK_low();
	DATA_high();
	
	//printf("s_read_byte(): value=%d (%s)\r\n", val, byte_to_binary(val));
	
	return val;
}

void s_transstart(void) {
	//simple_puts("s_transstart()\r\n");
	
	SCK_low(); DATA_high();
	SCK_high();
	DATA_low();
	SCK_low();
	SCK_high();
	DATA_high();
	SCK_low();
}

uint8_t s_measure(uint16_t *p_value, uint8_t *p_checksum, uint8_t mode) {
	uint8_t error = 0, tmp;

	s_connectionreset();	
	s_transstart();
	switch (mode) {
		case TEMP:
			//simple_puts("s_measure(): Measure TEMP\r\n");
			error += s_write_byte(MEASURE_TEMP);
			crcdata[0] = MEASURE_TEMP;
			break;
		case HUMI:
			//simple_puts("s_measure(): Measure HUMI\r\n");
			error += s_write_byte(MEASURE_HUMI);
			crcdata[0] = MEASURE_HUMI;
			break;
		default:
			simple_puts("s_measure(): Bad argument\r\n");
			return 1; // error
			break;
	}
	
	//simple_puts("s_measure(): Waiting for sensor\r\n");
	DATA_set_input();
	
	put_into_sleep(0, SLEEP_MODE_PWR_DOWN, WDTO_500MS);
	
	// maximum measurement time is 320 ms for 14 bit resolution; we should have slept enough, no need to loop
	//for (*waited_time = 0; *waited_time < 3000; ++(*waited_time)) {
	//	if (DATA_read() == 0) break; //wait until sensor has finished the measurement 
	//	_delay_ms(1); // we should never end up waiting here; we slept for 500 ms before the loop
	//}
	
	if (DATA_read()) {
		simple_puts("s_measure(): Sensor timeout\r\n");
		return 1; // error
	}
	
	//simple_puts("s_measure(): Reading sensor\r\n");
	tmp = s_read_byte(ACK);	// read the first byte (MSB)
	crcdata[1] = tmp;
	*p_value = tmp;
	*p_value <<= 8; // MSB
	
	tmp = s_read_byte(ACK);	// read the second byte (LSB)
	crcdata[2] = tmp;
	*p_value += tmp;
	
	//printf("raw_value=%d\r\n", (int)p_value);
	
	tmp = s_read_byte(noACK);
	crcdata[3] = tmp;
	*p_checksum = tmp;
	
	return 0;
}

void s_connectionreset(void) {
	uint8_t i;
	
	//simple_puts("s_connectionreset()\r\n");
	DATA_high();
	SCK_low();
	for (i = 0; i < 9; ++i) {
		SCK_high();
		SCK_low();
	}
	//s_transstart(); // why do we need this here, when we do it in s_measure()?
}

int is_checksum_ok(uint8_t p_checksum) {
	char data[25];
	char *result;
	char result_rev[9];
	char orig_crc[9];
	int i;

	strcpy(data, byte_to_binary(crcdata[0]));
	strcat(data, byte_to_binary(crcdata[1]));
	strcat(data, byte_to_binary(crcdata[2]));
	result = MakeCRC(data);
	
	//printf("CRC input: %s\r\n", data);
	for (i = 0; i < 8; ++i) {
		result_rev[7-i] = result[i];
	}
	result_rev[8] = '\0';
	
	strcpy(orig_crc, byte_to_binary(p_checksum));
	
	if (strcmp(orig_crc, result_rev) == 0) return 1;
	else return 0;
}

double convert_temp(uint16_t p_value) {
	double d1 = -39.7;
	double d2 = 0.01;
	
	return d1 + d2*p_value;
}

double convert_humi(double *RHlin, uint16_t p_value, double t_cels) {
	double c1 = -2.0468;
	double c2 = 0.0367;
	double c3 = -1.5955e-6;
	
	double t1 = 0.01;
	double t2 = 0.00008;
	
	*RHlin = c1 + c2*p_value + c3*p_value*p_value;
	
	return (t_cels - 25)*(t1 + t2*p_value) + *RHlin;
}

uint8_t measure_helper(uint16_t *p_value, uint8_t mode) {
	uint8_t p_checksum;
	
	if (s_measure(p_value, &p_checksum, mode) != 0) {
		simple_puts("s_measure(): Communication error\r\n");
		return 1;
	}
	if (!is_checksum_ok(p_checksum)) {
		simple_puts("s_measure(): Checksum error\r\n");
		return 1;
	}
	return 0;
}

void do_measure() {
	uint8_t err;
	uint16_t p_value, temp_value;
#ifndef SAVE_BATTERY
	char humidity_linear[10];
#endif
	char humidity[10], temperature[10];
	double humidity_linear_value, temperature_value;
#ifndef SAVE_BATTERY
	char t_raw_value[10], h_raw_value[10];
#endif
	
	err = measure_helper(&p_value, TEMP);
	if (err) return;
#ifndef SAVE_BATTERY	
	dddd_xx_double_to_string(t_raw_value, p_value);
#endif
	temperature_value = convert_temp(p_value);
	dddd_xx_double_to_string(temperature, temperature_value);
	temp_value = p_value;
	
	err = measure_helper(&p_value, HUMI);
	if (err) return;
#ifndef SAVE_BATTERY	
	dddd_xx_double_to_string(h_raw_value, p_value);
#endif
	dddd_xx_double_to_string(humidity, convert_humi(&humidity_linear_value, p_value, temperature_value));
	
#ifndef SAVE_BATTERY
	dddd_xx_double_to_string(humidity_linear, humidity_linear_value);
#endif

#ifndef SAVE_BATTERY
	simple_puts("temperature=");
	simple_puts(temperature);
	simple_puts("*C :: humidity=");
	simple_puts(humidity);
	simple_puts("% (linear=");
	simple_puts(humidity_linear);
	simple_puts("%) :: raw=");
	simple_puts(t_raw_value);
	simple_puts(":");
	simple_puts(h_raw_value);
	simple_puts("\r\n");
	//printf("temperature=%s :: humidity=%s (linear=%s)\r\n", temperature, humidity, humidity_linear);
#else
	// also try to keep under 17 chars to fit in the TX buffer of XBee
	simple_puts(temperature); // 5
	simple_puts(";"); // 1
	simple_puts(humidity); // 5
	simple_puts("\r\n"); // 2
	// total: 13 chars
#endif

	put_into_sleep(0, SLEEP_MODE_IDLE, WDTO_15MS); // don't disable peripherals, just snooze; we need the UART running
	//_delay_ms(5); // flush UART, we will go to sleep right away
}

int main(void) {
	uint8_t sleep_cycles_left;

	_delay_ms(100); // SHTxx sensor booting
	
	IO_init();
	
	#ifdef VERBOSE
		simple_puts("Starting up\r\n");
	#endif

#ifdef PRINTF_TO_USART
	stdout = &mystdout;	
#endif
	
	// make sure that you make any output at the very end, so that we don't wake up XBee too early

	#ifdef VERBOSE
		simple_puts("Entering loop\r\n");
		_delay_ms(5); // flush the USART, we are doing IO_init() right away
	#endif
	while (1) {
		IO_init(); // reset every time, because after returning from a deep sleep, we reset some pins to inputs
		do_measure();
		#ifdef VERBOSE
			simple_puts("Going to sleep for 60 seconds\r\n");
		#endif
		
		sleep_cycles_left = 8 + 1;
		while (--sleep_cycles_left) {
			#ifdef VERBOSE
				simple_puts("Still needs sleep\r\n");
				_delay_ms(5); // flush the USART, we may go to sleep right away
			#endif
			put_into_sleep(1, SLEEP_MODE_PWR_DOWN, WDTO_8S);
		}
		wdt_disable();
	}
	
	return 0;
}
