#include <avr/power.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include "string.h"

#include "lcd_temp.h"
#include "bootloader.h"

#ifdef LCD
#include "lib/lcd.h"
#endif

#ifdef TIME_H
#include "time.h"
#endif
//#include "eeprom.c"



// *** Globals ***

char input_buffer[SIZE_INPUT_BUFFER] = "";
char *p_input = input_buffer;
uint8_t input_complete = 0;

#ifdef RTC
volatile int32_t seconds_count = TIMESTAMP;
int8_t show_rtc = SHOW_RTC;
int8_t timezone = TZ;
uint16_t timer_corr = 0;
uint16_t timer_corr_counter;
#endif

#ifdef PWM
uint16_t pwm_loop_count = PWM_LOOP_COUNT;		// Speed PWM loop cycle
int8_t pwm_cycle = 0;							// Cycle PWM n times; -1 for continuous
#endif

#ifdef KEYBOARD
uint8_t keyb_buffer[5] = "";
uint8_t keyb_keepalive = 0;
#endif

#ifdef COUNTER
volatile uint32_t counter_major;
volatile uint16_t counter_minor;
uint16_t counter_minor_overflow = COUNTER_MINOR_OVERFLOW;
uint8_t counter_reported;
uint16_t counter_minor_report = COUNTER_MINOR_REPORT;
volatile uint16_t counter_freq = 0;

ISR(COUNTER_INTERRUPT_VECTOR) {
		counter_minor++;
		counter_reported = 0;
		if (counter_minor == counter_minor_overflow) {
			counter_minor = 0;
			counter_major++;
		}
}
#endif


// *** Interupt for 1s counter ***
#ifdef RTC
ISR(TIMER1_OVF_vect)  { 
	TCNT1H = (unsigned char) (TIMER_1000ms >> 8);
	TCNT1L = (unsigned char) TIMER_1000ms;
	seconds_count++;
	#ifdef COUNTER
	{
		static uint16_t last_minor;
		static uint16_t last_major;
		counter_freq = (counter_major-last_major)*counter_minor_overflow + (counter_minor - last_minor);
		last_minor = counter_minor;
		last_major = counter_major;
	}
	#endif
}
#endif



// *** USB Class definitions ***

USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = {
  .Config = {
    .ControlInterfaceNumber   = 0,
    .DataINEndpoint           = {
	.Address          = CDC_TX_EPADDR,
	.Size             = CDC_TXRX_EPSIZE,
	.Banks            = 1,
    },
    .DataOUTEndpoint = {
      .Address          = CDC_RX_EPADDR,
      .Size             = CDC_TXRX_EPSIZE,
      .Banks            = 1,
    },
    .NotificationEndpoint = {
      .Address          = CDC_NOTIFICATION_EPADDR,
      .Size             = CDC_NOTIFICATION_EPSIZE,
      .Banks            = 1,
    },
  },
};


#ifdef KEYBOARD

/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
 	{
		.Config =
			{
				.InterfaceNumber              = 2,
				.ReportINEndpoint             =
					{
						.Address              = KEYBOARD_EPADDR,
						.Size                 = KEYBOARD_EPSIZE,
						.Banks                = 1,
					},
				.PrevReportINBuffer           = PrevKeyboardHIDReportBuffer,
				.PrevReportINBufferSize       = sizeof(PrevKeyboardHIDReportBuffer),
			},
    };

/** HID class driver callback function for the creation of HID reports to the host.
 *
 *  \param[in]     HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in,out] ReportID    Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
 *  \param[in]     ReportType  Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
 *  \param[out]    ReportData  Pointer to a buffer where the created report should be stored
 *  \param[out]    ReportSize  Number of bytes written in the report (or zero if no report is to be sent)
 *
 *  \return Boolean true to force the sending of the report, false to let the library determine if it needs to be sent
 */
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
	USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
	if (keyb_buffer[0]) {
	  	KeyboardReport->KeyCode[0] = keyb_buffer[0];
		keyb_buffer[0] = 0;
	}
	*ReportSize = sizeof(USB_KeyboardReport_Data_t);
	return false;
}

/** HID class driver callback function for the processing of HID reports from the host.
 *
 *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in] ReportID    Report ID of the received report from the host
 *  \param[in] ReportType  The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
 *  \param[in] ReportData  Pointer to a buffer where the received report has been stored
 *  \param[in] ReportSize  Size in bytes of the received HID report
 */
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                          const uint8_t ReportID,
                                          const uint8_t ReportType,
                                          const void* ReportData,
                                          const uint16_t ReportSize)
{
}

#endif // USB Keyboard



/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void) {
	CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
	#ifdef KEYBOARD
	HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface);
	USB_Device_EnableSOFEvents();
	#endif
} 

/** Event handler for the library USB Unhandled Control Request event. */
void EVENT_USB_Device_UnhandledControlRequest(void) {
	CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
}

/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void) {
	input_buffer[0] = 0;
	p_input = input_buffer;
}

/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void) {
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void) {
	CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
	#ifdef KEYBOARD
	HID_Device_ProcessControlRequest(&Keyboard_HID_Interface);
	#endif
}

#ifdef KEYBOARD
/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
    HID_Device_MillisecondElapsed(&Keyboard_HID_Interface);
}
#endif



// *** USB output wrappers ***

inline static void sendstr (char *s) {
	CDC_Device_SendString(&VirtualSerial_CDC_Interface, s);
}

inline static void sendchar (char c) {
	CDC_Device_SendByte(&VirtualSerial_CDC_Interface, c);
}

inline static void sendstr_P (const char *s) {
	char p;
	while ((p = pgm_read_byte_near(s++))) CDC_Device_SendByte(&VirtualSerial_CDC_Interface, p);
}


#ifdef LCD
void lcd_send32 (int32_t s) {
	int8_t first = 0;
	if (s < 0) { s = -s; lcd_putc('-'); }
	if (s) {
		for (uint32_t n = 1e9; n>0; n=n/10) {
			uint8_t x = s / n;
			if (x || first) {
				lcd_putc(x+48);
				first = 1;
			}
			s = s % n ;
		}
	} else lcd_putc(48);
} 
#endif

void send32 (int32_t s) {
	int8_t first = 0;
	if (s < 0) { s = -s; sendchar('-'); }
	if (s) {
		for (uint32_t n = 1e9; n>0; n=n/10) {
			uint8_t x = s / n;
			if (x || first) {
				sendchar(x+48);
				first = 1;
			}
			s = s % n ;
		}
	} else sendchar(48);
}

void sendhex32 (int32_t s) {
	int8_t first = 0;
	sendchar('0');
	sendchar('x');
	if (s) {
		for (uint32_t n = 0x10000000; n>0; n=n/16) {
			uint8_t x = s / n;
			if (x || first) {
				if (x<10) x += 48; else x += 87;
				sendchar(x);
				first = 1;
			}
			s = s % n ;
		}
	} else sendchar(48);
}

int32_t read32(char *p) {
	uint32_t n = 0;
	char neg = 0;
	while (*p == ' ') p++;
	if ( *p == '0' && *(p+1) == 'x') {
		p++; p++;
		do {
			if ( (*p >=65) && (*p <= 70))		// A -> F
				n =- 55;
			if ( (*p >=97) && (*p <= 102))		// a -> f
				n =- 87;
			if ( (*p >=48) && (*p <= 48+17))	// 0 -> 9
				n = n*16 + (*p) - 48;
		} while ( *(++p) && *p != ' ' );
	} else {
		if (*p =='-') {
			neg = 1;
			p++;
		}
		do {
			if ( (*p >=48) && (*p <= 57))
				n = n*10 + (*p) - 48;
		} while ( *(++p) && *p != ' ' );
	}
	if (neg) return -n; else return n;
}



// *** Config to internal EEPROM ***

void read_config(void) {
	struct s_config *config = malloc(sizeof(struct s_config));
	eeprom_read_block(config, 0, sizeof(struct s_config));
	if ( (config->signature[0] == 'L') && (config->signature[1] == 'C') && (config->signature[2] == 'D') ) {
		#ifdef RTC
		if (config->timestamp > seconds_count) seconds_count = config->timestamp;
		show_rtc = config->show_rtc;
		timezone = config->timezone;
		timer_corr = config->timer_corr;
		#endif

		#ifdef PWM
		OCR0A = config->lcd_bl;
		pwm_loop_count = config->pwm_loop_count;
		pwm_cycle = config->pwm_cycle;
		#else
		#ifdef LCD_BL_PORT
		if (config->lcd_bl) LCD_BL_PORT |= LCD_BL_MASK; else LCD_BL_PORT &= ~LCD_BL_MASK;
		#endif
		#endif
		#ifdef KEYBOARD
		keyb_keepalive = config->keyb_keepalive;
		#endif
		#ifdef BUTTONS
		for (uint8_t n=0; n<TOTAL_BUTTONS; n++) {
			buttons[n].scancode=config->buttons[n];
		}
		#endif
		#ifdef RELAYS
		for (uint8_t n=0; n<TOTAL_RELAYS; n++) {
			if (config->relays[n]) 
				*relays[n].port |= relays[n].mask;
			else
				*relays[n].port &= ~relays[n].mask;
		}
		#endif
		#ifdef COUNTER
		counter_major = config->counter_major;
		counter_minor = config->counter_minor;
		counter_minor_overflow = config->counter_minor_overflow;
		counter_minor_report = config->counter_minor_report;
		if (counter_minor > counter_minor_overflow) {
			counter_minor = counter_minor % counter_minor_overflow;
			counter_major += counter_minor / counter_minor_overflow;
		}
		#endif
	}
	free(config);
}

void save_config(void) {
	struct s_config *config = malloc(sizeof(struct s_config));
	config->signature[0] = 'L';
	config->signature[1] = 'C';
	config->signature[2] = 'D';
	#ifdef RTC
	config->timestamp = seconds_count;
	config->show_rtc = show_rtc;
	config->timezone = timezone;
	config->timer_corr = timer_corr;
	#else
	config->timestamp = TIMESTAMP;
	config->show_rtc = 0;
	config->timezone = 0;
	config->timer_corr = 0;
	#endif
	config->lcd_bl = 0;
	config->pwm_loop_count = 0;
	config->pwm_cycle = 0;
	#ifdef PWM
	config->lcd_bl = OCR0A;
	config->pwm_loop_count = pwm_loop_count;
	config->pwm_cycle = pwm_cycle;
	#else
	#ifdef LCD_BL_PORT
	if (LCD_BL_PORT & LCD_BL_MASK) config->lcd_bl = 255; else config->lcd_bl = 0;
	config->pwm_loop_count = PWM_LOOP_COUNT;
	config->pwm_cycle = 0;
	#endif
	#endif
	#ifdef KEYBOARD
	config->keyb_keepalive = keyb_keepalive;
	#endif
	#ifdef BUTTONS
	for (uint8_t n=0; n<TOTAL_BUTTONS; n++) {
		config->buttons[n]=buttons[n].scancode;
	}
	#endif
	#ifdef RELAYS
	for (uint8_t n=0; n<TOTAL_RELAYS; n++) {
		config->relays[n]= (PIN(*relays[n].port)) & relays[n].mask;
	}
	#endif
	#ifdef COUNTER
	config->counter_major =	counter_major;
	config->counter_minor =	counter_minor;
	config->counter_minor_overflow = counter_minor_overflow;
	config->counter_minor_report = counter_minor_report;
	#endif



	eeprom_write_block(config, 0, sizeof(struct s_config));
	free(config);
}



// *** Temperature code start ***

#ifdef TEMP		

#include "/lib/onewire.h"
#include "/lib/ds18x20.h"
#include "/lib/crc8.h"

#define MAXSENSORS 3
uint8_t gSensorIDs[MAXSENSORS][OW_ROMCODE_SIZE];
uint8_t nSensor;

void puthex_nibble(const unsigned char b) {
	unsigned char  c = b & 0x0f;
	if ( c > 9 ) c += 'A'-10; 
	else c += '0';
	sendchar(c);
}

inline void puthex_byte( const unsigned char b ) {
	puthex_nibble( b >> 4 );
	puthex_nibble( b );
}



void DS18X20_show_id( uint8_t *id, size_t n ) {
	size_t i;
	for( i = 0; i < n; i++ ) {
		if ( i == 0 ) { sendstr_P(PSTR("FC: ")); }
		else if ( i == n-1 ) { sendstr_P(PSTR("CRC: ")); }
		if ( i == 1 ) { sendstr_P(PSTR("SN: ")); }
		puthex_byte(id[i]);
//		sendchar(' ');
		if ( i == 0 ) {
			if ( id[0] == DS18S20_FAMILY_CODE ) { sendstr_P(PSTR("(18S)")); }
			else if ( id[0] == DS18B20_FAMILY_CODE ) { sendstr_P(PSTR("(18B)")); }
			else if ( id[0] == DS1822_FAMILY_CODE ) { sendstr_P(PSTR("(22)")); }
			else { sendstr_P(PSTR("( ? )")); }
		}
		sendchar(' ');
	}
	if (crc8(id, OW_ROMCODE_SIZE))
		sendstr_P(&CRC_FAIL[0]);
	else 
		sendstr_P(&CRC_OK[0]);
}

void show_sensors(uint8_t n) {
	uint8_t i;
	sendstr_P(&SENSORS_FOUND[0]);
	CDC_Device_SendByte(&VirtualSerial_CDC_Interface, n+48);
	sendstr_P(&CRLF[0]);
	for (i = 0; i < n; i++ ) {
		sendstr_P(PSTR("# in Bus: "));
		CDC_Device_SendByte(&VirtualSerial_CDC_Interface, i+1+48);
		sendchar(':');
		sendchar(' ');
		DS18X20_show_id(&gSensorIDs[i][0], OW_ROMCODE_SIZE );
		sendstr_P(&CRLF[0]);
	}
}

static uint8_t search_sensors(void) {
	uint8_t i;
	uint8_t id[OW_ROMCODE_SIZE];
	uint8_t diff, nSensors;
	ow_reset();
	nSensors = 0;
	diff = OW_SEARCH_FIRST;
	while ( diff != OW_LAST_DEVICE && nSensors < MAXSENSORS ) {
		DS18X20_find_sensor( &diff, &id[0] );
		if( diff == OW_PRESENCE_ERR ) {
//			sendstr_P(PSTR("No Sensor found");
//			sendstr_P(&CRLF[0]);
			break;
		}
		if( diff == OW_DATA_ERR ) {
//			sendstr_P(PSTR("Bus Error");
//			sendstr_P(&CRLF[0]);
			break;
		}
		for ( i=0; i < OW_ROMCODE_SIZE; i++ )
			gSensorIDs[nSensors][i] = id[i];
		nSensors++;
	}
	return nSensors;
}

#endif		//  *** Temperature Code end ***



// *** Harware init ***

void SetupHardware(void) {

	#ifdef RTC
	// Timer_setup
	TIMSK1 = (1<<TOIE1); // | (1<<TOIE0);
//	TCCR1B |= (1<<CS12) & ~(1<<CS11) | (1<<CS10); 	// Timer1 CLK / 1024
//	TCCR1B |= ~(1<<CS12) | (1<<CS11) | (1<<CS10); 	// Timer1 CLK / 64
	TCCR1B = (1<<CS12) & ~(1<<CS11) & ~(1<<CS10); 	// Timer1 CLK / 256
//	TCCR0B = (1<<CS02) & ~(1<<CS01) | (1<<CS00); 	// Timer0 CLK / 1024
	#endif

	#ifdef PWM
	//PWM 0A PB7
	TCCR0A |= (1<<WGM00)|(1<<WGM01)|(1<<COM0A1);
	TCCR0B |= (1<<CS00);
	DDRB|=LCD_BL_MASK; 
	OCR0A=0;
	#endif

	// Prescaler and watchdog
	MCUSR &= ~(1 << WDRF);
	wdt_disable();
	clock_prescale_set(clock_div_1);

	#ifdef LCD
	lcd_init(LCD_DISP_ON);
	lcd_puts_p(PSTR("LCD waiting...\n"));
	DDR(LCD_BL_PORT)  |= LCD_BL_MASK; 
	LCD_BL_PORT |= LCD_BL_MASK;
	#endif

	#ifdef TEMP
	nSensor = 0;
	nSensor = search_sensors(); 
	#ifdef LCD
	lcd_puts_p(&SENSORS_FOUND[0]);
	lcd_putc(nSensor+48);
	lcd_putc('\n');
	#endif
	#endif

	#ifdef COUNTER
	COUNTER_REGISTER_INIT;	// Defined in lcd_temp.h
	EIMSK |= (1<<COUNTER_INTERRUPT);
	DDR(COUNTER_PORT) &= ~(COUNTER_MASK);
	#endif

	USB_Init();
	sei();
	
	#ifdef LCD
	lcd_puts_p(PSTR("USB ready...\n"));
	#endif

	// Pull target /RESET and HWB line high
	DDR(AVR_RESET_LINE_PORT) 	|= AVR_RESET_LINE_MASK; 
	AVR_RESET_LINE_PORT 		|= AVR_RESET_LINE_MASK;
	DDR(HWB_PORT) 	|= HWB_MASK; 
	HWB_PORT 		|= HWB_MASK;

	//turn off stat LED connected to PB0
	// DDRB |= (1<<0);
	// sbi(PORTB, 0);

	// Init input buttons 
	#ifdef BUTTONS
	for (uint8_t n=0; n<TOTAL_BUTTONS;n++) DDR(*buttons[n].port) &= ~(buttons[n].mask);
	#endif

	// Init Relays
	#ifdef RELAYS
	for (uint8_t n=0; n<TOTAL_RELAYS;n++) DDR(*relays[n].port) |= relays[n].mask;
	#endif
}



// *** Check input buttons ***

#ifdef BUTTONS	
void check_buttons(void)		{
	for (int8_t n=0; n<TOTAL_BUTTONS;n++) {
		if ( (!(PIN(*buttons[n].port) & (buttons[n].mask))) != buttons[n].status) {
			buttons[n].count++; 
			if (buttons[n].count == READCOUNT) {
				buttons[n].status = !(PIN(*buttons[n].port) & (buttons[n].mask));
					if (buttons[n].status) {				// low/zero
						sendstr_P(PSTR("BUTTON "));
						sendchar(n+48);
						sendstr_P(&CRLF[0]);
						#ifdef KEYBOARD
						if (buttons[n].scancode) keyb_buffer[0] = buttons[n].scancode;
						#endif
						//lcd.home();
						//lcd_putc('b');
					}
			}
		} else buttons[n].count = 0;
	}
}
#endif



// *** Main program ***

int main(void) {
	#ifdef PWM
	uint16_t loop = PWM_LOOP_COUNT;
	#endif
	check_bootloader();
	#ifdef CHECK_HWB
	check_hwb();
	#endif
	SetupHardware();
	read_config();
	#ifdef RTC
	timer_corr_counter = timer_corr;
	int8_t old_second = 0; 	
	#ifdef LCD
	if (show_rtc) lcd_clrscr();
	#endif
	#endif

	for (;;) {

		// *** Process USB input ***
		int16_t ReceivedByte = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
		if (!(ReceivedByte < 0)) {
			*p_input++ = (char)ReceivedByte;
			if (*(p_input-1) == '\n') {
				if (*(p_input-2) == '\r') {
					*(p_input-2) = 0;
				}
				*(p_input-1) = 0;
				input_complete = 1;
				p_input = input_buffer;
			}
			if (p_input == &input_buffer[SIZE_INPUT_BUFFER-1]) {
				input_complete = 1;
				input_buffer[SIZE_INPUT_BUFFER-1] = 0;
			}

		}

		#ifdef BUTTONS
		check_buttons();
		#endif
		if (input_complete) process_usb_cmd();


		#ifdef KEYBOARD
		HID_Device_USBTask(&Keyboard_HID_Interface);
		#endif
		CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
		USB_USBTask();


		// *** Cycle LCD backlight LED intensity ***
		#ifdef PWM
		if (pwm_cycle) {
			if (!loop--) { 
				static uint8_t dir = 1;
				static uint8_t duty = 0;
				duty += dir;
				OCR0A=duty; 
				if (duty == 255) { dir = -1; if (pwm_cycle > 0) { pwm_cycle--;} }
				if (duty == 0)   { dir =  1; if (pwm_cycle > 0) { pwm_cycle--;} }
				loop = pwm_loop_count;
			} 
		}
		#endif


		// *** Counter report ***
		#ifdef COUNTER
		if ( !counter_reported && (counter_minor % counter_minor_report == 0)) {
			sendstr_P(PSTR("Counter "));
			send32(counter_minor);
			sendchar(' ');
			send32(counter_major);
			sendchar(' ');
			send32(counter_freq);
			sendchar(' ');
			send32(seconds_count);
			sendstr_P(CRLF);
			counter_reported = 1;
		} 
		#endif


		// *** Process timer update ***
		#ifdef RTC
		if ( (old_second != (int8_t)seconds_count) ) {
			if (timer_corr) {
				if (! --timer_corr_counter) { 
					timer_corr_counter = timer_corr;
					seconds_count--;
				}
			}
			old_second = (int8_t)seconds_count;
			#ifdef KEYBOARD	// Send shift key ask keep alive every 256s
			if (!old_second && keyb_keepalive) keyb_buffer[0] = KEYB_KEEPALIVE_KEY;
			#endif
			if ( !(seconds_count % TIMER_SAVE_CONFIG) ) { save_config(); }	// Save config (timestamp)
			#ifdef LCD
			if (show_rtc == 1) lcd_display_time();
			if (show_rtc >1) --show_rtc;
			#endif
		}
		#endif // RTC

	} // for loop
}



// *** Show time and date on LCD ***

#ifdef LCD
#ifdef RTC
void lcd_display_time(void) {
	lcd_gotoxy(0, LCD_LINES-1);
	#ifndef TIME_H
//	lcd_puts_p(PSTR("Epoch:    "));
	lcd_gotoxy(LCD_DISP_LENGTH-10, LCD_LINES-1);
	lcd_send32(seconds_count);
	#else
	struct tm *t;
	t = (struct tm *) malloc(sizeof(struct tm));
	timestamp2tm(seconds_count + (timezone*60*60), t);
	char mystr[12];


// 				Time progress bar
/*				lcd.home();
				lcd_send32(round( ( (t->tm_hour)*60 + (t->tm_min)) * LCD_DISP_LENGTH/(24*60)));
				lcd_putc(' ');

				if ( !t->tm_hour && !t->tm_min && !t->tm_sec) {
					lcd_gotoxy(0, LCD_LINES-2);
					for (int8_t i = 0; i < LCD_DISP_LENGTH-1; i++) {
						lcd_putc('_');
					}
					lcd_putc('X');
				}

				// bar
				#if FLASHEND >= (0x7FFF)
				if (!old_second) {
					lcd_gotoxy(0, LCD_LINES-2);
					for (int8_t i = 1; i <= round( ((t->tm_hour)*60 + (t->tm_min)) * LCD_DISP_LENGTH/(24*60)); i++) {
						lcd_putc('.');
					}
//					lcd_putc(' ');
				}
				#endif
*/

	lcd_gotoxy(0, LCD_LINES-1);
	#if LCD_DISP_LENGTH >= 20
		strcpy_P(mystr, dayname[t->tm_wday]);
		lcd_puts(mystr);
		lcd_putc(' ');
		strcpy_P(mystr, monthname[t->tm_mon-1]);
		lcd_puts(mystr);
		sprintf_P(mystr, PSTR(" %02d     "), t->tm_mday); 
		lcd_puts(mystr);
	#endif
	#if LCD_DISP_LENGTH == 16
		sprintf_P(mystr, PSTR("%02d/%02d       "), t->tm_mday, t->tm_mon); 
		lcd_puts(mystr);
	#endif
	lcd_gotoxy(LCD_DISP_LENGTH-8, LCD_LINES-1);
	sprintf_P(mystr, PSTR("%02d:%02d:%02d"), t->tm_hour, t->tm_min, t->tm_sec); 
	lcd_puts(mystr);
	free(t);
	#endif // TIME_H
	lcd.home();
}
#endif // RTC
#endif // LCD



// *** Process input from USB ***

void process_usb_cmd(void) {
	if (!strncmp_P(input_buffer, AT, strlen_P(AT) )) {
		switch (input_buffer[2]) {
			case 'r':	// Reboot
				sendstr_P(&REBOOTING[0]);
				sendstr_P(&CRLF[0]);
				CDC_Device_Flush(&VirtualSerial_CDC_Interface);
				CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
				USB_USBTask();
				#ifdef LCD
					lcd_clrscr();
					lcd_puts_p(REBOOTING);
				#endif
				save_config();
				USB_Disable();
				hardware_reset();
				break;

			case 'f':	// Enter boot loader
				sendstr_P(ENTERING);
				sendstr_P(ENTER_BOOTLOADER);
				sendstr_P(CRLF);

				CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
				CDC_Device_Flush(&VirtualSerial_CDC_Interface);
				USB_USBTask();
				#ifdef LCD
					lcd_clrscr();
					lcd_puts_p(ENTER_BOOTLOADER);
				#endif
//				delay(24000);
				save_config();
				USB_Disable();
//				for (uint16_t x = 0; x < 50000; x++); 
				boot_loader();
				break;

			case 'v': // Version
				sendstr_P(COMPILEDATE);
				sendstr_P(CRLF);
				break;

		}


		#ifdef TEMP // Temperature commands
		if (!strncmp_P(input_buffer, ATT, strlen_P(ATT) )) {
			switch (input_buffer[3]) {
				uint8_t n;
				int16_t decicelsius;

				case 's':
					nSensor = search_sensors(); 
					sendstr_P(&SENSORS_FOUND[0]);
					sendchar( nSensor + 48);
					sendstr_P(&CRLF[0]);
					break;

				case 'l':
					show_sensors(nSensor);
					break;

				case 't':
					n = input_buffer[4] - 48;
					sendstr_P(PSTR("Sensor "));
					CDC_Device_SendByte(&VirtualSerial_CDC_Interface, n+48);
					sendchar(':');
					sendchar(' ');
					if ( DS18X20_start_meas( DS18X20_POWER_EXTERN, &gSensorIDs[n-1][0] ) == DS18X20_OK ) {
						_delay_ms( DS18B20_TCONV_12BIT );
						if ( DS18X20_read_decicelsius( &gSensorIDs[n-1][0], &decicelsius) == DS18X20_OK ) {
							send32(decicelsius);
							sendstr_P(PSTR(" dC "));
						} else {
							sendstr_P(&CRC_FAIL[0]);
						}
					} else {
						sendstr_P(PSTR("Error, no contact"));
					}
					sendstr_P(CRLF);
					break;
			}
		}
		#endif

				

		#ifdef LCD // LCD Commands
		if (!strncmp_P(input_buffer, ATLCD, strlen_P(ATLCD) )) {
				#ifdef RTC
				if (show_rtc) show_rtc = 5; // Suspend RTC updates for 5s
				#endif

			if (!strncmp_P(input_buffer, LCDINIT, strlen_P(LCDINIT) )) {
//				uint8_t size = strlen_P(LCDINIT); 
//				uint8_t cmd = (input_buffer[size+1]-48) * 10 + input_buffer[size+2]-48; 
				uint8_t cmd = read32(&input_buffer[strlen_P(LCDINIT)]);
//				CDC_Device_SendString(&VirtualSerial_CDC_Interface, &LCDINIT[0]);
				if ( cmd != 0x08 && cmd != 0x0D && cmd != 0x0E && cmd != 0x0F )
					cmd = 0x0C;		// default on, no blink or curser
				lcd_init(cmd);
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDCLRSCR, strlen_P(LCDCLRSCR) )) {
				lcd_clrscr();
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDHOME, strlen_P(LCDHOME) )) {
				lcd.home();
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDGOTOXY, strlen_P(LCDGOTOXY) )) {
				uint8_t size = strlen(LCDGOTOXY);
				if (input_buffer[size] == ' ') size++;
				uint8_t x = (input_buffer[size]-48)*10 + input_buffer[size+1]-48;
				uint8_t y = (input_buffer[size+2]-48)*10 + input_buffer[size+3]-48;
			    lcd_gotoxy(x, y);
			}

			if (!strncmp_P(input_buffer, LCDPUTC, strlen_P(LCDPUTC) )) {
		 	       	lcd_putc(input_buffer[strlen(LCDPUTC)+1]);
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDCMD, strlen_P(LCDCMD) )) {
			       	lcd_command(read32(&input_buffer[strlen_P(LCDCMD)]));
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDBLON, strlen_P(LCDBLON) )) {
				#ifdef PWM
				OCR0A = 0;
				#else
				LCD_BL_PORT &= ~LCD_BL_MASK;
				#endif
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDBLOFF, strlen_P(LCDBLOFF) )) {
				#ifdef PWM
				OCR0A = 255;
				#else
				LCD_BL_PORT |= LCD_BL_MASK;
				#endif
				sendstr_P(OK);
			}
		#endif // LCD 

			#ifdef PWM
			if (!strncmp_P(input_buffer, LCDPWMSET, strlen_P(LCDPWMSET) )) {
				OCR0A = 255 - read32(&input_buffer[strlen_P(LCDPWMSET)]);
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDPWMLOOP, strlen_P(LCDPWMLOOP) )) {
				pwm_loop_count = read32(&input_buffer[strlen_P(LCDPWMLOOP)]);				
				sendstr_P(OK);
			}

			if (!strncmp_P(input_buffer, LCDPWMCYCLE, strlen_P(LCDPWMCYCLE) )) {
				pwm_cycle = read32(&input_buffer[strlen_P(LCDPWMCYCLE)]);
				sendstr_P(OK);
			}
			#endif // PWM

#ifdef LCD
		} 
#endif // LCD 



		// *** EEPROM Commands ***
/*		if (!strncmp_P(input_buffer, &EEREAD[0], strlen_P(&EEREAD[0]) )) {
			char mystr[10];
			write_SPIeeprom (0, &mystr[10], 6);
			sendstr(mystr);
			sendstr("\r\n");
		} */

		// Mark EEPROM as clear
		if (!strncmp_P(input_buffer, EECLEAR, strlen_P(EECLEAR) )) {
			eeprom_write_dword(0, 0);
			sendstr_P(OK);
		} 



		#ifdef RTC
		// Set RTC
		if (!strncmp_P(input_buffer, RTCSET, strlen_P(RTCSET) )) {
			seconds_count = read32(&input_buffer[strlen_P(RTCSET)]);
			if (seconds_count < 0) seconds_count = 0;
			timer_corr_counter = timer_corr;
			save_config();
			sendstr_P(OK);
		} 

		// Get RTC
		if (!strncmp_P(input_buffer, RTCGET, strlen_P(RTCGET) )) {
			send32(seconds_count);
			sendstr_P(CRLF);
		} 

		// Set RTC offset
		if (!strncmp_P(input_buffer, RTCOSET, strlen_P(RTCOSET) )) {
			timer_corr = read32(&input_buffer[strlen_P(RTCOSET)]);
			timer_corr_counter = timer_corr;
			save_config();
			sendstr_P(OK);
		} 

		// Get RTC offset
		if (!strncmp_P(input_buffer, RTCOGET, strlen_P(RTCOGET) )) {
			send32(timer_corr);
			sendstr_P(CRLF);
		} 

		// Turn on LCD RTC
		if (!strncmp_P(input_buffer, RTCON, strlen_P(RTCON) )) {
			show_rtc = 1;
			sendstr_P(OK);
		} 

		// Turn off LCD RTC
		if (!strncmp_P(input_buffer, RTCOFF, strlen_P(RTCOFF) )) {
			show_rtc = 0;
			sendstr_P(OK);
		}

		#ifdef TIME_H
		// Set Timezone
		if (!strncmp_P(input_buffer, RTCTZSET, strlen_P(RTCTZSET) )) {
			timezone = read32(&input_buffer[strlen_P(RTCTZSET)]);
			if (timezone > 12) timezone = 0;
			if (timezone < -12) timezone = 0;
			sendstr_P(OK);
			save_config();
		} 

		// Get Timezone
		if (!strncmp_P(input_buffer, RTCTZGET, strlen_P(RTCTZGET) )) {
			send32(timezone);
			sendstr_P(CRLF);
		} 

		// Write formatted RTC to USB
//		#  if FLASHEND > (0x3FFF)
		if (!strncmp_P(input_buffer, RTCNOW, strlen_P(RTCNOW) )) {
			char mystr[12];
			struct tm *t;
			t = (struct tm *) malloc(sizeof(struct tm));
			timestamp2tm(seconds_count+timezone*60*60, t);
			#if LCD_DISP_LENGTH >= 20
			sendstr_P(dayname[t->tm_wday]);
			#else
			sendchar(t->tm_wday+48);
			#endif
			sendchar(' ');
			send32(t->tm_yday);
			for (;t->tm_year > 100; t->tm_year -= 100);
			sprintf_P(mystr, PSTR(" %02d-%02d-%02d "), t->tm_year, t->tm_mon, t->tm_mday);
			sendstr(mystr); 
			sprintf_P(mystr, PSTR("%02d:%02d:%02d\r\n"), t->tm_hour, t->tm_min, t->tm_sec); 
			sendstr(mystr); 
			free(t);
		}
//		#  endif // FLASHEND
		#endif // TIME_H
		#endif // RTC


		#ifdef KEYBOARD
		if (!strncmp_P(input_buffer, ATKEYB, strlen_P(ATKEYB) )) {

			// Turn on keyboard keep alive, sending of shift key every 256 sec
			if (!strncmp_P(input_buffer+strlen_P(ATKEYB), KEYBALIVEON, strlen_P(KEYBALIVEON) )) {
				keyb_keepalive = 1;
				sendstr_P(OK);
				save_config();
			} 
	
			// Turn off keyboard keep alive, sending of shift key every 256 sec
			if (!strncmp_P(input_buffer+strlen_P(ATKEYB), KEYBALIVEOFF, strlen_P(KEYBALIVEOFF) )) {
				keyb_keepalive = 0;
				sendstr_P(OK);
				save_config();
			} 

			#ifdef BUTTONS
			// Get Keyboard scan code for button
			if (!strncmp_P(input_buffer+strlen_P(ATKEYB), KEYBGETKEY, strlen_P(KEYBGETKEY) )) {
				uint8_t n = read32(input_buffer + strlen_P(ATKEYB) + strlen_P(KEYBGETKEY));
				if (n < TOTAL_BUTTONS) {
					sendhex32(buttons[n].scancode);
				} else {
					sendstr_P(BTN_NOT_DEFINED);
				}
				sendstr_P(CRLF);
			} 

			// Set Keyboard scan code for button
			if (!strncmp_P(input_buffer+strlen_P(ATKEYB), KEYBSETKEY, strlen_P(KEYBSETKEY) )) {
				uint8_t n = read32(input_buffer + +strlen_P(ATKEYB) + strlen_P(KEYBSETKEY));
				if (n < TOTAL_BUTTONS) {
					buttons[n].scancode = read32(&input_buffer[strlen_P(KEYBSETKEY)+2]);
					sendstr_P(OK);
					save_config();
				} else {
					sendstr_P(BTN_NOT_DEFINED);
					sendstr_P(CRLF);
				}
			} 

			// Get Keyboard status for button n
			if (!strncmp_P(input_buffer+strlen_P(ATKEYB), KEYBSTATUS, strlen_P(KEYBSTATUS) )) {
				uint8_t n = read32(input_buffer + strlen_P(ATKEYB) + strlen_P(KEYBSTATUS));
				if (n < TOTAL_BUTTONS) {
					if (!(PIN(*buttons[n].port) & (buttons[n].mask)))
						sendstr_P(PSTR("ON"));
					else
						sendstr_P(PSTR("OFF"));
				} else {
					sendstr_P(BTN_NOT_DEFINED);
				}
				sendstr_P(CRLF);
			} 
			#endif // BUTTONS
		}	
		#endif //KEYBOARD


		#ifdef RELAYS
		// Turn on relay
		if (!strncmp_P(input_buffer, RELAYON, strlen_P(RELAYON) )) {
			uint8_t n = read32(input_buffer + strlen_P(RELAYON));
			if (n < TOTAL_RELAYS) {
				*relays[n].port &= ~relays[n].mask;	// zero/low
				sendstr_P(OK);
				save_config();
			} else {
				sendstr_P(RELAY_NOT_DEFINED);
				sendstr_P(CRLF);
			}
		} 

		// Turn off relay
		if (!strncmp_P(input_buffer, RELAYOFF, strlen_P(RELAYOFF) )) {
			uint8_t n = read32(input_buffer + strlen_P(RELAYOFF));
			if (n < TOTAL_RELAYS) {
				*relays[n].port |= relays[n].mask; // one/high
				sendstr_P(OK);
				save_config();
			} else {
				sendstr_P(RELAY_NOT_DEFINED);
				sendstr_P(CRLF);
			}
		} 
		#endif // Relay


		#ifdef COUNTER
		// Get Major counter, minor counter, freq, minor report, minor overflow and timestamp
		if (!strncmp_P(input_buffer, COUNTERGET, strlen_P(COUNTERGET) )) {
			send32(counter_major);
			sendchar(' ');
			send32(counter_minor);
			sendchar(' ');
			send32(counter_freq);
			sendchar(' ');
			send32(counter_minor_report);
			sendchar(' ');
			send32(counter_minor_overflow);
			sendchar(' ');
			send32(seconds_count);
			sendstr_P(CRLF);
		} 

		// Set Major counter
		if (!strncmp_P(input_buffer, COUNTERMAJORSET, strlen_P(COUNTERMAJORSET) )) {
			counter_major = read32(input_buffer + strlen_P(COUNTERMAJORSET)); 
			sendstr_P(OK);
			sendstr_P(CRLF);
			save_config();
		}

		// Set minor counter
		if (!strncmp_P(input_buffer, COUNTERMINORSET, strlen_P(COUNTERMINORSET) )) {
			counter_minor = read32(input_buffer + strlen_P(COUNTERMINORSET)); 
			sendstr_P(OK);
			save_config();
		} 

		// Set minor overflow
		if (!strncmp_P(input_buffer, COUNTEROVERFLOW, strlen_P(COUNTEROVERFLOW) )) {
			counter_minor_overflow = read32(input_buffer + strlen_P(COUNTEROVERFLOW)); 
			sendstr_P(OK);
			save_config();
		} 

		// Set minor report
		if (!strncmp_P(input_buffer, COUNTERREPORT, strlen_P(COUNTERREPORT) )) {
			counter_minor_report = read32(input_buffer + strlen_P(COUNTERREPORT)); 
			if (counter_minor > counter_minor_overflow) {
				counter_minor = counter_minor % counter_minor_overflow;
				counter_major += counter_minor / counter_minor_overflow;
			}
			sendstr_P(OK);
			save_config();
		} 

		#endif


	} else { // Print to LCD and echo back to host
		#ifdef LCD
		lcd_puts(input_buffer);
		//lcd_putc('\n');
		#endif
		sendstr_P(PSTR("Re: "));
		sendstr(input_buffer);
		sendstr_P(CRLF);
	}
	CDC_Device_Flush(&VirtualSerial_CDC_Interface);
	input_buffer[0] = 0;
	input_complete = 0;
	p_input = input_buffer;		
}