Example #1
0
void CRemoteInterface::update()
{
    if (motorControl.update)
    {
        updateMotorControl();
        motorControl.update = false;
    }

    const uint32_t curtime = millis();
    if (curtime > statusSendDelay)
    {
        statusSendDelay = curtime + 500;
        sendStatus();
    }

    if (REM_SERIAL.available())
        checkForCommands();
}
Example #2
0
/**
 * \brief The main application
 *
 * This application logs visible and infrared irradiance levels to an SD card
 * 
 *
 * \note 
 * 
 */
int main(void)
{
	uint8_t ct, swDnUp, swBbIr;
	uint8_t errSD, cnt, r;
	uint16_t cntout = 0;
	strJSON[0] = '\0'; // "erase" the string
	DDRD &= ~(1<<5); // make the Bluetooth connection monitor pin an input
	PORTD &= ~(1<<5); // disable internal pull-up resistor
	DDRD |= (1<<4); // make Bluetooth power control an output
	DDRD |= (1<<7); // make Bluetooth baud rate control an output
	DDRB |= (1<<1); // make GPS power control an output
	DDRB |= (1<<0); // make GPS on/off an output
	
	GPS_power_off; // default to power off
	GPS_On_Off_Low; // brief (~200ms) high pulse turns GPS on/off

	BT_power_off();
//	BT_baud_9600();
	BT_baud_115k();

//	cli();
//	setupDiagnostics(); // need heartbeat timer to correctly turn SD power off (may work around this)
//	// also may help with power control tracking while testing dead battery re-charge
//	sei();
//	turnSDCardPowerOff();

	// force SD card power off, and pins in lowest power modes

	if (!(PRR0 & (1<<PRSPI))) // is SPI power on (Pwr Save bit clear)?
	{
		if (SPCR & (1<<SPE)) // is SPI enabled?
		{
			SPCR &= ~(1<<SPE); // disable SPI
		}
		PRR0 |= (1<<PRSPI); // turn off power to SPI module, stop its clock
	}		
	DESELECT();

    DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_SCK)); // change SPI output lines MOSI and SCK into inputs
	// pins might source current momentarily
	 // set port bits to 0, disable any internal pull-ups; tri-state the pins
	SPI_PORT &= ~((1<<SPI_MOSI_BIT)|(1<<SPI_SCK_BIT)|(1<<SPI_MISO_BIT)); // MISO was already an input
	
	SD_CS_DD &= ~(1<<SD_CS_BIT); // change SS to an input, momentarily sources current through internal pull-up
	SD_CS_PORT &= ~(1<<SD_CS_BIT); // set port bit to zero, tri-state the input

	SD_PWR_DD |= (1<<SD_PWR_BIT);          // Turns on PWR pin as output 
	SD_PWR_PORT |= (1<<SD_PWR_BIT);   // Drive PWR pin high; this will turn FET off
	SD_PWR_DD &= ~(1<<SD_PWR_BIT);          // change PWR pin to an input
	// internal pull-up momentarily pulls high, along with external pull-up
	SD_PWR_PORT &= ~(1<<SD_PWR_BIT);   // tri-state the pin; external pull-up keeps FET off
	
//	Stat |= STA_NOINIT;      // Set STA_NOINIT

	// try allowing the following on first power-up, even if cell is barely charged
	
	cli();
	setupDiagnostics();
	uart0_init();
	initFlags |= (1<<initUART0);
	uart1_init();
	initFlags |= (1<<initUART1);
	sei();
	
	// main osc is not tuned yet, can't do that until I2C is working
	commandBuffer[0] = '\0'; // "empty" the command buffer
	commandBufferPtr = commandBuffer;
	stateFlags1 |= (1<<writeDataHeaders); // write column headers at least once on startup

	intTmp1 = readCellVoltage(&cellVoltageReading);
	
	I2C_Init(); // enable I2C
	initFlags |= (1<<initI2C);
				
	if (initFlags & (1<<initUART0)) 
		outputStringToUART0("\r\n  UART0 Initialized\r\n");
					
	if (initFlags & (1<<initUART1))
		outputStringToUART1("\r\n  UART1 Initialized\r\n");
	
	if (initFlags & (1<<initI2C)) 
		outputStringToUART0("\r\n  I2C_Init completed\r\n");
		
	// test the I2C bus, if it doesn't work nothing much else will
	intTmp1 = I2C_Start();
	if (intTmp1 == 0) { // I2C start timed out
		// we are hung, go into SOS mode
		PRR0 |= (1<<PRTWI); // disable I2C, turn module power off (set Power Reduction bit)
		DDRC &= 0b00111111; // make SCL and SDA pins inputs so we can read them
		PORTC &= 0b00111111; // disable internal pull-up resistors
		
		// uart0 is about all we've got to talk on
		UBRR0 = 207; // 0.2% error BAUD_4800_2X_OSC_8MHZ, assume main osc untuned 8MHz
		Timer2 = 0;
		while (1) {
			// mostly, fast-blink the pilot light
			for (Timer1 = 10; Timer1; );	// Wait for 100ms
			PORTA |= (0b00000100); // set pilot light on
			for (Timer1 = 10; Timer1; );	// Wait for 100ms
			PORTA &= ~(0b00000100); // turn off bit 2, pilot light blinkey
			if (!Timer2) { // about every 3 seconds, send diagnostics
				cli(); // temporarily disable interrupts to prevent Timer3 from
					// changing the count partway through
				stateFlags1 |= (1<<isRoused); // enable uart output
				rouseCountdown = 100; // hold the Rouse flag on long enough for output
				sei();
				len = sprintf(str, "\n\r I2C bus start failure: SCL = %i, SDA = %i \n\r", (PINC & 1), ((PINC & 2) ? 1 : 0));
				outputStringToUART0(str);
				Timer2 = 1000; // ~3 sec to next message
			}
		}
	}
	else { // I2C bus started OK
		intTmp1 = I2C_Write(0); // send the generic device address so devices release the bus
		I2C_Stop(); // release I2C bus and continue
	}

	intTmp1 = rtc_readTime(&dt_RTC);
	strcat(strJSON, "\r\n{\"timechange\":{\"from\":\"");
	datetime_getstring(datetime_string, &dt_RTC);
	strcat(strJSON, datetime_string);
	strcat(strJSON, "\",\"to\":\"");
	
	if (dt_RTC.year) { // 0 on power up, otherwise must already have been set
		rtcStatus = rtcTimeRetained;
		strcat(strJSON, datetime_string);
		strcat(strJSON, "\",\"by\":\"retained\"}}\r\n");
	} else { // RTC year = 0 on power up, clock needs to be set
		rtc_setdefault();
		if (!rtc_setTime(&dt_RTC)) {
			rtcStatus = rtcTimeSetToDefault;
			datetime_getstring(datetime_string, &dt_RTC);
			strcat(strJSON, datetime_string);
			strcat(strJSON, "\",\"by\":\"default\"}}\r\n");
		} else {
			rtcStatus = rtcTimeSetFailed;
			strcat(strJSON, datetime_string);
			strcat(strJSON, "\",\"by\":\"failure\"}}\r\n");
		}
	}
	stateFlags1 |= (1<<writeJSONMsg); // log JSON message on next SD card write	
	timeFlags &= ~(1<<nextAlarmSet); // alarm not set yet
	irradFlags |= (1<<isDark); // set the Dark flag, default till full-power initializations passed
	
	// tune uC osc down to 7.3728 MHz, implement 115200 baud
	// need I2C to do this
	tuneMainOsc();
				
	r = initializeADXL345();
	if (r) {
		len = sprintf(str, "\n\r ADXL345 initialize failed: %d\n\r\n\r", r);
		outputStringToUART0(str);
	} else {
		initFlags |= (1<<initAccelerometer);
		outputStringToUART0("\r\n ADXL345 initialized\r\n");
	}
				
	switch (rtcStatus) {
					
		case rtcTimeRetained:
			outputStringToBothUARTs("\n\r time retained through uC reset\n\r");
			break;
						
		case rtcTimeSetToDefault:
			outputStringToBothUARTs("\n\r time set to default, now elapsed to ");
			datetime_getstring(datetime_string, &dt_RTC);
			outputStringToBothUARTs(datetime_string);
			outputStringToBothUARTs("\n\r\n\r");
			break;	
					
		case rtcTimeSetFailed:
			outputStringToBothUARTs("\n\r could not set Real Time Clock \n\r");
			break;
					
	}
				
	// attempt to read/write the time zone; will retry later if e.g. power too low
	syncTimeZone();

	
	stateFlags1 |= (1<<isRoused); // force on for testing, enable UART output
//	for (Timer1 = 3; Timer1; );	// Wait for 30ms
	outputStringToUART0("\r\n  test\r\n");
//	for (Timer1 = 3; Timer1; );	// Wait for 30ms
	outputStringToUART0("\r\n  test\r\n");
//	for (Timer1 = 3; Timer1; );	// Wait for 30ms
	outputStringToUART0("\r\n  test\r\n");
//	for (Timer1 = 3; Timer1; );	// Wait for 30ms

	outputStringToBothUARTs("\n\r Power good \n\r\n\r");
	
/*
	// try to adjust the uC clock frequency
	// first step, measure the uC clock, relative to the RTC, which latter should be very accurate
	// eventually put this in a more reasonable place, but for now right here before main loop
	
	// go into uC clock adjust mode
	outputStringToUART0("\r\n going into uC adjust mode\r\n");
	timeFlags &= ~(1<<nextAlarmSet); // clear flag
	disableRTCInterrupt();
	intTmp1 = rtc_enableSqWave();
	// PRTIM1 make sure power reduction register bit if off so timers run
	
	
	// go back into normal timekeeping mode
	outputStringToUART0("\r\n returning to timekeeping mode\r\n");
	if (!(timeFlags & (1<<nextAlarmSet))) {
		intTmp1 = rtc_setupNextAlarm(&dt_CurAlarm);
		timeFlags |= (1<<nextAlarmSet);
	}
*/

	while (1) { // main program loop
		// code that will only run once when/if cell voltage first goes above threshold,
		// sufficient to run initializations and modules that take more power
		if (!(stateFlags1 & (1<<reachedFullPower))) { 
			if (cellVoltageReading.adcWholeWord > CELL_VOLTAGE_GOOD_FOR_STARTUP) {
				stateFlags1 |= (1<<reachedFullPower); // flag, so this loop does not happen again till next reset
				if (cellVoltageReading.adcWholeWord > CELL_VOLTAGE_GOOD_FOR_ALL_FUNCTIONS) {
					// probably, somebody has just popped a fresh battery in, and wants to set up this device
					stayRoused(18000); // keep system roused for 3 minutes (180 sec) for diagnostic output
				} else {
					// probably, battery has slowly charged from dead, and nobody is here watching
					// long diagnostics would just waste power for no good reason
					stayRoused(300); // only briefly, 3 seconds, then go into low power mode
				}
				if (cellVoltageReading.adcWholeWord > CELL_VOLTAGE_GOOD_FOR_ALL_FUNCTIONS) {
					// it's likely someone is setting up this device with a fresh battery
					tuneMainOsc(); // re-tune, in case clock was slow on first low-power startup
					keepBluetoothPowered(180); // start with Bluetooth power on for 3 minutes
				}
			} // end test CELL_VOLTAGE_GOOD_FOR_STARTUP
		} // end test reachedFullPower flag
		
		// end of segment that runs only once, when Full Power first achieved
		
		// beginning of loop that runs repeatedly
		// tests of normal operation
		checkCriticalPower();
		if (stateFlags1 & (1<<reachedFullPower)) { // only run this after cell has charged to full power and modules initialized
			if (motionFlags & (1<<tapDetected)) {
				outputStringToUART0("\n\r Tap detected \n\r\n\r");
				if (stateFlags1 & (1<<isRoused)) { // if tap detected while already roused
					stayRoused(12000); // 2 minutes (120 seconds)
					tuneMainOsc(); // re-tune, in case clock was slow on first low-power startup
					keepBluetoothPowered(120); // try for two minutes to get a Bluetooth connection
				}
				motionFlags &= ~(1<<tapDetected);
			}
			if (stateFlags1 & (1<<isRoused)) { // if roused
				irradFlags &= ~(1<<isDark); // clear the Dark flag
				timeFlags &= ~(1<<nextAlarmSet); // flag that the next alarm might not be correctly set
	//			if (irradFlags & (1<<isDark))
	//				timeFlags &= ~(1<<nextAlarmSet); // flag that the next alarm might not be correctly set
			}
		
			if (BT_connected()) {
				// keep resetting this, so BT power will stay on for 2 minutes after connection lost
				// to allow easy reconnection
				keepBluetoothPowered(120);
			} else { // not connected
				if (btFlags & (1<<btWasConnected)) { // connection lost
					; // action(s) when connection lost
				}
				btFlags &= ~(1<<btWasConnected); // clear the flag
			}		
		} // end of this full power segment
		
		machineState = Idle; // beginning, or done with everything; return to Idle state

		while (machineState == Idle) { // RTC interrupt will break out of this
			checkCriticalPower();
		
//			if (stateFlags1 & (1<<reachedFullPower)) { // another full-power-only segment
			intTmp1 =  clearAnyADXL345TapInterrupt();
			if (intTmp1) {
				len = sprintf(str, "\r\n could not clear ADXL345 Tap Interrupt: %d\r\n", intTmp1);
				outputStringToUART0(str);
			}
			enableAccelInterrupt();
			checkForBTCommands();
			checkForCommands();
//			} // end of this full-power segment
			
			if (!(timeFlags & (1<<nextAlarmSet))) {
	//			outputStringToUART0("\n\r about to call setupNextAlarm \n\r\n\r");
				intTmp1 = rtc_setupNextAlarm(&dt_CurAlarm);
				timeFlags |= (1<<nextAlarmSet);
			}

			if (!(stateFlags1 & (1<<isRoused))) { // may add other conditions later
				// go to sleep
				PORTA &= ~(0b00000100); // turn off bit 2, pilot light blinkey
				// SE bit in SMCR must be written to logic one and a SLEEP
				//  instruction must be executed.

				// When the SM2..0 bits are written to 010, the SLEEP instruction makes the MCU enter
				// Power-down mode
	
				// SM2 = bit 3
				// SM1 = bit 2
				// SM0 = bit 1
				// SE = bit 0
				// don't set SE yet
				SMCR = 0b00000100;
				// set SE (sleep enable)
				SMCR |= (1<<SE);
				// go intoPower-down mode SLEEP
				asm("sleep");
			}

		} // end of (machState == Idle)
			// when (machState != Idle) execution passes on from this point
			// when RTCC alarm or Accelerometer tap occurs, changes machineState to WakedFromSleep
			
		checkCriticalPower();
		
		// Tap interrupt will not be active until first time initialization, so
		//  following flag should not be settable till then anyway
		//  but put internal check in case code rearranged
		if (motionFlags & (1<<tapDetected)) { // if it was a tap, go into Roused state
			if (stateFlags1 & (1<<reachedFullPower)) { // only if had achieved full power and initialized
				stayRoused(3000); // 30 seconds
			} else {
				stayRoused(300); // 3 seconds
			}
			motionFlags &= ~(1<<tapDetected); // clear the flag
		}
			
		timeFlags &= ~(1<<nextAlarmSet); // flag that current alarm is no longer valid

		while (timeFlags & (1<<alarmDetected)) { // interrupt that woke from sleep was RTC alarm
			// use 'while' loop to allow various tests to break out
			timeFlags &= ~(1<<alarmDetected); // clear flag so this will only happen once in any case
			// monitor cell voltage, to decide whether there is enough power to proceed
			// remember previous voltage; very first read on intialize, so should be meaningful
			previousADCCellVoltageReading = cellVoltageReading.adcWholeWord;
			intTmp1 = readCellVoltage(&cellVoltageReading);
			if (!(stateFlags1 & (1<<reachedFullPower))) { // if not achieved full power and initialized, skip this data acquisition loop
				// for testing, set to 10-second interval, so don't have to wait an hour to see if battery charging worked
				irradFlags &= ~(1<<isDark); // remove this line when done testing dead battery re-charging
				break; // will test reachedFullPower at top of main program loop
			}
			
			if (cellVoltageReading.adcWholeWord < CELL_VOLTAGE_THRESHOLD_UART) {
				// power too low for any output, no need to even read sensors
				// remove comment-out of following line when done testing dead battery re-charge
//				irradFlags |= (1<<isDark); // act as if dark, to save power
				break;
			}
			
			// see if it's time to log data
			if ((!((dt_CurAlarm.minute) & 0x01) && (dt_CurAlarm.second == 0)) || (irradFlags & (1<<isDark))) {
            // if an even number of minutes, and zero seconds
            // or the once-per-hour wakeup while dark
				timeFlags |= (1<<timeToLogData);
				if (irradFlags & (1<<isDark)) { // store the voltage reading at this point
					refDarkVoltage = cellVoltageReading.adcWholeWord; 
				}
				
			//	outputStringToUART0("\n\r Time to log data \n\r");
			} else {
				timeFlags &= ~(1<<timeToLogData);
			//	outputStringToUART0("\n\r Not time to log data \n\r");
			}
			
			// if not time to log data, and not roused
			if ((!(timeFlags & (1<<timeToLogData))) && (!((stateFlags1 & (1<<isRoused))))) {
				// won't do anything with results anyway, don't bother reading sensors, save power
				stayRoused(5); // rouse for 0.05 second to flash the pilot light
				break; 
			}

			datetime_getstring(datetime_string, &dt_CurAlarm);
			outputStringToBothUARTs(datetime_string);
			
			if (cellVoltageReading.adcWholeWord < CELL_VOLTAGE_THRESHOLD_READ_DATA) {
				len = sprintf(str, "\t power too low to read sensors, %lumV\r\n", (unsigned long)(2.5 * (unsigned long)(cellVoltageReading.adcWholeWord)));
				outputStringToBothUARTs(str);
				break;
			}
			
			// attempt to assure time zone is synchronized
			syncTimeZone(); // internally tests if work is already done
			
			// read sensors
			for (ct = 0; ct < 4; ct++) {
				switch (ct)
				{
				case 0:
					swDnUp = TSL2561_DnLooking;
					swBbIr = TSL2561_CHANNEL_BROADBAND;
					break;
				case 1:
					swDnUp = TSL2561_DnLooking;
					swBbIr = TSL2561_CHANNEL_INFRARED;
					break;
				case 2:
					swDnUp = TSL2561_UpLooking;
					swBbIr = TSL2561_CHANNEL_BROADBAND;
					break;
				case 3:
					swDnUp = TSL2561_UpLooking;
					swBbIr = TSL2561_CHANNEL_INFRARED;
					break;
				}
				intTmp1 = getIrrReading(swDnUp, swBbIr, &irrReadings[ct]);
				if (!intTmp1) {
					len = sprintf(str, "\t%lu", (unsigned long)((unsigned long)irrReadings[ct].irrWholeWord * (unsigned long)irrReadings[ct].irrMultiplier));
				} else if (intTmp1 == errNoI2CAddressAck) { // not present or not responding
					len = sprintf(str, "\t-");
				} else {
					len = sprintf(str, "\n\r Could not get reading, err code: %x \n\r", intTmp1);
				}						
				outputStringToBothUARTs(str);
			}
			if (!temperature_GetReading(&temperatureReading)) {
					len = sprintf(str, "\t%d", (int8_t)(temperatureReading.tmprHiByte));
					outputStringToBothUARTs(str);
			} else
				outputStringToBothUARTs("\t-");
			
			// calc cell voltage from ADC reading earlier
			// formula from datasheet: V(measured) = adcResult * (1024 / Vref)
			// using internal reference, Vref = 2.56V = 2560mV
			// V(measured) = adcResult * 2.5 (units are millivolts, so as to get whole numbers)
			len = sprintf(str, "\t%lu", (unsigned long)(2.5 * (unsigned long)(cellVoltageReading.adcWholeWord)));
			outputStringToBothUARTs(str);
			outputStringToBothUARTs("\r\n");
			
			// begin to build log string while testing, even if we end up not logging data
			strcpy(strLog, "\n\r");
			strcat(strLog, datetime_string);
			irradFlags &= ~((1<<isDarkBBDn) | (1<<isDarkIRDn) | (1<<isDarkBBUp) | (1<<isDarkIRUp)); // default clear
			for (ct = 0; ct < 4; ct++) { // generate irradiance readings
				if (!(irrReadings[ct].validation)) {
					lngTmp1 = (unsigned long)((unsigned long)irrReadings[ct].irrWholeWord * (unsigned long)irrReadings[ct].irrMultiplier);
//						len = sprintf(str, "\t%lu", (unsigned long)((unsigned long)irrReadings[ct].irrWholeWord * (unsigned long)irrReadings[ct].irrMultiplier));
					len = sprintf(str, "\t%lu", lngTmp1);
					if ((ct == 0) || (ct == 3)) { // broadband
						lngTmp2 = darkCutOffBB;
					} else { // infrared
						lngTmp2 = darkCutoffIR;
					}
					if (lngTmp1 < lngTmp2) {
						switch (ct) {
							case 0:
								irradFlags |= (1<<isDarkBBDn);
								break;
							case 1:
								irradFlags |= (1<<isDarkIRDn);
								break;
							case 2:
								irradFlags |= (1<<isDarkBBUp);
								break;
							case 3:
								irradFlags |= (1<<isDarkIRUp);
								break;
						}
					}
				} else { // no valid data for this reading
					len = sprintf(str, "\t");
					// treat invalid readings as if dark
					switch (ct) {
						case 0:
							irradFlags |= (1<<isDarkBBDn);
							break;
						case 1:
							irradFlags |= (1<<isDarkIRDn);
							break;
						case 2:
							irradFlags |= (1<<isDarkBBUp);
							break;
						case 3:
							irradFlags |= (1<<isDarkIRUp);
							break;
					}
				}
				strcat(strLog, str);
			} // end of irradiance sensor validity testing

			if (timeFlags & (1<<timeToLogData)) {
//				outputStringToUART0("\n\r Entered log data routine \n\r");
				// (previously built irradiance part of log string)
				
				// log temperature
				if (!temperatureReading.verification)
					len = sprintf(str, "\t%d", (int8_t)(temperatureReading.tmprHiByte));
				else
					len = sprintf(str, "\t");
				strcat(strLog, str);
				// log cell voltage
				len = sprintf(str, "\t%lu\n\r", (unsigned long)(2.5 * (unsigned long)(cellVoltageReading.adcWholeWord)));
				strcat(strLog, str);
				
				len = strlen(strLog);
				errSD = writeCharsToSDCard(strLog, len);
				if (errSD) {
					tellFileError (errSD);
				} else {
					outputStringToBothUARTs(" Data written to SD card \n\r\n\r");
				}
			} // end of test if time to log data
			
			// test if dark or not dark
			// if all sensors are less than thresholds, or missing; and system not in Roused state
			if ((irradFlags & (1<<isDarkBBDn)) && 
				    (irradFlags & (1<<isDarkIRDn)) && 
					(irradFlags & (1<<isDarkBBUp)) && 
					(irradFlags & (1<<isDarkIRUp)) && 
					(!(stateFlags1 & (1<<isRoused)))) {
				// flag that it is dark
				irradFlags |= (1<<isDark);
			} else { // or not
				// try new algorithm:
				if (cellVoltageReading.adcWholeWord > CELL_VOLTAGE_THRESHOLD_SD_CARD) { // if cell is high
					irradFlags &= ~(1<<isDark); // always leave the Dark state
				} else { // if cell voltage is low, only leave the Dark state
					// if cell has charged somewhat since last reading
					// this should eliminate early morning drain, when data will not be good anyway
					// require a small increase in cell voltage to ignore random jitter
					if (cellVoltageReading.adcWholeWord > (refDarkVoltage + 3)) {
						irradFlags &= ~(1<<isDark);
					}						
				}
			} // end of testing for dark or not dark		


			// let main loop restore Idle state, after assuring timer interrupts are re-established
			break; // if did everything, break here
		} // end of data acquisition segment
				
		turnSDCardPowerOff();
		
		if (stateFlags1 & (1<<reachedFullPower)) { // another full-power-only segment
			if (!BT_connected()) { // timeout diagnostics if no Bluetooth connection
				if (stateFlags1 & (1<<isRoused)) {
					len = sprintf(str, "\r\n sleep in %u seconds\r\n", (rouseCountdown/100));
					outputStringToUART0(str);
				}
				if (BT_powered()) {
					len = sprintf(str, "\r\n BT off in %u seconds\r\n", (btCountdown/100));
					outputStringToUART0(str);
				}	
			}
		} // end of this full-power-only segment
	} // end of main program loop
} // end of fn main