#include <c:\program files\picc\devices\16F877.h>
#fuses XT,WDT,NOPROTECT,PUT	// osc XT, watchdog on, no code protect, power up timer on
				// bown out OFF, LV prog Disabled, Flash Write Disabled, Data EE off, code protect off
#use delay (clock=2457600)	// 2.4576MHz clock
#use RS232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)	// 9600 baud
#use FAST_IO(A)			// I/O dir doesn't change when
#use FAST_IO(B)			// reading and writing to ports
#use FAST_IO(C)
#use FAST_IO(D)

// this file: xprtloop.c
// monitors laser loop pressure in outbuilding and sends data to xport
// A/D is used to read pressure, setpoints, and upper and lower thresholds
// signals xport to send email if pressure too low, too high, and on each fill

// A/D channel inputs
// RA0 (pin 2)	High Pressure Alarm Set Point
// RA1 (pin 3)	Low Pressure Alarm Set Point
// RA2 (pin 4)	Pressure Set Point
// RA3 (pin 5)	Pressure
// RA4 (pin 6)	5V (pressure & setpoints are ratiometric)

// Alias for A/D channel definitions
const	int8	hp_stpt_chan = 0;	// A/D chan 0 is high pressure alarm trip point
const	int8	lp_stpt_chan = 1;	// A/D chan 1 is low pressure alarm trip point
const	int8	p_stpt_chan = 2;	// A/D chan 2 is pressure set point
const	int8	pres_chan = 3;		// A/D chan 3 is pressure
const	int8	pwr_chan = 4;		// A/D chan 4 is 5V power supply
const	int8	YES = 89;		// ascii character for "Y"
const	int8	NO = 78;		// ascii character for "N"
const	int8	vlv_max = 10;		// ignore high press alarm if within 5 loop-times of valve opening
const	int8	OPEN = 0;		// valve is open
const	int8	CLOSED = 1;		// valve is closed

// Digital I/O
// RB7 (pin 40)	Low Pressure Alarm (IN)
// RB6 (pin 39)	High Pressure Alarm (IN)

// serial string to send to xport to trigger email
// chr$(182) chr$(182)	Pressure above high pressure alram trip point
// chr$(184) chr$(184)	Pressure below low pressure alram trip point
// chr$(191) chr$(191)	To many autofill cycles in too short a time

main(){
	// All values are 8 bit ints (throw away 2 LSB's from A/D)
	unsigned int8	hp_stpt;	// digitized high pressure alarm trip point
	unsigned int8	lp_stpt;	// digitized low pressure alarm trip point
	unsigned int8	p_stpt;		// digitized pressure set point
	unsigned int8	pres;		// digitized pressure
	unsigned int8	pwr;		// digitized 5V power supply

	unsigned int8	i;		// loop counter
	unsigned int8	old_vlv=CLOSED;	// last valve position was closed
	unsigned int8	vlv_cnt = 0;	// time how long valve is open

	char	halms;			// High alarm Y or N (ascii 89 or 78)
	char	lalms;			// Low alarm Y or N (ascii 89 or 78)
	char	next;			// Will fill on next timer trigger (ascii 89 or 78)
	char	fill;			// Is filling now (ascii 89 or 78)

	float	f_hpal;			// floating point high pressure alarm trip point (PSI)
	float	f_lpal;			// floating point low pressure alarm trip point (PSI)
	float	f_pres;			// floating point pressure (PSI)
	float	f_p_stpt;		// floating point pressure set point

	restart_wdt();			// reset watch dog timer
	setup_wdt(WDT_576MS);		// watch dog timer overflow in 576ms
	setup_psp(PSP_DISABLED);	// disable parallel slave port
	setup_adc(ADC_CLOCK_DIV_32);	// longer than needed for 2.4576MHz clock
	setup_adc_ports(ALL_ANALOG);	// all Port A are A/D inputs

	set_tris_a(0xFF);		// All input for A/D
	set_tris_b(0xFF);		// Port B all inputs
	set_tris_c(0xEF);		// out (RC7=RX, RC4=CP2 XPORT), in (RC6=TX, rest unused)
//	set_tris_c(0xBF);		// RC7 RX, RC6 TX, rest unused inputs
//	set_tris_d(0xFF);		// Port D inputs (unused)
	set_tris_d(0xF3);		// RD2 & RD3 outputs (CP1 & CP3 on the XPORT)
	set_tris_e(0xFF);		// Port E inputs (for A/D)

	output_a(0x00);			// Port A low
	output_b(0x00);			// Port B low
	output_c(0x00);			// Port C low
	output_d(0x00);			// Port D low
	output_e(0x00);			// Port E low
	port_b_pullups(FALSE);		// disable Port B internal pullups
	output_bit(pin_D2,1);		// Email High Pres Alarm (active low so set high, no email yet)
	output_bit(pin_D3,1);		// Email Low Pres Alarm (active low so set high, no email yet)
	output_bit(pin_C4,1);		// Email Fill Occurred (active low so set high, no email yet)

//	output_b(0x40);		// pwm's = 0, CW dir, const A = 1
//	output_bit(pin_B7,0);	// pwm A low
//	if (i == A_duty)
//	delay_cycles(25);	// adjust to set pwm frequency
//	output_d(speed);	// for debugging A/D

	// give XPORT time to boot up (about 10 seconds)
	for(i = 0; I <= 39; ++i){	// 40 * 1/4 sec = 10 seconds
	  delay_ms(250);		// delay 250ms
	  restart_wdt();		// reset watch dog timer
	}
	// XPORT has had time to boot up so send a message
	printf("Pressure sensor low pass filter settling (wait about 2 minutes)   ");

	// delay for about two minutes while pressure has time to stabilize after power-up
	// filter time constant is about 20 seconds
	for(i = 0; I <= 240; ++i){	// 240 * 1/4 sec = 60 seconds
	  delay_ms(250);		// delay 250ms
	  restart_wdt();		// reset watch dog timer
	}
	for(i = 0; I <= 240; ++i){	// 240 * 1/4 sec = 60 seconds
	  delay_ms(250);		// delay 250ms
	  restart_wdt();		// reset watch dog timer
	}

loop:
	// read the five inputs to the A/D
	set_adc_channel(hp_stpt_chan);	// Read High Pressure Alarm Set Point
	delay_ms(1);			// Give input A/D MUX time to settle
	hp_stpt = read_adc();		// read A/D
	set_adc_channel(lp_stpt_chan);	// Read Low Pressure Alarm Set Point
	delay_ms(1);			// Give input A/D MUX time to settle
	lp_stpt = read_adc();		// read A/D
	set_adc_channel(p_stpt_chan);	// Read Pressure Set Point
	delay_ms(1);			// Give input A/D MUX time to settle
	p_stpt = read_adc();		// read A/D
	set_adc_channel(pres_chan);	// Read Pressure
	delay_ms(1);			// Give input A/D MUX time to settle
	pres = read_adc();		// read A/D
	set_adc_channel(pwr_chan);	// Read 5V power supply
	delay_ms(1);			// Give input A/D MUX time to settle
	pwr = read_adc();		// read A/D

	// used for debugging
//	printf("High_Pres_ALM_stpt: %3u\n",hp_stpt);
//	printf("pressure_setpt____: %3u\n",p_stpt);
//	printf("pressure__________: %3u\n",pres);
//	printf("Low_Pres_ALM_stpt_: %3u\n",lp_stpt);
//	printf("5V_Power_Supply___: %3u\n",pwr);

	// Presure sensor output 0.5-4.5V (0-30 PSI)	// 7.5 PSI/Volt or 133mV/PSI 
	// pressure sensor output is ratiometric so divide digitized pressure by digitized power
	// (float) Pressure (PSI) = 15.0 + (pres/pwr - 0.5) * 37.5	// calibrate at 15 PSI

	if (pwr == 0)	// make sure pwr <> 0 for div by zero below
	  pwr = 1;	// note: if div by 1 will still trigger high pres alarm

	// convert integer A/D values into presure in PSI
	f_hpal = ((float)hp_stpt/(float)pwr) * 37.5 - 3.75;	// high pressure alarm trip point
	f_lpal = ((float)lp_stpt/(float)pwr) * 37.5 - 3.75;	// low pressure alarm trip point
	f_pres = ((float)pres/(float)pwr) * 37.5 - 3.75;	// pressure
	f_p_stpt = ((float)p_stpt/(float)pwr) * 37.5 - 3.75;	// pressure set point
/*
	// used for debugging
	printf("%3u ",hp_stPt);
	printf("%3u ",p_stPt);
	printf("%3u ",pres);
	printf("%3u ",lp_stPt);
	printf("%3u ",pwr);
	printf("%2.1f ",f_hpal);
	printf("%2.1f ",f_p_stpt);
	printf("%2.1f ",f_pres);
	printf("%2.1f ",f_lpal);
*/

	if (vlv_cnt != 0)	// if the valve has been opened recently count down the time from opening
	  --vlv_cnt;		// when time hits zero if pres is > than high alm trip pt alarm will trigger
	// if the valve status has just changed from closed to open
	if ((old_vlv = CLOSED) & (!input(pin_b0))){
	  old_vlv = OPEN;		// mark the old valve position as OPEN
	  vlv_cnt = vlv_max;		// set the time to alow the presure to be over the high pres alm point 
	} else if(input(pin_b0))	// if the valve is closed
	  old_vlv = CLOSED;		// mark the old valve position as CLOSED

	// vlv_cnt is used because when the valve is opened the pressure rises above the alarm trip point
	// the 0.5PSI is for hysteresis so if the pressure is at the trip point it doesn't send lots of email
	// if (pres > high alarm trip point or high alarm pin low) and valve hasn't been opened recently
	if (((f_pres > (f_hpal+0.5)) | (!input(pin_b6))) & (vlv_cnt == 0)){
	  halms = YES;		// trigger high pressure alarm (ascii character via browser)
	  output_bit(pin_D2,0);	// trigger high pressure alarm (send email)
	// clear the email alarm if pressure is 0.5PSI below alarm trip point or high alarm pin is high
	}else if ((f_pres < (f_hpal-0.5)) | (input(pin_b6))){
	  halms = NO;		// clear ascii high pressure alarm
	  output_bit(pin_D2,1);	// clear high pressure alarm email pin
	}

	// Delay added because xport would sometimes ignore email trigger if printf statement
	// sent characters imediately after the trigger (at about the same time - don't know required delay)
	for(i = 0; i <= 3; ++i){	// 1 second delay
	  delay_ms(250);		// 4 * 250ms
	  restart_wdt();		// reset watch dog timer
	}

	// if pressure below low alarm trip point (+ hysteresis) or low alarm pin is pulled low
	if ((f_pres < (f_lpal-0.5)) | (!input(pin_b7))){
	  lalms = YES;		// trigger low pressure alarm (ascii character via browser)
	  output_bit(pin_D3,0);	// trigger low pressure alarm (send email)
	}else if ((f_pres > (f_lpal+0.5)) | (input(pin_b7))){
	  lalms = NO;		// clear ascii low presure alarm
	  output_bit(pin_D3,1);	// clear low pressure alarm email pin
	}

	// Delay added because xport would sometimes ignore email trigger if printf statement
	// sent characters imediately after the trigger (at about the same time - don't know required delay)
	for(i = 0; i <= 3; ++i){	// 1 second delay
	  delay_ms(250);		// 4 * 250ms
	  restart_wdt();		// reset watch dog timer
	}

	if(!input(pin_b4))	// YES if valve will open next delta T
	  next = YES;
	else
	  next = NO;

	if(!input(pin_b0)){	// YES if valve is currently open
	  fill = YES;
	  output_bit(pin_c4,0);	// trigger fill email
	}else {
	  fill = NO;
	  output_bit(pin_c4,1);	// clear fill email pin
	}

	// Delay added because xport would sometimes ignore email trigger if printf statement
	// sent characters imediately after the trigger (at about the same time - don't know required delay)
	for(i = 0; i <= 3; ++i){	// 1 second delay
	  delay_ms(250);		// 4 * 250ms
	  restart_wdt();		// reset watch dog timer
	}

	printf("%2.1f____%2.1f____%2.1f____%c_____%2.1f___%c______%c_____%c___     ", f_pres, f_p_stpt, f_hpal, halms, f_lpal, lalms, next, fill);
//	printf("old_vlv = %d vlv_cnt = %d _______________________________     ", old_vlv, vlv_cnt);

	// used for debugging
//	printf("High Pres ALM setpt: %2.1f\n PSI ",f_hpal);
//	printf("  Presure Set Point: %2.1f\n PSI ",f_p_stpt);
//	printf("            Presure: %2.1f\n PSI ",f_pres);
//	printf("LOW  Pres ALM setpt: %2.1f\n PSI ",f_lpal);

	restart_wdt();			// reset watch dog timer

	// delay about two seconds
//	for(i = 0; i <= 3; ++i){	// 4 * 1/4 sec = 1 second
	for(i = 0; i <= 7; ++i){	// 2 seconds
	  delay_ms(250);		// delay 250ms
	  restart_wdt();		// reset watch dog timer
	}

	goto loop;			// loop forever
}

