#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
// WORK IN PROGRESS ONLY - PROBABLY WON'T WORK
// Additional functionality:
// - long-press UP to trigger orderly shutdown. In that state press DOWN to restore
-// - long-press DOWN to trigger test voltage reset. Sets average voltage to ZERO
+// - long-press DOWN to trigger test voltage reset. Sets average voltage to ZERO
// temporarily - this triggers warning and shutdown - and eventual reboot
// Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
- uint8_t warningTimeout;
+ uint8_t warningTimeout;
// Device address for the SSD1306 display
uint16_t longpressedUp = 0 ;
uint16_t longpressedDown = 0;
-uint8_t triggerShutdown =0;
+uint8_t triggerShutdown =0;
void LcdP(char *fmt, ...)
- char buf[128]; // resulting string limited to 128 chars
- vsnprintf(buf, 128, fmt, args);
+ char buf[128]; // resulting string limited to 128 chars
+ vsnprintf(buf, 128, fmt, args);
- char buf[128]; // resulting string limited to 128 chars
- vsnprintf(buf, 128, fmt, args);
+ char buf[128]; // resulting string limited to 128 chars
+ vsnprintf(buf, 128, fmt, args);
void gotoXY(int x, int y)
//------------------------------------------------------------------------------
- analogReference(INTERNAL);
- if (eeSave.check != 0x52d2)
- eeSave.highV = SWITCHON;
- eeSave.lowV = SWITCHOFF;
- eeSave.startTimeout = 5;
- eeSave.goodTimeout = 5;
- eeSave.warningTimeout = 20;
- eeSave.offTimeout = 20;
- eeSave.batteryOffset=400;
- oled.begin(&Adafruit128x32, I2C_ADDRESS);
- oled.setFont(Adafruit5x7);
- digitalWrite(POWER, POWEROFF); pinMode(POWER, OUTPUT);
- digitalWrite(WARNING, WARNINGOFF); pinMode(WARNING, OUTPUT);
- pinMode(SET, INPUT_PULLUP);
- pinMode(UP, INPUT_PULLUP);
- pinMode(DOWN, INPUT_PULLUP);
- average = analogRead(VOLTAGE); // we start off with read value;
- my100millis = millis() + 1000;
- stateCounter = eeSave.startTimeout;
+ analogReference(INTERNAL);
+ if (eeSave.check != 0x52d1)
+ eeSave.highV = SWITCHON;
+ eeSave.lowV = SWITCHOFF;
+ eeSave.startTimeout = 5;
+ eeSave.goodTimeout = 5;
+ eeSave.warningTimeout = 25;
+ eeSave.offTimeout = 20;
+ eeSave.batteryOffset=400;
+ oled.begin(&Adafruit128x32, I2C_ADDRESS);
+ oled.setFont(Adafruit5x7);
+ #define WARNINGOFF HIGH
+ digitalWrite(POWER, POWEROFF); pinMode(POWER, OUTPUT);
+ digitalWrite(WARNING, WARNINGOFF); pinMode(WARNING, OUTPUT);
+ pinMode(SET, INPUT_PULLUP);
+ pinMode(UP, INPUT_PULLUP);
+ pinMode(DOWN, INPUT_PULLUP);
+ average = analogRead(VOLTAGE); // we start off with read value;
+ my100millis = millis() + 1000;
+ stateCounter = eeSave.startTimeout;
- LcdP("%d.%02dv", int(vol), ((int(vol * 100)) % 100)); oled.clearToEOL();
+ LcdP("%d.%02dv", int(vol), ((int(vol * 100)) % 100)); oled.clearToEOL();
//------------------------------------------------------------------------------
- if (my100millis <= millis()) // 100ms for key check
- if (digitalRead(SET) == 0)
- if (digitalRead(UP) == 0) // special case long press is reset...
- case 1 : if (eeSave.highV < 4.3) eeSave.highV += 0.1; break;
- case 2 : if (eeSave.lowV < 4.2) eeSave.lowV += 0.1; break;
- case 3 : eeSave.startTimeout++; break;
- case 4 : eeSave.goodTimeout++; break;
- case 5 : eeSave.warningTimeout++; break;
- case 6 : eeSave.offTimeout++; break;
- case 7 : eeSave.batteryOffset++; break;
- if (++longpressedUp==LONGPRESS_UP) triggerShutdown=1;
- { uKeypressed = 0; longpressedUp = 0; }
- if (digitalRead(DOWN) == 0)
- case 1 : if (eeSave.highV > 2.3) eeSave.highV -= 0.1; break;
- case 2 : if (eeSave.lowV > 2.2) eeSave.lowV -= 0.1; break;
- case 3 : eeSave.startTimeout--; break;
- case 4 : eeSave.goodTimeout--; break;
- case 5 : eeSave.warningTimeout--; break;
- case 6 : eeSave.offTimeout--; break;
- case 7 : eeSave.batteryOffset--; break;
- if (++longpressedDown==LONGPRESS_DOWN) average=0; // temporarily drop the voltage and hence trigger warning
- { dKeypressed = 0; longpressedDown = 0; }
- if (eeSave.highV <= eeSave.lowV) eeSave.highV = eeSave.lowV + 0.1;
- if (eeSave.lowV >= eeSave.highV) eeSave.lowV = eeSave.highV - 0.1;
- case 0: if (updated == 0)
- case 1: if (updated == 0)
- LcdP("SET: High V=%d.%d ", int(eeSave.highV), ((int(eeSave.highV * 100)) % 100) / 10); oled.clearToEOL();
- case 2: if (updated == 0)
- LcdP("SET: Low V=%d.%d ", int(eeSave.lowV), ((int(eeSave.lowV * 100)) % 100) / 10); oled.clearToEOL();
- case 3: if (updated == 0)
- LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
- case 4: if (updated == 0)
- LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
- case 5: if (updated == 0)
- LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
- case 6: if (updated == 0)
- LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
- case 7: if (updated == 0)
- LcdP("SET: Battery %d",eeSave.batteryOffset-400); oled.clearToEOL();
- case 8: if (updated == 0)
- LcdP("Version: %s",VER); oled.clearToEOL();
- case 9: updated = 0; instate = 0; break;
- if (--updateTimeout == 0)
- // main 1 second loop here for determining where we are
- if (mymillis <= millis())
- mymillis += 1000; // every second we do this
- average = ((average * 7) + analogRead(VOLTAGE)) / 8; //i.e. average over 8 seconds 0-1023 = 0-5v - might make testing longer in the end
- vol = (((float)average) * 5.0 / 824.0) * (float)eeSave.batteryOffset/400.0; //10k/2k2
- if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
- case POWERUP_STATE : // powered up everything off
- if (stateCounter == eeSave.startTimeout) { gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL(); }
- gotoXY(0, 2); LcdP("Battery: "); showBattery();
- gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
- digitalWrite(WARNING, WARNINGOFF); // no warning
- digitalWrite(POWER, POWEROFF);
- if (vol > eeSave.highV)
- if (stateCounter == 0) {
- stateCounter = eeSave.goodTimeout;
- stateCounter = eeSave.offTimeout;
- case GOOD_STATE : // battery is above upper threshold
- if (stateCounter == eeSave.goodTimeout) { gotoXY(0, 0); LcdP("** Pi Power %s **",VER); oled.clearToEOL(); }
- gotoXY(0, 1); LcdP("Ok. Battery: "); showBattery();
- gotoXY(0, 2); LcdP("High: %d.%dv Low: %d.%dv", int(eeSave.highV), ((int(eeSave.highV * 100)) % 100) / 10, int(eeSave.lowV), ((int(eeSave.lowV * 100)) % 100) / 10); oled.clearToEOL();
- digitalWrite(WARNING, WARNINGOFF); // no warning
- digitalWrite(POWER, POWERON);
- gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
- if (stateCounter == 0) {
- stateCounter = eeSave.warningTimeout;
- stateCounter = eeSave.goodTimeout-1;
- if ((instate==0) && (vol >= eeSave.lowV))
- if (mySecs&2) { LcdP("On: %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL(); }
- else { LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL(); }
- case TIMEOUT_STATE : // battery has dropped below upper threshold - ALWAYS timeout to OFF_STATE
- gotoXY(0, 2); LcdP("Timing out. B: "); showBattery();
- gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
- digitalWrite(WARNING, WARNINGON); // warning
- digitalWrite(POWER, POWERON);
- if (stateCounter == 0 ) {
- stateCounter=eeSave.offTimeout;
- case OFF_STATE : // everything off
- if (stateCounter == eeSave.offTimeout) oled.clear();
- gotoXY(0, 2); LcdP("Stdby. V="); showBattery();
- digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
- digitalWrite(POWER, POWEROFF);
- if (vol > eeSave.highV)
- gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
- if (stateCounter == 0) {
- stateCounter = eeSave.goodTimeout;
- stateCounter = eeSave.offTimeout-1; // avoid clearscreen
- gotoXY(0, 3); oled.clearToEOL();
- case SHUTDOWN_STATE: // everything off and stays off... hold the SET button.... set state to SHUTDOWN_STATE and stateCounter to SHUTDOWN_PERIOD
- if (stateCounter==SHUTDOWN_PERIOD)
- gotoXY(0, 2); LcdP("Shutdown commencing.."); oled.clearToEOL();
- digitalWrite(WARNING, WARNINGON); // warning
- gotoXY(0, 2); LcdP("Shutdown COMPLETE."); oled.clearToEOL();
- digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
- digitalWrite(POWER, POWEROFF);
- else if (digitalRead(DOWN) == 0) { oled.clear(); state=POWERUP_STATE; stateCounter = eeSave.startTimeout; }
+ if (my100millis <= millis()) // 100ms for key check
+ if (digitalRead(SET) == 0)
+ if (digitalRead(UP) == 0) // special case long press is reset...
+ case 1 : if (eeSave.highV < 4.3) eeSave.highV += 0.1; break;
+ case 2 : if (eeSave.lowV < 4.2) eeSave.lowV += 0.1; break;
+ case 3 : eeSave.startTimeout++; break;
+ case 4 : eeSave.goodTimeout++; break;
+ case 5 : eeSave.warningTimeout++; break;
+ case 6 : eeSave.offTimeout++; break;
+ case 7 : eeSave.batteryOffset++; break;
+ if (++longpressedUp==LONGPRESS_UP) triggerShutdown=1;
+ { uKeypressed = 0; longpressedUp = 0; }
+ if (digitalRead(DOWN) == 0)
+ case 1 : if (eeSave.highV > 2.3) eeSave.highV -= 0.1; break;
+ case 2 : if (eeSave.lowV > 2.2) eeSave.lowV -= 0.1; break;
+ case 3 : eeSave.startTimeout--; break;
+ case 4 : eeSave.goodTimeout--; break;
+ case 5 : eeSave.warningTimeout--; break;
+ case 6 : eeSave.offTimeout--; break;
+ case 7 : eeSave.batteryOffset--; break;
+ if (++longpressedDown==LONGPRESS_DOWN) average=0; // temporarily drop the voltage and hence trigger warning
+ { dKeypressed = 0; longpressedDown = 0; }
+ if (eeSave.highV <= eeSave.lowV) eeSave.highV = eeSave.lowV + 0.1;
+ if (eeSave.lowV >= eeSave.highV) eeSave.lowV = eeSave.highV - 0.1;
+ case 0: if (updated == 0)
+ case 1: if (updated == 0)
+ LcdP("SET: High V=%d.%d ", int(eeSave.highV), ((int(eeSave.highV * 100)) % 100) / 10); oled.clearToEOL();
+ case 2: if (updated == 0)
+ LcdP("SET: Low V=%d.%d ", int(eeSave.lowV), ((int(eeSave.lowV * 100)) % 100) / 10); oled.clearToEOL();
+ case 3: if (updated == 0)
+ LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
+ case 4: if (updated == 0)
+ LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
+ case 5: if (updated == 0)
+ LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
+ case 6: if (updated == 0)
+ LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
+ case 7: if (updated == 0)
+ LcdP("SET: Battery %d",eeSave.batteryOffset-400); oled.clearToEOL();
+ case 8: if (updated == 0)
+ LcdP("Version: %s",VER); oled.clearToEOL();
+ case 9: updated = 0; instate = 0; break;
+ if (--updateTimeout == 0)
+ // main 1 second loop here for determining where we are
+ if (mymillis <= millis())
+ mymillis += 1000; // every second we do this
+ average = ((average * 7) + analogRead(VOLTAGE)) / 8; //i.e. average over 8 seconds 0-1023 = 0-5v - might make testing longer in the end
+ vol = (((float)average) * 5.0 / 824.0) * (float)eeSave.batteryOffset/400.0; //10k/2k2
+ if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
+ case POWERUP_STATE : // powered up everything off
+ if (stateCounter == eeSave.startTimeout) { gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL(); }
+ gotoXY(0, 2); LcdP("Battery: "); showBattery();
+ gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
+ digitalWrite(WARNING, WARNINGON); // no warning
+ digitalWrite(POWER, POWEROFF);
+ if (vol > eeSave.highV)
+ if (stateCounter == 0) {
+ stateCounter = eeSave.goodTimeout;
+ stateCounter = eeSave.offTimeout;
+ case GOOD_STATE : // battery is above upper threshold
+ if (stateCounter == eeSave.goodTimeout) { gotoXY(0, 0); LcdP("** Pi Power %s **",VER); oled.clearToEOL(); }
+ gotoXY(0, 1); LcdP("Ok. Battery: "); showBattery();
+ gotoXY(0, 2); LcdP("High: %d.%dv Low: %d.%dv", int(eeSave.highV), ((int(eeSave.highV * 100)) % 100) / 10, int(eeSave.lowV), ((int(eeSave.lowV * 100)) % 100) / 10); oled.clearToEOL();
+ digitalWrite(WARNING, WARNINGOFF); // no warning
+ digitalWrite(POWER, POWERON);
+ gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
+ if (stateCounter == 0) {
+ stateCounter = eeSave.warningTimeout;
+ stateCounter = eeSave.goodTimeout-1;
+ if ((instate==0) && (vol >= eeSave.lowV))
+ if (mySecs&2) { LcdP("On: %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL(); }
+ else { LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL(); }
+ case TIMEOUT_STATE : // battery has dropped below upper threshold - ALWAYS timeout to OFF_STATE
+ gotoXY(0, 2); LcdP("Timing out. B: "); showBattery();
+ gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
+ digitalWrite(WARNING, WARNINGON); // warning
+ digitalWrite(POWER, POWERON);
+ if (stateCounter == 0 ) {
+ stateCounter=eeSave.offTimeout;
+ case OFF_STATE : // everything off
+ if (stateCounter == eeSave.offTimeout) oled.clear();
+ gotoXY(0, 2); LcdP("Stdby. V="); showBattery();
+ digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
+ digitalWrite(POWER, POWEROFF);
+ if (vol > eeSave.highV)
+ gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
+ if (stateCounter == 0) {
+ stateCounter = eeSave.goodTimeout;
+ stateCounter = eeSave.offTimeout-1; // avoid clearscreen
+ gotoXY(0, 3); oled.clearToEOL();
+ case SHUTDOWN_STATE: // everything off and stays off... hold the SET button.... set state to SHUTDOWN_STATE and stateCounter to SHUTDOWN_PERIOD
+ if (stateCounter==SHUTDOWN_PERIOD)
+ gotoXY(0, 2); LcdP("Shutdown commencing.."); oled.clearToEOL();
+ digitalWrite(WARNING, WARNINGON); // warning
+ gotoXY(0, 2); LcdP("Shutdown COMPLETE"); oled.clearToEOL();
+ digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
+ digitalWrite(POWER, POWEROFF);
+ else if (digitalRead(DOWN) == 0) { oled.clear(); state=POWERUP_STATE; stateCounter = eeSave.startTimeout; }