Snippets

Peter Scargill Uninterruptible for Pi and other boards - using Atmega 328 processor

Updated by Peter Scargill

File uninterruptible.c Modified

  • Ignore whitespace
  • Hide word diff
 // Then turns back on when battery is up to it. BIG hysteresis between
 // shutdown voltage and wakeup voltage
 //
+// Additional functionality - long-press UP to trigger orderly shutdown. In that state press DOWN to restore
+//
 
-#define VER "v1.02"
+#define VER "v1.03"
 struct
 {
   uint16_t check;
 #define SWITCHON 3.7    // defaults for hysteresis voltage
 #define SWITCHOFF 3.1
 
-#define LONGPRESS 60   // 10ths of second before long presson UP triggers shutdown
+#define LONGPRESS_UP 60   // 10ths of second before long presson UP triggers shutdown
+#define LONGPRESS_DOWN 60 // 10ths of second before battery fail test
 
 #define SHUTDOWN_PERIOD 20
 
 uint32_t upSecs = 0;
 uint32_t downSecs = 0;
 
-uint16_t longPressed = 0 ;
+uint16_t longpressedUp = 0 ;
+uint16_t longpressedDown = 0;
+
 uint8_t triggerShutdown =0; 
 
 void LcdP(char *fmt, ...)
 {
   EEPROM.get(0, eeSave);
   analogReference(INTERNAL);
-  if (eeSave.check != 0x52d2)
+  if (eeSave.check != 0x52d1)
   {
-    eeSave.check = 0x52d2;
+    eeSave.check = 0x52d1;
 
     eeSave.highV = SWITCHON;
     eeSave.lowV = SWITCHOFF;
     eeSave.startTimeout = 5;
-    eeSave.goodTimeout = 10;
+    eeSave.goodTimeout = 5;
     eeSave.warningTimeout = 20;
     eeSave.offTimeout = 20;
 
       }
       else
       {
-       if (++longPressed==LONGPRESS) triggerShutdown=1;
+       if (++longpressedUp==LONGPRESS_UP) triggerShutdown=1;
       }
     }
     else
-      { uKeypressed = 0; longPressed = 0; }
+      { uKeypressed = 0; longpressedUp = 0; }
       
     if (digitalRead(DOWN) == 0)
     {
         updated = 0;
         updateTimeout = 100;
       }
+      else
+      {
+       if (++longpressedDown==LONGPRESS_DOWN) average=0; // temporarily drop the voltage and hence trigger warning
+      }
     }
     else
-      dKeypressed = 0;
+      { 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;
     switch (instate)
         if (stateCounter == eeSave.startTimeout) { gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL(); }
         --stateCounter;
         gotoXY(0, 2); LcdP("Battery: "); showBattery();
-        gotoXY(0, 3); LcdP("Secs: %03d",stateCounter); oled.clearToEOL();
+        gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWEROFF);
         if (vol > eeSave.highV)
         digitalWrite(POWER, POWERON);
         if (vol < eeSave.lowV)
         {
-          gotoXY(0, 3); LcdP("Secs: %03d",stateCounter); oled.clearToEOL();
+          gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
           gotoXY(0,4); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.warningTimeout;
 
       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("Secs: %03d",stateCounter); oled.clearToEOL();
+        gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGON); // warning
         digitalWrite(POWER, POWERON);
         --stateCounter;
         digitalWrite(POWER, POWEROFF);
         if (vol > eeSave.highV)
         {
-          gotoXY(0, 3); LcdP("Secs %03d",stateCounter); oled.clearToEOL();
+          gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.goodTimeout;
             state = GOOD_STATE;
Updated by Peter Scargill

File uninterruptible.c Modified

  • Ignore whitespace
  • Hide word diff
 // shutdown voltage and wakeup voltage
 //
 
-#define VER "v1.01"
+#define VER "v1.02"
 struct
 {
   uint16_t check;
 #define GOOD_STATE 1
 #define TIMEOUT_STATE 2
 #define OFF_STATE 3
+#define SHUTDOWN_STATE 4
 
 #define WAITING 60      // wait before actually turning off "relay" after
 #define ONDELAY 20      // delay before turning on
 #define SWITCHON 3.7    // defaults for hysteresis voltage
 #define SWITCHOFF 3.1
 
+#define LONGPRESS 60   // 10ths of second before long presson UP triggers shutdown
+
+#define SHUTDOWN_PERIOD 20
+
 #define SET 4
 #define UP 6
 #define DOWN 7
 uint32_t upSecs = 0;
 uint32_t downSecs = 0;
 
+uint16_t longPressed = 0 ;
+uint8_t triggerShutdown =0; 
+
 void LcdP(char *fmt, ...)
 {
   char buf[128]; // resulting string limited to 128 chars
   oled.setFont(Adafruit5x7);
   uint32_t m = micros();
 
-  Serial.begin(57600);
-
 #define POWERON LOW
 #define POWEROFF HIGH
 #define WARNINGON LOW
   if (my100millis <= millis()) // 100ms for key check
   {
     my100millis += 100;
-    if (digitalRead(SET) == 0)
+    if (digitalRead(SET) == 0) 
     {
       if (sKeypressed == 0)
       {
     }
     else
       sKeypressed = 0;
-    if (digitalRead(UP) == 0)
+      
+    if (digitalRead(UP) == 0) // special case long press is reset...
     {
       if (uKeypressed == 0)
       {
         updated = 0;
         updateTimeout = 100;
       }
+      else
+      {
+       if (++longPressed==LONGPRESS) triggerShutdown=1;
+      }
     }
     else
-      uKeypressed = 0;
+      { uKeypressed = 0; longPressed = 0; }
+      
     if (digitalRead(DOWN) == 0)
     {
       if (dKeypressed == 0)
     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; //10k/2k2
 
+    if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
+
     switch (state)
     {
       case POWERUP_STATE : // powered up everything off
         if (stateCounter == eeSave.startTimeout) { gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL(); }
         --stateCounter;
         gotoXY(0, 2); LcdP("Battery: "); showBattery();
-        gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
+        gotoXY(0, 3); LcdP("Secs: %03d",stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWEROFF);
         if (vol > eeSave.highV)
 
       case GOOD_STATE :  // battery is above upper threshold
         if (stateCounter == eeSave.goodTimeout) { gotoXY(0, 0); LcdP("** Pi Power %s **",VER); oled.clearToEOL(); }
+        --stateCounter;
         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();
-        gotoXY(0, 3);
-        LcdP("On:  %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL();
-        gotoXY(0, 4);
-        LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWERON);
         if (vol < eeSave.lowV)
         {
-          gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
+          gotoXY(0, 3); LcdP("Secs: %03d",stateCounter); oled.clearToEOL();
+          gotoXY(0,4); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.warningTimeout;
             state = TIMEOUT_STATE;
           }
-        } else stateCounter = eeSave.goodTimeout;
-        --stateCounter;
+        } else 
+            {
+              stateCounter = eeSave.goodTimeout-1;
+              gotoXY(0, 3);
+              LcdP("On:  %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL();
+              gotoXY(0, 4);
+              LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL();
+            }
         ++upSecs;
         break;
 
       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("Count: %03d",stateCounter); oled.clearToEOL();
-        digitalWrite(WARNING, WARNINGON); // no warning
+        gotoXY(0, 3); LcdP("Secs: %03d",stateCounter); oled.clearToEOL();
+        digitalWrite(WARNING, WARNINGON); // warning
         digitalWrite(POWER, POWERON);
+        --stateCounter;
         if (stateCounter == 0 ) {
           stateCounter=eeSave.offTimeout;
           state = OFF_STATE;
         }
-        --stateCounter;
         ++upSecs;
         break;
 
       case OFF_STATE : // everything off
         if (stateCounter == eeSave.offTimeout) oled.clear();
-        gotoXY(0, 2); LcdP("Off: "); showBattery();
-        digitalWrite(WARNING, WARNINGOFF); // no warning
+        --stateCounter;
+        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("Count: %03d",stateCounter); oled.clearToEOL();
+          gotoXY(0, 3); LcdP("Secs %03d",stateCounter); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.goodTimeout;
             state = GOOD_STATE;
           }
         } else 
           {
-            stateCounter = eeSave.offTimeout; // avoid clearscreen
+            stateCounter = eeSave.offTimeout-1; // avoid clearscreen
             gotoXY(0, 3); oled.clearToEOL();
           }
-        --stateCounter;
         ++downSecs;
         break;
+
+     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) 
+          { 
+            oled.clear();
+            gotoXY(0, 2); LcdP("Shutdown commencing.."); oled.clearToEOL();
+            digitalWrite(WARNING, WARNINGON); // warning
+          }
+        if (stateCounter)
+          {--stateCounter;
+          if (stateCounter==0) 
+            { 
+              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; }  
     }
   }
 }
Updated by Peter Scargill

File uninterruptible.c Modified

  • Ignore whitespace
  • Hide word diff
 // WORK IN PROGRESS ONLY - PROBABLY WON'T WORK
 // Very first attempt at a backup system that "does the job"
 // Peter Scargill 2017  - https://tech.scargill.net
-// 
-// So this powers up with an SSD1306 display, showing the 
+//
+// So this powers up with an SSD1306 display, showing the
 // battery voltage (needs dividers - I used 10k and 2k2) to an
 // analog in - voltage calc routine needs tweaking
 // averages voltage to avoid spikes and save hardware filtering
 //
 // Starts up - if voltage above upper threshhold for a while turns
-// output bit on - that controls a P-channel mosfet on output 
+// output bit on - that controls a P-channel mosfet on output
 // of booster - power board from booster - OR power board from battery
 // and control booster chip directly
 // There is a warning bit, normally low - to warn micro of impending
 // Then turns back on when battery is up to it. BIG hysteresis between
 // shutdown voltage and wakeup voltage
 //
+
+#define VER "v1.01"
 struct
 {
+  uint16_t check;
   float highV;
   float lowV;
-  uint16_t check;
-} myVars;
+  uint8_t startTimeout;
+  uint8_t goodTimeout;
+  uint8_t warningTimeout;
+  uint8_t offTimeout;
+} eeSave;
 
 // Device address for the SSD1306 display
 #define I2C_ADDRESS 0x3C
 //------------------------------------------------------------------------------
 void setup()
 {
-  EEPROM.get(0, myVars);
+  EEPROM.get(0, eeSave);
   analogReference(INTERNAL);
-  if (myVars.check != 0x52d2)
+  if (eeSave.check != 0x52d2)
   {
-    myVars.check = 0x52d2;
-    myVars.highV = SWITCHON;
-    myVars.lowV = SWITCHOFF;
-    EEPROM.put(0, myVars);
+    eeSave.check = 0x52d2;
+
+    eeSave.highV = SWITCHON;
+    eeSave.lowV = SWITCHOFF;
+    eeSave.startTimeout = 5;
+    eeSave.goodTimeout = 10;
+    eeSave.warningTimeout = 20;
+    eeSave.offTimeout = 20;
+
+    EEPROM.put(0, eeSave);
   }
+
   Wire.begin();
   oled.begin(&Adafruit128x64, I2C_ADDRESS);
   oled.set400kHz();
   oled.clear();
   my100millis = millis() + 1000;
   state = 0;
-  stateCounter = 0;
+  stateCounter = eeSave.startTimeout;
 }
 
 
 //------------------------------------------------------------------------------
 void loop()
 {
-  // pmain loop
+  // main loop
 
   if (my100millis <= millis()) // 100ms for key check
   {
       if (uKeypressed == 0)
       {
         uKeypressed = 1;
-        if (instate == 1)
-          myVars.highV += 0.1;
-        else if (instate == 2)
-          myVars.lowV += 0.1;
+        switch (instate)
+        {
+          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;
+        }
         updated = 0;
         updateTimeout = 100;
       }
       if (dKeypressed == 0)
       {
         dKeypressed = 1;
-        if (instate == 1)
-          myVars.highV -= 0.1;
-        else if (instate == 2)
-          myVars.lowV -= 0.1;
+        switch (instate)
+        {
+          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;
+        }
         updated = 0;
         updateTimeout = 100;
       }
     }
     else
       dKeypressed = 0;
-    if (myVars.lowV < 2.0) myVars.lowV = 2.0;
-    if (myVars.highV > 4.2) myVars.highV = 4.2;
-    if (myVars.highV <= myVars.lowV) myVars.highV = myVars.lowV + 0.1;
-    if (myVars.lowV >= myVars.highV) myVars.lowV = myVars.highV - 0.1;
+    if (eeSave.highV <= eeSave.lowV) eeSave.highV = eeSave.lowV + 0.1;
+    if (eeSave.lowV >= eeSave.highV) eeSave.lowV = eeSave.highV - 0.1;
     switch (instate)
     {
       case 0:  if (updated == 0)
       case 1:  if (updated == 0)
         {
           gotoXY(0, 5);
-          LcdP("SET: High V=%d.%d   ", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10); oled.clearToEOL();
+          LcdP("SET: High V=%d.%d   ", int(eeSave.highV), ((int(eeSave.highV * 100)) % 100) / 10); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 2:  if (updated == 0)
         {
           gotoXY(0, 5);
-          LcdP("SET: Low  V=%d.%d   ", int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
+          LcdP("SET: Low  V=%d.%d   ", int(eeSave.lowV), ((int(eeSave.lowV * 100)) % 100) / 10); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 3:  if (updated == 0)
         {
           gotoXY(0, 5);
-          LcdP("Version 1.01"); oled.clearToEOL();
+          LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 4:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 5:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 6:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 7:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP(VER); oled.clearToEOL();
           updated = 1;
         }
         break;
-      case 4:  updated = 0; instate = 0; break;
+      case 8:  updated = 0; instate = 0; break;
     }
 
     if (updateTimeout)
       {
         instate = 0;
         updated = 0;
-        EEPROM.put(0, myVars);
+        EEPROM.put(0, eeSave);
       }
     }
   }
     switch (state)
     {
       case POWERUP_STATE : // powered up everything off
-        if (stateCounter == 0)
-        {
-          gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL();
-        }
+        if (stateCounter == eeSave.startTimeout) { gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL(); }
+        --stateCounter;
         gotoXY(0, 2); LcdP("Battery: "); showBattery();
-
+        gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWEROFF);
-        if (vol > myVars.highV)
+        if (vol > eeSave.highV)
         {
-          if (++stateCounter > 3) {
-            stateCounter = 0;
+          if (stateCounter == 0) {
+            stateCounter = eeSave.goodTimeout;
             state = GOOD_STATE;
           }
         } else
-        {
-          stateCounter = 0;
-          if (++startCounter > 10) state = OFF_STATE;
+        { 
+          if (stateCounter==0) {
+          stateCounter = eeSave.offTimeout;
+          state = OFF_STATE;
+          }
         }
         break;
 
       case GOOD_STATE :  // battery is above upper threshold
-        if (stateCounter == 0) {
-          gotoXY(0, 0);
-          LcdP("** Pi Power v0.1 **");
-          oled.clearToEOL();
-        }
+        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(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10, int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
+        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();
         gotoXY(0, 3);
         LcdP("On:  %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL();
         gotoXY(0, 4);
         LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWERON);
-        if (vol < myVars.lowV)
+        if (vol < eeSave.lowV)
         {
-          if (++stateCounter > 2) {
-            stateCounter = 0;
+          gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
+          if (stateCounter == 0) {
+            stateCounter = eeSave.warningTimeout;
             state = TIMEOUT_STATE;
           }
-        } else stateCounter = 1;
-        upSecs++;
+        } else stateCounter = eeSave.goodTimeout;
+        --stateCounter;
+        ++upSecs;
         break;
 
       case TIMEOUT_STATE : // battery has dropped below upper threshold - ALWAYS timeout to OFF_STATE
-        gotoXY(0, 1); LcdP("Timing out. B: "); showBattery();
+        gotoXY(0, 2); LcdP("Timing out. B: "); showBattery();
+        gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGON); // no warning
         digitalWrite(POWER, POWERON);
-        if (++stateCounter > 10) {
-          stateCounter = 0;
+        if (stateCounter == 0 ) {
+          stateCounter=eeSave.offTimeout;
           state = OFF_STATE;
         }
-        downSecs++;
+        --stateCounter;
+        ++upSecs;
         break;
 
       case OFF_STATE : // everything off
-        if (stateCounter == 0) oled.clear();
-        gotoXY(0, 1); LcdP("Off: "); showBattery();
+        if (stateCounter == eeSave.offTimeout) oled.clear();
+        gotoXY(0, 2); LcdP("Off: "); showBattery();
         digitalWrite(WARNING, WARNINGOFF); // no warning
         digitalWrite(POWER, POWEROFF);
-        if (vol > myVars.highV)
+        if (vol > eeSave.highV)
         {
-          if (++stateCounter > 5) {
-            stateCounter = 0;
+          gotoXY(0, 3); LcdP("Count: %03d",stateCounter); oled.clearToEOL();
+          if (stateCounter == 0) {
+            stateCounter = eeSave.goodTimeout;
             state = GOOD_STATE;
           }
-        } else stateCounter = 1; // avoid clearscreen
-        downSecs++;
+        } else 
+          {
+            stateCounter = eeSave.offTimeout; // avoid clearscreen
+            gotoXY(0, 3); oled.clearToEOL();
+          }
+        --stateCounter;
+        ++downSecs;
         break;
     }
   }
Created by Peter Scargill

File uninterruptible.c Added

  • Ignore whitespace
  • Hide word diff
+#include <Wire.h>
+#include "SSD1306Ascii.h"
+#include "SSD1306AsciiWire.h"
+#include <EEPROM.h>
+
+// WORK IN PROGRESS ONLY - PROBABLY WON'T WORK
+// Very first attempt at a backup system that "does the job"
+// Peter Scargill 2017  - https://tech.scargill.net
+// 
+// So this powers up with an SSD1306 display, showing the 
+// battery voltage (needs dividers - I used 10k and 2k2) to an
+// analog in - voltage calc routine needs tweaking
+// averages voltage to avoid spikes and save hardware filtering
+//
+// Starts up - if voltage above upper threshhold for a while turns
+// output bit on - that controls a P-channel mosfet on output 
+// of booster - power board from booster - OR power board from battery
+// and control booster chip directly
+// There is a warning bit, normally low - to warn micro of impending
+// shutdown
+//
+// Shuts down power after giving load time to respond and turn off
+// also shuts down after this point EVEN if the power came back on
+// after giving the processor time if it was JUST after warning bit went
+// on/ While off uses minimal display area to save current (OLED)
+// Then turns back on when battery is up to it. BIG hysteresis between
+// shutdown voltage and wakeup voltage
+//
+struct
+{
+  float highV;
+  float lowV;
+  uint16_t check;
+} myVars;
+
+// Device address for the SSD1306 display
+#define I2C_ADDRESS 0x3C
+
+SSD1306AsciiWire oled;
+
+#define POWER 12  // inverted - ON - power available for my cheap version
+#define WARNING 5 // inverted
+
+#define POWERUP_STATE 0
+#define GOOD_STATE 1
+#define TIMEOUT_STATE 2
+#define OFF_STATE 3
+
+#define WAITING 60      // wait before actually turning off "relay" after
+#define ONDELAY 20      // delay before turning on
+
+#define SWITCHON 3.7    // defaults for hysteresis voltage
+#define SWITCHOFF 3.1
+
+#define SET 4
+#define UP 6
+#define DOWN 7
+
+#define VOLTAGE A7
+
+uint32_t secs = 0;
+uint32_t upSecs = 0;
+uint32_t downSecs = 0;
+
+void LcdP(char *fmt, ...)
+{
+  char buf[128]; // resulting string limited to 128 chars
+  va_list args;
+  va_start(args, fmt);
+  vsnprintf(buf, 128, fmt, args);
+  va_end(args);
+  oled.print(buf);
+}
+
+uint32_t mymillis = 0;
+uint32_t my100millis = 0;
+uint8_t lightState = 0;
+uint8_t state = 0; // 0 is power up, 1 is on, 2 is turning off
+uint8_t stateCounter = 0; // might want to stay in a state for some time
+uint8_t startCounter = 0; // startup fallover into OFF state
+float vol;
+uint8_t onDelay = 0;
+
+uint16_t average;
+
+uint8_t updateTimeout = 0;
+
+char *theState = "standby";
+
+uint8_t instate = 0;
+uint8_t updated = 0;
+uint8_t sKeypressed = 0;
+uint8_t uKeypressed = 0;
+uint8_t dKeypressed = 0;
+
+void p(char *fmt, ...)
+{
+  char buf[128]; // resulting string limited to 128 chars
+  va_list args;
+  va_start(args, fmt);
+  vsnprintf(buf, 128, fmt, args);
+  va_end(args);
+  Serial.print(buf);
+}
+
+void gotoXY(int x, int y)
+{
+  oled.setCursor(x, y);
+}
+
+//------------------------------------------------------------------------------
+void setup()
+{
+  EEPROM.get(0, myVars);
+  analogReference(INTERNAL);
+  if (myVars.check != 0x52d2)
+  {
+    myVars.check = 0x52d2;
+    myVars.highV = SWITCHON;
+    myVars.lowV = SWITCHOFF;
+    EEPROM.put(0, myVars);
+  }
+  Wire.begin();
+  oled.begin(&Adafruit128x64, I2C_ADDRESS);
+  oled.set400kHz();
+  oled.setFont(Adafruit5x7);
+  uint32_t m = micros();
+
+  Serial.begin(57600);
+
+#define POWERON LOW
+#define POWEROFF HIGH
+#define WARNINGON LOW
+#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;
+  oled.clear();
+  my100millis = millis() + 1000;
+  state = 0;
+  stateCounter = 0;
+}
+
+
+void showBattery(void)
+{
+  LcdP("%d.%02dv", int(vol), ((int(vol * 100)) % 100)); oled.clearToEOL();
+}
+//------------------------------------------------------------------------------
+void loop()
+{
+  // pmain loop
+
+  if (my100millis <= millis()) // 100ms for key check
+  {
+    my100millis += 100;
+    if (digitalRead(SET) == 0)
+    {
+      if (sKeypressed == 0)
+      {
+        sKeypressed = 1;
+        instate++;
+        updated = 0;
+        updateTimeout = 100;
+      }
+    }
+    else
+      sKeypressed = 0;
+    if (digitalRead(UP) == 0)
+    {
+      if (uKeypressed == 0)
+      {
+        uKeypressed = 1;
+        if (instate == 1)
+          myVars.highV += 0.1;
+        else if (instate == 2)
+          myVars.lowV += 0.1;
+        updated = 0;
+        updateTimeout = 100;
+      }
+    }
+    else
+      uKeypressed = 0;
+    if (digitalRead(DOWN) == 0)
+    {
+      if (dKeypressed == 0)
+      {
+        dKeypressed = 1;
+        if (instate == 1)
+          myVars.highV -= 0.1;
+        else if (instate == 2)
+          myVars.lowV -= 0.1;
+        updated = 0;
+        updateTimeout = 100;
+      }
+    }
+    else
+      dKeypressed = 0;
+    if (myVars.lowV < 2.0) myVars.lowV = 2.0;
+    if (myVars.highV > 4.2) myVars.highV = 4.2;
+    if (myVars.highV <= myVars.lowV) myVars.highV = myVars.lowV + 0.1;
+    if (myVars.lowV >= myVars.highV) myVars.lowV = myVars.highV - 0.1;
+    switch (instate)
+    {
+      case 0:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 1:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("SET: High V=%d.%d   ", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 2:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("SET: Low  V=%d.%d   ", int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 3:  if (updated == 0)
+        {
+          gotoXY(0, 5);
+          LcdP("Version 1.01"); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 4:  updated = 0; instate = 0; break;
+    }
+
+    if (updateTimeout)
+    {
+      if (--updateTimeout == 0)
+      {
+        instate = 0;
+        updated = 0;
+        EEPROM.put(0, myVars);
+      }
+    }
+  }
+
+  // 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; //10k/2k2
+
+    switch (state)
+    {
+      case POWERUP_STATE : // powered up everything off
+        if (stateCounter == 0)
+        {
+          gotoXY(0, 0); LcdP("Starting up"); oled.clearToEOL();
+        }
+        gotoXY(0, 2); LcdP("Battery: "); showBattery();
+
+        digitalWrite(WARNING, WARNINGOFF); // no warning
+        digitalWrite(POWER, POWEROFF);
+        if (vol > myVars.highV)
+        {
+          if (++stateCounter > 3) {
+            stateCounter = 0;
+            state = GOOD_STATE;
+          }
+        } else
+        {
+          stateCounter = 0;
+          if (++startCounter > 10) state = OFF_STATE;
+        }
+        break;
+
+      case GOOD_STATE :  // battery is above upper threshold
+        if (stateCounter == 0) {
+          gotoXY(0, 0);
+          LcdP("** Pi Power v0.1 **");
+          oled.clearToEOL();
+        }
+        gotoXY(0, 1); LcdP("Ok. Battery: "); showBattery();
+        gotoXY(0, 2); LcdP("High: %d.%dv Low: %d.%dv", int(myVars.highV), ((int(myVars.highV * 100)) % 100) / 10, int(myVars.lowV), ((int(myVars.lowV * 100)) % 100) / 10); oled.clearToEOL();
+        gotoXY(0, 3);
+        LcdP("On:  %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60); oled.clearToEOL();
+        gotoXY(0, 4);
+        LcdP("Off: %04ldd %02ld:%02ld:%02ld", downSecs / 86400, (downSecs / 3600) % 24 , (downSecs % 3600) / 60, downSecs % 60); oled.clearToEOL();
+        digitalWrite(WARNING, WARNINGOFF); // no warning
+        digitalWrite(POWER, POWERON);
+        if (vol < myVars.lowV)
+        {
+          if (++stateCounter > 2) {
+            stateCounter = 0;
+            state = TIMEOUT_STATE;
+          }
+        } else stateCounter = 1;
+        upSecs++;
+        break;
+
+      case TIMEOUT_STATE : // battery has dropped below upper threshold - ALWAYS timeout to OFF_STATE
+        gotoXY(0, 1); LcdP("Timing out. B: "); showBattery();
+        digitalWrite(WARNING, WARNINGON); // no warning
+        digitalWrite(POWER, POWERON);
+        if (++stateCounter > 10) {
+          stateCounter = 0;
+          state = OFF_STATE;
+        }
+        downSecs++;
+        break;
+
+      case OFF_STATE : // everything off
+        if (stateCounter == 0) oled.clear();
+        gotoXY(0, 1); LcdP("Off: "); showBattery();
+        digitalWrite(WARNING, WARNINGOFF); // no warning
+        digitalWrite(POWER, POWEROFF);
+        if (vol > myVars.highV)
+        {
+          if (++stateCounter > 5) {
+            stateCounter = 0;
+            state = GOOD_STATE;
+          }
+        } else stateCounter = 1; // avoid clearscreen
+        downSecs++;
+        break;
+    }
+  }
+}
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.