// PrayBot.c
// Copyright (c) 2006 by Timothy J. Weber, http://timothyweber.org.

#include <system.h>

#pragma CLOCK_FREQ 4000000

#define	_INTRC_OSC_NOCLK	0x3FFC

#ifdef DEBUG
#pragma DATA _CONFIG, _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _BODEN_ON & _INTRC_OSC_NOCLK
#else
#pragma DATA _CONFIG, _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BODEN_ON & _INTRC_OSC_NOCLK
#endif

// The 8-bit A/D value that triggers the motor to turn on.
// See the spreadsheet in PrayBot.ods for the derivation of this value.
#define VCC_THRESHOLD  97

// Pins on GPIO.

// High: Motor is on.
#define MOTOR_PIN  2
#define MOTOR_MASK  (1 << MOTOR_PIN)

// Low: current is drawn through the Zener.
#define VTEST_PIN  0
#define VTEST_MASK  (1 << VTEST_PIN)

// A/D for Zener voltage.
#define VSENSE_PIN  1
#define VSENSE_MASK  (1 << VSENSE_PIN)

unsigned char ReadVoltage()
{
	// Turn on the sense pin.
	trisio.VTEST_PIN = 0;
	gpio.VTEST_PIN = 0;  // sink current from the Zener.
	
	// Turn on the A/D.
	adcon0.ADON = 1;
		
	// Wait for it to settle.
	clear_wdt();
	delay_10us(8);  // min Tacq = 20, but we extend that for extra stability given that the Zener's
		/// just been turned on.
		
	// Start the conversion.
	adcon0.GO_DONE = 1;
	
	// Wait till it's complete.
	while (adcon0.NOT_DONE)
		;
		
	// Turn off the A/D and the sense pin.
	adcon0.ADON = 0;
	trisio.VTEST_PIN = 1;
	
	// Return the high byte of the A/D results.
	return adresh;
}

void main()
{

// Initialization	
	
	// Initialize the watchdog timer with 1:16 prescaling (= ~0.3 seconds).
	option_reg = 0b11111100;
	
	// Disable unused peripherals.
	cmcon = 7;
	
// Wait for Vcc to get to about 5.3 V.

	// Set up the A/D.
	// Don't turn it on yet, though.
	#if VSENSE_PIN == 1
		adcon0 = 0b00000100;
		//             01     A/D Channel
		ansel = 0b01010010;
		//         101        4 us sample time at 4 MHz clock
		//              1     GPIO pin 1 enabled for A/D
	#else
		#error Need to set A/D channel and input pin to correspond to VSENSE_PIN
	#endif
	
	// Read and wait.
	while (1) {
		// Take the sample.
		clear_wdt();
		unsigned char sample = ReadVoltage();
		
		// Check it against the threshold.
		if (sample >= VCC_THRESHOLD)
			// Above the threshold: stop waiting.
			break;
		else {
			// Wait a little before trying again.
			// This conserves power, because we're not shunting current through the Zener while we wait.
			#ifdef DEBUG
				delay_ms(200);
				delay_ms(100);
			#else
				// In fact, we'll power down completely, and rely on the watchdog timer to start us up again.
				sleep();
			#endif
		}
	}

// Now we have enough to start the motor.
	
	// Turn on the motor.
	trisio.MOTOR_PIN = 0;
	gpio.MOTOR_PIN = 1;
	
	// Sleep forever.
	option_reg = 0b11111111; // make the watchdog as quiet as possible
	while (1) {
		sleep();
	}
}

