/* 
	Wind Speed Display and Data Logger
    Copyright (C) 2007 APRS World, LLC

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	Author: 
		APRS World, LLC
		<info@aprsworld.com>
		PO Box 1264
		Winona, MN 55987

	Project website:
		http://www.aprsworld.com/windmonitor2/
*/

#include "windmon2.h"

/* global variables */
int8 features;
int8 clicks;
/* the wind speed is determined by counting the number of pulses in a window of time. cal determines
how long the window is. If the speed (number of pulses) is greater than 0 then the speed_offset is
added

cal is a magic number
speed_offset is in the units displayed. Example: speed_offset=2 will make a speed of 8 display 10
 */
int8 cal; 		
int8 speed_offset;

int8 window;
int8 speed;	// speed to show on screen
int8 gust;
short current;	// which digit we are on
short update;	// update flag

int8 tensdigit=0;
int8 onesdigit=0;

#define BEFORESLEEP 255

#include <24256.c>
#include "printd.c"
#include "hw_i.c"


/* This is the interrupt handler for the anemometer */
#int_ext
void anemometer_tick(void) {
	disable_interrupts(INT_RTCC);
	clicks++;
	enable_interrupts(INT_RTCC);
}


/* every time the real-time clock overflows we go here */
#int_rtcc
void tick(void) {
	static int8 sleepcount=BEFORESLEEP;

	/* turn off this interrupt so we don't get interrupted,
	probably not nescessary unless this ISR takes longer than
	it should */
	disable_interrupts(INT_TIMER0);

	window--;
	if ( 0 == window ) {
		disable_interrupts(INT_EXT);
		window = cal;
		speed = clicks;
		/* it takes some wind to make the anemometer start spinning */
		if ( speed > 0 ) {
			speed=speed+speed_offset;
		}
		if ( speed > gust ) {
			gust = speed;
		}
		clicks = 0;
		update = true;
		enable_interrupts(INT_EXT);
	}
	
	/* if sleep has been zero for the last BEFORESLEEP interations,
	then we put the processor into sleep mode to save power. The next
	external interrupt (anemometer click) will wake it up */
	if ( bit_test(features,F_SLEEP) ) {
		if ( 0 == speed )
			sleepcount--;		
		else
			sleepcount=BEFORESLEEP;
		
		if ( 0 == sleepcount ) {
			sleepcount=BEFORESLEEP;	
			shutdown();
		}
	}
	
	if ( bit_test(features,F_DISP) ) {

		if ( update ) {
			update=false;
			/* default case we just show speed */
			update_digits(speed);
		}
			

		/* alternate between left and right digit */		
		if ( 0 == current ) {
			current=1;
			output_high(TEN_LED);
			displayDigit(onesdigit);
			output_low(ONE_LED);
		} else {
			current=0;
			output_high(ONE_LED);
			/* don't zero pad */
			if ( 0 == tensdigit )
				displayDigit(BLANK);
				else
				displayDigit(tensdigit);
			output_low(TEN_LED);
		}
	}

	enable_interrupts(INT_TIMER0);	// turn back on this interrupt
}

void init_628() {
	/* PIC16F628 stuff to make port_a digital instead of analog */
	setup_comparator(NC_NC_NC_NC);
	setup_vref(FALSE);
	
	set_tris_a(0b00010000);
	set_tris_b(0b00000011);
}

void init_interrupts() {
	set_rtcc(0);
	setup_counters( RTCC_INTERNAL, RTCC_DIV_32);
	enable_interrupts(INT_RTCC);
	enable_interrupts(INT_EXT); 
	enable_interrupts(H_TO_L);	// interrupt on high to low transition

	enable_interrupts(GLOBAL);
}


void startup_msg() {
	printf("# windmon2 ");
	puts(__DATE__);

	if ( FEATURES_DOWNLOAD == features )
		displayDigit(0);
	else if ( FEATURES_DISPLAY == features )
		displayDigit(1);
	else if ( FEATURES_LOG == features )
		displayDigit(DIGIT_L);

	output_low(TEN_LED);
	delay_ms(600);
	output_high(TEN_LED);
	output_low(ONE_LED);
	delay_ms(600);
	output_high(ONE_LED);

}

void disp_off() {
	output_high(ONE_LED);
	output_high(TEN_LED);
	bit_clear(features,F_DISP);
}

void disp_on() {
	bit_set(features,F_DISP);
}


void startLog() {
	int16 l;

	int16 delay;
	int8 lspeed;	// local speed, otherwise it might change
	
	bit_clear(features,F_SLEEP);	// no sleeping while logging

	delay = read_int_ulong(E_DELAY);
	
	printf("# log ");
	print_lint(delay,1);
//	puts(" sec");

	/* turn off display if requested */	
	if ( ! bit_test(features,F_LOG_DISP) )
		disp_off();
		
	init_ext_eeprom();
	
	for ( l=0 ; l<32768 ; l++ ) {
		delay_half_seconds(delay);	

		lspeed = speed;	

		disable_interrupts(GLOBAL);
		/* make the next location 0xFF so we know where we left off */
		if ( l < 32767 ) {
			write_ext_eeprom(l+1,0xff);
		}

		/* then write out current value, in case we power off
		while writting */
		write_ext_eeprom(l,lspeed);
		enable_interrupts(GLOBAL);

		putchar('.');
	}
	
	disp_on();
}

void playLog() {
	int16 l;
	int8 value;

	/* turn off display */
	disp_off();

	for ( l=0 ; l<32767 ; l++ ) {
		value=read_ext_eeprom(l);

		/* terminate when we get 0xff end of data marker */
		if ( 0xff == value ) {
			break;
		}

		print_int(value,false);
		crnl();
	}

	disp_on();
}


short set_mode(char c) {

	/* if we don't get the mode, then we get it ourselves */
	if ( 0 == c ) {	
		c = getchar();
		delay_ms(10);
		putchar(c);
		c = tolower(c);
	}

	crnl();


	if ( 'd' == c ) {
		printf("# dlog");
		features = FEATURES_LOG;
	} else if ( 'o' == c ) {
		printf("# dload");
		features = FEATURES_DOWNLOAD;
	} else if ( 'i' == c ) {
		printf("# Disp");
		features = FEATURES_DISPLAY;
	} else {
		puts("# Invalid");
		return 1;
	}
	crnl();

	/* save our new mode */
	write_eeprom(E_FEATURES,features);

	return 0;
}

void getMode(void) {
	disable_interrupts(GLOBAL);
		
	do {
		puts("(d)log, d(o)wnload, d(i)sp");
	} while ( set_mode(0) );

	delay_ms(500);	
#if 1
	reset_cpu();
#else
	/* don't reset, just hang */
	while (1) delay_cycles(1);
#endif
}


void write_int_eeprom_hex(void) {
	write_eeprom(gethex(),gethex());
}

void read_int_eeprom_hex(void) {
	print_hex(read_eeprom(gethex()));
}

void printSpeed(void) {
	int8 s;
	
	disable_interrupts(GLOBAL);
	s=speed;
	enable_interrupts(GLOBAL);

	print_int(s,false);
	crnl();
}

void main(void) {
	int8 c;

	/* initialize globals */
	speed=0;
	gust=0;
	clicks=0;	

	init_628();
	
	features=read_eeprom(E_FEATURES);
	window=cal=read_eeprom(E_CAL);
	speed_offset=read_eeprom(E_SPEED_OFFSET);

	startup_msg();
	
	init_interrupts();	

	/* immediately start logging, but next time come up in download mode */
	if ( bit_test(features,F_LOG) ) {
		write_eeprom(E_FEATURES, 0b00001001);
		startLog();
	}

#IGNORE_WARNINGS 203
	while ( true ) {
#IGNORE_WARNINGS NONE
		printf("cmd: ");
		c = getchar();
		putchar(c);
		crnl();
		switch (tolower(c)) {
			/* Mode of operation */
			case 'm':	getMode();
					break;
			case 's':	printSpeed();
					break;

			/* Logging */					
			case 'g':	startLog();
					break;
			case 'p':	playLog();
					break;

			/* Set anemometer calibration */
			case '-':	cal--;
					printCal(); 
					break;
 			case '+':	cal++;
					printCal(); 
					break;
			case '[':	speed_offset--;
					printSpeedOffset();
					break;
			case ']':	speed_offset++;
					printSpeedOffset();
					break;

			/* computer setup */
			case 'r':	read_int_eeprom_hex();
					break;
			case 'w':	write_int_eeprom_hex();
					break;

			/* unknown command */
			default:	putchar('?'); crnl();
		}

	}
}


