From 5387bcd167b7c9690570946895a34c8e02958c45 Mon Sep 17 00:00:00 2001 From: LowPowerLab Date: Thu, 10 Dec 2015 21:06:18 -0500 Subject: [PATCH] Add MightyHat gateway sample code --- Examples/MightyHat/MightyHat.ino | 630 +++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 Examples/MightyHat/MightyHat.ino diff --git a/Examples/MightyHat/MightyHat.ino b/Examples/MightyHat/MightyHat.ino new file mode 100644 index 0000000..c50f6ff --- /dev/null +++ b/Examples/MightyHat/MightyHat.ino @@ -0,0 +1,630 @@ +// ********************************************************************************************************** +// MightyHat gateway base unit sketch that works with MightyHat with onboard RFM69W/RFM69HW +// This will relay all RF data over serial to the host computer (RaspberryPi, PC etc) and vice versa +// http://LowPowerLab.com/MightyHat +// Copyright http://www.LowPowerLab.com (2015) +// Also see http://LowPowerLab.com/gateway +//********************************************************************************** +// License +// ********************************************************************************** +// 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 3 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, see . +// +// Licence can be viewed at +// http://www.gnu.org/licenses/gpl-3.0.txt +// +// Please maintain this license information along with authorship +// and copyright notices in any redistribution of this code +// ********************************************************************************** + +#include //get it here: http://github.com/lowpowerlab/rfm69 +#include //get it here: https://www.github.com/lowpowerlab/rfm69 +#include //get it here: http://github.com/lowpowerlab/spiflash +#include //get it here: https://github.com/LowPowerLab/WirelessProgramming +#include //comes with Arduino IDE (www.arduino.cc) +#include "U8glib.h" //https://bintray.com/olikraus/u8glib/Arduino + //u8g compared to adafruit lib: https://www.youtube.com/watch?v=lkWZuAnHa2Y + //draing bitmaps: https://www.coconauts.net/blog/2015/01/19/easy-draw-bitmaps-arduino/ +//***************************************************************************************************************************** +// ADJUST THE SETTINGS BELOW DEPENDING ON YOUR HARDWARE/SITUATION! +//***************************************************************************************************************************** +#define NODEID 1 +#define NETWORKID 100 +#define FREQUENCY RF69_915MHZ //Match this with the version of your Moteino! (others: RF69_433MHZ, RF69_868MHZ) +#define ENCRYPTKEY "sampleEncryptKey" //has to be same 16 characters/bytes on all nodes, not more not less! +#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W! +//#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL +//***************************************************************************************************************************** +#define ACK_TIME 30 // # of ms to wait for an ack +#define SERIAL_BAUD 115200 +#define DEBUG_EN //comment out if you don't want any serial verbose output (keep out in real use) + +#define BTN_LED_RED 9 +#define BTN_LED_GRN 6 // This will indicate when Pi has power +#define POWER_LED_RED() { digitalWrite(BTN_LED_RED, HIGH); digitalWrite(BTN_LED_GRN, LOW); } +#define POWER_LED_GRN() { digitalWrite(BTN_LED_RED, LOW); digitalWrite(BTN_LED_GRN, HIGH); } +#define POWER_LED_ORANGE() { digitalWrite(BTN_LED_RED, HIGH); digitalWrite(BTN_LED_GRN, HIGH); } +#define POWER_LED_OFF() { digitalWrite(BTN_LED_RED, LOW); digitalWrite(BTN_LED_GRN, LOW); } +#define ON 1 +#define OFF 0 + +#define FLASH_CS 8 + +#define BUZZER 5 // Buzzer attached to D5 (PWM pin required for tones) +#define BUTTON A2 // Power button pin +#define LATCH_EN 4 +#define LATCH_VAL 7 +#define SIG_SHUTOFF A3 // Signal to Pi to ask for a shutdown +#define SIG_BOOTOK A6 // Signal from Pi that it's OK to cutoff power + // !!NOTE!! Originally this was D7 but it was moved to A0 at least temporarily. + // On MightyBoost R1 you need to connect D7 and A0 with a jumper wire. + // The explanation for this is given here: http://lowpowerlab.com/mightyboost/#source +#define BATTERYSENSE A7 // Sense VBAT_COND signal (when powered externally should read ~3.25v/3.3v (1000-1023), when external power is cutoff it should start reading around 2.85v/3.3v * 1023 ~= 880 (ratio given by 10k+4.7K divider from VBAT_COND = 1.47 multiplier) + // hence the actual input voltage = analogRead(A7) * 0.00322 (3.3v/1024) * 1.47 (10k+4.7k voltage divider ratio) + // when plugged in this should be 4.80v, nothing to worry about + // when on battery power this should decrease from 4.15v (fully charged Lipoly) to 3.3v (discharged Lipoly) + // trigger a shutdown to the target device once voltage is around 3.4v to allow 30sec safe shutdown + +#define BATTERY_VOLTS(analog_reading) analog_reading * 0.00322 * 1.51 // 100/66 is the inverse ratio of the voltage divider ( Batt > 1MEG > A7 > 2MEG > GND ) +#define LOWBATTERYTHRESHOLD 3.65 // a shutdown will be triggered to the target device when battery voltage drops below this (Volts) +#define CHARGINGTHRESHOLD 4.3 +#define RESETHOLDTIME 500 // Button must be hold this many mseconds before a reset is issued (should be much less than SHUTDOWNHOLDTIME) +#define SHUTDOWNHOLDTIME 2000 // Button must be hold this many mseconds before a shutdown sequence is started (should be much less than ForcedShutoffDelay) +#define ShutoffTriggerDelay 6000 // will start checking the SIG_BOOTOK line after this long +#define RESETPULSETIME 500 // When reset is issued, the SHUTOFF signal is held HIGH this many ms +#define ForcedShutoffDelay 7500 // when SIG_BOOTOK==0 (PI in unknown state): if button is held + // for this long, force shutdown (this should be less than RecycleTime) +#define ShutdownFinalDelay 4500 // after shutdown signal is received, delay for this long + // to allow all PI LEDs to stop activity (pulse LED faster) +#define RecycleTime 60000 // window of time in which SIG_BOOTOK is expected to go HIGH + // should be at least 3000 more than Min + // if nothing happens after this window, if button is + // still pressed, force cutoff power, otherwise switch back to normal ON state +#define PRINTPERIOD 1500 + +#ifdef DEBUG_EN + #define DEBUG(input) Serial.print(input) + #define DEBUGln(input) Serial.println(input) +#else + #define DEBUG(input) + #define DEBUGln(input) +#endif + +//******************************************** BEGIN LCD PRIMITIVES ******************************************************************************** +#define PIN_LCD_CS LATCH_VAL //Pin 2 on LCD, lcd CS is shared with Latch value pin since they are both outputs and never HIGH at the same time +#define PIN_LCD_RST A1 //Pin 1 on LCD +#define PIN_LCD_DC A0 //Pin 3 on LCD +#define PIN_LCD_LIGHT 3 //Backlight pin +#define xbmp_logo_width 67 +#define xbmp_logo_height 36 +#define LCD_BACKLIGHT(x) { if (x) analogWrite(PIN_LCD_LIGHT, 100); else analogWrite(PIN_LCD_LIGHT, 0); } + +const uint8_t xbmp_logo[] PROGMEM = { + 0x00, 0x00, 0x00, 0xfe, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x3c, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x42, 0x80, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x42, 0x80, 0x10, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc2, 0xc0, 0x10, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x3c, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x1e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x1e, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x1e, 0x0f, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x42, 0x9e, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, + 0xc2, 0xff, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x42, 0x9e, 0x10, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x1e, 0x0f, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x1e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x21, 0x0f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc2, 0xc0, 0x10, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x42, 0x80, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x42, 0x80, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3c, 0x00, 0x0f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x03, 0x00, 0xb0, + 0x01, 0x00, 0x00, 0x18, 0xc0, 0x00, 0xc3, 0xd9, 0xb6, 0x39, 0xdb, 0x9c, + 0x1b, 0xcf, 0x03, 0x63, 0xdb, 0xb6, 0x6d, 0xdb, 0xb6, 0x19, 0xd8, 0x06, + 0x63, 0xdb, 0xf6, 0x6c, 0xdb, 0xbe, 0x19, 0xde, 0x06, 0x63, 0x33, 0x33, + 0x6c, 0x66, 0x86, 0x19, 0xdb, 0x06, 0x63, 0x33, 0x33, 0x6c, 0x66, 0x86, + 0x19, 0xdb, 0x06, 0xcf, 0x31, 0x33, 0x38, 0x66, 0xbc, 0x79, 0xde, 0x03 }; + +#define xbmp_batt_width 9 +#define xbmp_batt_height 6 +const uint8_t xbmp_batt_c[] PROGMEM = { 0xff, 0x00, 0xbf, 0x00, 0x9f, 0x01, 0x8f, 0x01, 0x87, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_x[] PROGMEM = { 0xff, 0x00, 0xa5, 0x00, 0x81, 0x01, 0x99, 0x01, 0xa5, 0x00, 0xff, 0x00 }; + +const uint8_t xbmp_batt_0[] PROGMEM = { }; +const uint8_t xbmp_batt_1[] PROGMEM = { 0xff, 0x00, 0x83, 0x00, 0x83, 0x01, 0x83, 0x01, 0x83, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_2[] PROGMEM = { 0xff, 0x00, 0x87, 0x00, 0x87, 0x01, 0x87, 0x01, 0x87, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_3[] PROGMEM = { 0xff, 0x00, 0x8f, 0x00, 0x8f, 0x01, 0x8f, 0x01, 0x8f, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_4[] PROGMEM = { 0xff, 0x00, 0x9f, 0x00, 0x9f, 0x01, 0x9f, 0x01, 0x9f, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_5[] PROGMEM = { 0xff, 0x00, 0xbf, 0x00, 0xbf, 0x01, 0xbf, 0x01, 0xbf, 0x00, 0xff, 0x00 }; +const uint8_t xbmp_batt_6[] PROGMEM = { 0xff, 0x00, 0xff, 0x00, 0xff, 0x01, 0xff, 0x01, 0xff, 0x00, 0xff, 0x00 }; + + +#define xbmp_rssi_width 7 +#define xbmp_rssi_height 6 +const uint8_t xbmp_rssi_1[] PROGMEM = { 0x40, 0x10, 0x00, 0x04, 0x04, 0x05 }; +const uint8_t xbmp_rssi_2[] PROGMEM = { 0x40, 0x10, 0x10, 0x14, 0x14, 0x15 }; +const uint8_t xbmp_rssi_3[] PROGMEM = { 0x40, 0x50, 0x50, 0x54, 0x54, 0x55 }; +const uint8_t xbmp_rssi_0[] PROGMEM = { 0x40, 0x10, 0x00, 0x04, 0x00, 0x01 }; +//******************************************** END LCD PRIMITIVES ******************************************************************************** + +//general variables +byte ackCount=0; +String inputstr; +byte inputLen=0; +char BATstr[5]; +char BATvstr[6]; +char RSSIstr[] = "-100dBm"; +char input[64]; +byte temp[61]; +char lcdbuff[80]; +int lastValidReading = 1; +unsigned long lastValidReadingTime = 0; +unsigned long NOW=0; +byte PowerState = OFF; +long lastPeriod = -1; +int rssi=0; +float systemVoltage = 5; +boolean batteryLow=false; +boolean batteryLowShutdown=false; + +//SPI devices (RFM69, FLASH MEM, LCD) +U8GLIB_PCD8544 lcd(PIN_LCD_CS, PIN_LCD_DC, PIN_LCD_RST); +SPIFlash flash(FLASH_CS, 0xEF30); //EF30 for 4mbit Windbond FLASH MEM +#ifdef ENABLE_ATC + RFM69_ATC radio; +#else + RFM69 radio; +#endif + +//******************************************** BEGIN LCD FUNCTIONS ******************************************************************************** +void drawLogo() { + lcd.firstPage(); + do { + lcd.drawXBMP((84-xbmp_logo_width)/2, (48-xbmp_logo_height)/2, xbmp_logo_width, xbmp_logo_height, xbmp_logo); //tutorial: https://www.coconauts.net/blog/2015/01/19/easy-draw-bitmaps-arduino/ + } while(lcd.nextPage()); +} + +void clearDisplay() { + lcd.firstPage(); + do{}while(lcd.nextPage()); +} + +void refreshLCD() { + + byte lcdwidth = lcd.getWidth(); + byte lcdheight = lcd.getHeight(); + char c; + byte i,pos,swidth; + byte * bmpPtr; + + //u8glib picture loop + lcd.firstPage(); + do { + lcd.setFont(u8g_font_profont10); + lcd.setFontRefHeightText(); + lcd.setFontPosTop(); + byte fontheight = lcd.getFontAscent()-lcd.getFontDescent(); + char * textp = lcdbuff; + byte textLength = strlen(textp); + byte line=0; + byte done = false; + + while(textLength && !done) + { + //DEBUGln(textLength); + for (i=1;i<=textLength;i++) + { + c = textp[i]; + textp[i]=0; + swidth = lcd.getStrWidth(textp); + textp[i] = c; + if (swidth > lcdwidth || c=='\n') { pos = i-1; break; } + else if (i==textLength) { done = true; } + } + if (!done) + { + c = textp[pos]; + textp[pos]=0; + } + lcd.drawStr(0, line * fontheight, textp); + if (done) break; + textp[pos] = c; + textp += pos; + textLength -= pos; + line++; + } + + lcd.setFontPosBaseline(); + + //battery + if (systemVoltage >= 4.3) bmpPtr = (byte*)xbmp_batt_c; + else if (systemVoltage >= 4) bmpPtr = (byte*)xbmp_batt_6; + else if (systemVoltage >= 3.9) bmpPtr = (byte*)xbmp_batt_5; + else if (systemVoltage >= 3.8) bmpPtr = (byte*)xbmp_batt_4; + else if (systemVoltage >= 3.7) bmpPtr = (byte*)xbmp_batt_3; + else if (systemVoltage >= 3.6) bmpPtr = (byte*)xbmp_batt_2; + else if (systemVoltage >= 3.5) bmpPtr = (byte*)xbmp_batt_1; + else bmpPtr = (byte*)xbmp_batt_x; + lcd.drawXBMP(lcdwidth-xbmp_batt_width, lcdheight-xbmp_batt_height, xbmp_batt_width, xbmp_batt_height, bmpPtr); + + if (systemVoltage >= CHARGINGTHRESHOLD) + sprintf(BATvstr, "CHRG"); //sprintf(BATvstr, "%sv", BATstr); + else { + sprintf(BATvstr, "%sv", BATstr); + } + lcd.drawStr(50, 48, BATvstr); + + //rssi + if (rssi > -70) bmpPtr = (byte*)xbmp_rssi_3; + else if (rssi > -80) bmpPtr = (byte*)xbmp_rssi_2; + else if (rssi > -90) bmpPtr = (byte*)xbmp_rssi_1; + else if (rssi > -95) bmpPtr = (byte*)xbmp_rssi_0; + lcd.drawXBMP(0, lcdheight-xbmp_rssi_height, xbmp_rssi_width, xbmp_rssi_height, bmpPtr); + lcd.drawStr(xbmp_rssi_width+1, 48, RSSIstr); + + } while(lcd.nextPage()); +} +//******************************************** END LCD FUNCTIONS ******************************************************************************** + + +//parse through any serial commands from the host (Pi) +void handleSerialInput() { + inputLen = readSerialLine(input, 10, 64, 10); //readSerialLine(char* input, char endOfLineChar=10, byte maxLength=64, uint16_t timeout=10); + inputstr = String(input); + inputstr.toUpperCase(); + + if (inputLen > 0) + { + if (inputstr.equals("BEEP")) Beep(10, false); + if (inputstr.equals("BEEP2")) Beep(10, true); + if (inputstr.equals("RAM")) { DEBUG(F("Free RAM bytes: "));DEBUGln(checkFreeRAM()); } + + if (inputstr.equals("KEY?")) + { + Serial.print(F("ENCRYPTKEY:")); + Serial.print(ENCRYPTKEY); + } + + byte targetId = inputstr.toInt(); //extract ID if any + byte colonIndex = inputstr.indexOf(":"); //find position of first colon + if (targetId > 0) inputstr = inputstr.substring(colonIndex+1); //trim "ID:" if any + if (targetId > 0 && targetId != NODEID && targetId != RF69_BROADCAST_ADDR && colonIndex>0 && colonIndex<4 && inputstr.length()>0) + { + inputstr.getBytes(temp, 61); + if (radio.sendWithRetry(targetId, temp, inputstr.length())) + Serial.println(F("ACK:OK")); + else + Serial.println(F("ACK:NOK")); + } + } +} + +void Blink(byte PIN, int DELAY_MS) +{ + pinMode(PIN, OUTPUT); + digitalWrite(PIN,HIGH); + delay(DELAY_MS); + digitalWrite(PIN,LOW); +} + +void setupPowerControl(){ + pinMode(BUTTON, INPUT_PULLUP); + pinMode(SIG_BOOTOK, INPUT); + pinMode(SIG_SHUTOFF, OUTPUT); + pinMode(BTN_LED_RED, OUTPUT); + pinMode(BTN_LED_GRN, OUTPUT); + pinMode(LATCH_EN, OUTPUT); + //digitalWrite(LATCH_EN, LOW); + pinMode(LATCH_VAL, OUTPUT); + pinMode(BATTERYSENSE, INPUT); + digitalWrite(SIG_SHUTOFF, LOW);//added after sudden shutdown quirks, DO NOT REMOVE! +} + +void handlePowerControl() { + int reading = digitalRead(BUTTON); + NOW = millis(); + digitalWrite(SIG_SHUTOFF, LOW);//added after sudden shutdown quirks, DO NOT REMOVE! + + //artificial power ON after a low battery shutdown + if (PowerState == OFF && batteryLowShutdown && systemVoltage >= CHARGINGTHRESHOLD) + reading = HIGH; + + if ((PowerState == ON && batteryLow) || (reading != lastValidReading && NOW - lastValidReadingTime > 200)) + { + lastValidReading = reading; + lastValidReadingTime = NOW; + + if ((PowerState == ON && batteryLow) || reading == LOW) + { + radio.sleep(); + //make sure the button is held down for at least 'RESETHOLDTIME' before taking action (this is to avoid accidental button presses and consequently Pi shutdowns) + NOW = millis(); + while (!batteryLow && (PowerState == ON && millis()-NOW < RESETHOLDTIME)) { delay(10); if (digitalRead(BUTTON) != 0) return; } + + //RESETHOLDTIME is satisfied, now check if button still held until SHUTDOWNHOLDTIME is satisfied + POWER_LED_ORANGE(); //make the button LED orange to show something's going on + while (!batteryLow && (PowerState == ON && millis()-NOW < SHUTDOWNHOLDTIME)) + { + if (digitalRead(BUTTON) != 0) + { + if (BOOTOK()) //SIG_BOOTOK is HIGH so Pi is running the shutdowncheck.sh script, ready to intercept the RESET PULSE + { + sprintf(lcdbuff, "Rebooting Pi.."); + refreshLCD(); + + digitalWrite(SIG_SHUTOFF, HIGH); + delay(RESETPULSETIME); + digitalWrite(SIG_SHUTOFF, LOW); + //DEBUGln("SIG_SHUTOFF - HIGH>delay>LOW"); + + NOW = millis(); + boolean recycleDetected=false; + while (millis()-NOW < RecycleTime) //blink LED while waiting for BOOTOK to go high + { + //blink 3 times and pause + POWER_LED_OFF(); //digitalWrite(POWER_LED, LOW); + delay(100); + POWER_LED_ORANGE(); //digitalWrite(POWER_LED, HIGH); + delay(100); + POWER_LED_OFF(); //digitalWrite(POWER_LED, LOW); + delay(100); + POWER_LED_ORANGE(); //digitalWrite(POWER_LED, HIGH); + delay(100); + POWER_LED_OFF(); //digitalWrite(POWER_LED, LOW); + delay(100); + POWER_LED_ORANGE(); //digitalWrite(POWER_LED, HIGH); + delay(500); + + if (!BOOTOK()) recycleDetected = true; + else if (BOOTOK() && recycleDetected) + return; + } + return; //reboot pulse sent but it appears a reboot failed; exit all checks + } + else return; //ignore everything else (button was held for RESETHOLDTIME, but SIG_BOOTOK was LOW) + } + } + + //SIG_BOOTOK must be HIGH when Pi is ON. During boot, this will take a while to happen (till it executes the "shutdowncheck" script) + //so I dont want to cutoff power before it had a chance to fully boot up + if ((batteryLow || PowerState == ON) && BOOTOK()) + { + if (batteryLow) { + sprintf(lcdbuff, "Battery low! Shutting down Pi.."); + batteryLowShutdown = true; + } + else { + sprintf(lcdbuff, "Shutting down Pi.."); + } + refreshLCD(); + // signal Pi to shutdown + digitalWrite(SIG_SHUTOFF, HIGH); + //DEBUGln("SIG_SHUTOFF - HIGH - if(batteryLow || (PowerState == 1 && BOOTOK())"); + + //now wait for the Pi to signal back + NOW = millis(); + float in, out; + boolean forceShutdown = true; + + POWER_LED_OFF(); + while (millis()-NOW < RecycleTime) + { + if (in > 6.283) in = 0; + in += .00628; + + out = sin(in) * 127.5 + 127.5; + analogWrite(BTN_LED_RED, out); + delayMicroseconds(1500); + + //account for force-shutdown action (if button held for ForcedShutoffDelay, then force shutdown regardless) + if (millis()-NOW <= (ForcedShutoffDelay-SHUTDOWNHOLDTIME) && digitalRead(BUTTON) != 0) + forceShutdown = false; + if (millis()-NOW >= (ForcedShutoffDelay-SHUTDOWNHOLDTIME) && forceShutdown) + { + PowerState = OFF; + POWER_LED_OFF(); //digitalWrite(POWER_LED, PowerState); //turn off LED to indicate power is being cutoff + POWER(PowerState); + break; + } + + if (millis() - NOW > ShutoffTriggerDelay) + { + // Pi signaling OK to turn off + if (!BOOTOK()) + { + PowerState = OFF; + POWER_LED_OFF(); //digitalWrite(POWER_LED, PowerState); //turn off LED to indicate power is being cutoff + NOW = millis(); + while (millis()-NOW < ShutdownFinalDelay) + { + if (in > 6.283) in = 0; + in += .00628; + out = sin(in) * 127.5 + 127.5; + analogWrite(BTN_LED_RED,out); + delayMicroseconds(300); + } + + POWER(PowerState); + break; + } + } + } + + // last chance: if power still on but button still pressed, force cutoff power + if (PowerState == ON && digitalRead(BUTTON) == 0) + { + PowerState = OFF; + POWER(PowerState); + } + + digitalWrite(SIG_SHUTOFF, LOW); + //DEBUGln("SIG_SHUTOFF - LOW"); + } + else if (PowerState == ON && !BOOTOK()) + { + sprintf(lcdbuff, "Forced shutdown.."); + refreshLCD(); + + NOW = millis(); + unsigned long NOW2 = millis(); + int analogstep = 255 / ((ForcedShutoffDelay-SHUTDOWNHOLDTIME)/100); //every 500ms decrease LED intensity + while (digitalRead(BUTTON) == 0) + { + if (millis()-NOW2 > 100) + { + analogWrite(BTN_LED_RED, 255 - ((millis()-NOW)/100)*analogstep); + NOW2 = millis(); + } + if (millis()-NOW > ForcedShutoffDelay-SHUTDOWNHOLDTIME) + { + //TODO: add blinking here to signal final shutdown delay + PowerState = OFF; + POWER(PowerState); + break; + } + } + } + else if (PowerState == OFF) + { + PowerState = ON; + batteryLowShutdown=false; + POWER(PowerState); + } + } + + if (PowerState == ON) POWER_LED_GRN() else POWER_LED_OFF(); //digitalWrite(POWER_LED, PowerState); + } +} + +boolean BOOTOK() { + return analogRead(SIG_BOOTOK) > 800; +} + +void POWER(uint8_t ON_OFF) { + digitalWrite(LATCH_EN, HIGH); + delay(5); + digitalWrite(LATCH_VAL, ON_OFF); + digitalWrite(LATCH_EN, LOW); +} + +void Beep(byte theDelay, boolean twoSounds) +{ + if (theDelay > 20) theDelay = 20; + tone(BUZZER, 4200); //4200 + delay(theDelay); + noTone(BUZZER); + delay(10); + if (twoSounds) + { + tone(BUZZER, 4500); //4500 + delay(theDelay); + noTone(BUZZER); + } +} + +int checkFreeRAM() +{ + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} + +boolean readBattery() { + //periodically read the battery voltage + int currPeriod = millis()/PRINTPERIOD; + if (currPeriod != lastPeriod) + { + lastPeriod=currPeriod; + systemVoltage = BATTERY_VOLTS(analogRead(BATTERYSENSE)); + dtostrf(systemVoltage, 3,2, BATstr); + batteryLow = systemVoltage < LOWBATTERYTHRESHOLD; + //DEBUG("VBAT: ");DEBUG(systemVoltage); + return true; //signal that batt has been read + } + return false; +} + +void setup() { + setupPowerControl(); + Serial.begin(SERIAL_BAUD); + + radio.initialize(FREQUENCY,NODEID,NETWORKID); + radio.encrypt(ENCRYPTKEY); + +#ifdef IS_RFM69HW + radio.setHighPower(); //only for RFM69HW! +#endif + + sprintf(lcdbuff, "Listening @ %dmhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915); + DEBUGln(lcdbuff); + if (flash.initialize()) DEBUGln("SPI Flash Init OK!"); + else DEBUGln(F("SPI Flash MEM not found, skipping...")); + +#ifdef ENABLE_ATC + DEBUGln(F("RFM69_ATC Enabled (Auto Transmission Control)")); +#endif + + //LCD backlight + pinMode(PIN_LCD_LIGHT, OUTPUT); + LCD_BACKLIGHT(1); + lcd.setRot180(); + drawLogo(); + delay(2000); + readBattery(); + Serial.print(F("Free RAM bytes: "));Serial.println(checkFreeRAM()); + refreshLCD(); + delay(1500); +} + +float tempf; +boolean newPacketReceived; +void loop() { + handlePowerControl(); //checks any button presses and takes action + handleSerialInput(); //checks for any serial input from the Pi computer + + //process any received radio packets + newPacketReceived = false; + if (radio.receiveDone()) + { + rssi = radio.RSSI; + if (radio.DATALEN > 0) + { + sprintf(lcdbuff, "[%d] %s", radio.SENDERID, radio.DATA); + sprintf(RSSIstr, "%ddBm", rssi); + Serial.print(lcdbuff); + Serial.print(F(" [RSSI:"));Serial.print(rssi);Serial.print(']'); + } + + //check if the packet is a wireless programming request + CheckForWirelessHEX(radio, flash, false); //non verbose DEBUG + + //respond to any ACK if requested + if (radio.ACKRequested()) + { + byte theNodeID = radio.SENDERID; + radio.sendACK(); + DEBUG(F("[ACK-sent]")); + } + Serial.println(); + Blink(LED,2); + newPacketReceived = true; + } + if (readBattery() || newPacketReceived) refreshLCD(); + //LCD_BACKLIGHT(batteryLow ? 0 : 1); +}