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
 #include <Wire.h>
 #include "SSD1306Ascii.h"
 #include "SSD1306AsciiWire.h"
+
 #include <EEPROM.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
 #define VER "v1.06"
 struct
 {
-  uint16_t check;
-  float highV;
-  float lowV;
-  uint8_t startTimeout;
-  uint8_t goodTimeout;
-  uint8_t warningTimeout;
-  uint8_t offTimeout;
-  int16_t batteryOffset;
+	uint16_t check;
+	float highV;
+	float lowV;
+	uint8_t startTimeout;
+	uint8_t goodTimeout;
+	uint8_t warningTimeout;
+	uint8_t offTimeout;
+	int16_t batteryOffset;
 } eeSave;
 
 // Device address for the SSD1306 display
 uint16_t longpressedUp = 0 ;
 uint16_t longpressedDown = 0;
 
-uint8_t triggerShutdown =0; 
+uint8_t triggerShutdown =0;
 
 uint32_t mySecs=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);
+	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;
 
 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);
+	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);
+	oled.setCursor(x, y);
 }
 
 //------------------------------------------------------------------------------
 void setup()
 {
-  EEPROM.get(0, eeSave);
-  analogReference(INTERNAL);
-  if (eeSave.check != 0x52d2)
-  {
-    eeSave.check = 0x52d2;
-
-    eeSave.highV = SWITCHON;
-    eeSave.lowV = SWITCHOFF;
-    eeSave.startTimeout = 5;
-    eeSave.goodTimeout = 5;
-    eeSave.warningTimeout = 20;
-    eeSave.offTimeout = 20;
-    eeSave.batteryOffset=400;
-
-    EEPROM.put(0, eeSave);
-  }
-
-  Wire.begin();
-  oled.begin(&Adafruit128x32, I2C_ADDRESS);
-  oled.set400kHz();
-  oled.setFont(Adafruit5x7);
-  uint32_t m = micros();
-
-#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 = eeSave.startTimeout;
+	EEPROM.get(0, eeSave);
+	analogReference(INTERNAL);
+	if (eeSave.check != 0x52d1)
+	{
+		eeSave.check = 0x52d1;
+
+		eeSave.highV = SWITCHON;
+		eeSave.lowV = SWITCHOFF;
+		eeSave.startTimeout = 5;
+		eeSave.goodTimeout = 5;
+		eeSave.warningTimeout = 25;
+		eeSave.offTimeout = 20;
+		eeSave.batteryOffset=400;
+
+		EEPROM.put(0, eeSave);
+	}
+
+	Wire.begin();
+	oled.begin(&Adafruit128x32, I2C_ADDRESS);
+	oled.set400kHz();
+	oled.setFont(Adafruit5x7);
+	uint32_t m = micros();
+
+	#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 = eeSave.startTimeout;
 }
 
 
 void showBattery(void)
 {
-  LcdP("%d.%02dv", int(vol), ((int(vol * 100)) % 100)); oled.clearToEOL();
+	LcdP("%d.%02dv", int(vol), ((int(vol * 100)) % 100)); oled.clearToEOL();
 }
 //------------------------------------------------------------------------------
 void loop()
 {
-  // main 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) // special case long press is reset...
-    {
-      if (uKeypressed == 0)
-      {
-        uKeypressed = 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;
-          case 7 : eeSave.batteryOffset++; break;
-        }
-        updated = 0;
-        updateTimeout = 100;
-      }
-      else
-      {
-       if (++longpressedUp==LONGPRESS_UP) triggerShutdown=1;
-      }
-    }
-    else
-      { uKeypressed = 0; longpressedUp = 0; }
-      
-    if (digitalRead(DOWN) == 0)
-    {
-      if (dKeypressed == 0)
-      {
-        dKeypressed = 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;
-          case 7 : eeSave.batteryOffset--; break;
-        }
-        updated = 0;
-        updateTimeout = 100;
-      }
-      else
-      {
-       if (++longpressedDown==LONGPRESS_DOWN) average=0; // temporarily drop the voltage and hence trigger warning
-      }
-    }
-    else
-      { 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)
-    {
-      case 0:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 1:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          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, 3);
-          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, 3);
-          LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 4:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 5:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 6:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 7:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          LcdP("SET: Battery %d",eeSave.batteryOffset-400); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-      case 8:  if (updated == 0)
-        {
-          gotoXY(0, 3);
-          LcdP("Version: %s",VER); oled.clearToEOL();
-          updated = 1;
-        }
-        break;
-        
-      case 9:  updated = 0; instate = 0; break;
-    }
-
-    if (updateTimeout)
-    {
-      if (--updateTimeout == 0)
-      {
-        instate = 0;
-        updated = 0;
-        EEPROM.put(0, eeSave);
-      }
-    }
-  }
-
-  // main 1 second loop here for determining where we are
-  if (mymillis <= millis())
-  {
-    mymillis += 1000; // every second we do this
-    mySecs++;
-    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; }
-
-    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("Wait %03d secs",stateCounter); oled.clearToEOL();
-        digitalWrite(WARNING, WARNINGOFF); // no warning
-        digitalWrite(POWER, POWEROFF);
-        if (vol > eeSave.highV)
-        {
-          if (stateCounter == 0) {
-            stateCounter = eeSave.goodTimeout;
-            state = GOOD_STATE;
-          }
-        } else
-        { 
-          if (stateCounter==0) {
-          stateCounter = eeSave.offTimeout;
-          state = OFF_STATE;
-          }
-        }
-        break;
-
-      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();
-        digitalWrite(WARNING, WARNINGOFF); // no warning
-        digitalWrite(POWER, POWERON);
-        if (vol < eeSave.lowV)
-        {
-          gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
-          if (stateCounter == 0) {
-            stateCounter = eeSave.warningTimeout;
-            state = TIMEOUT_STATE;
-          }
-        } else 
-            {
-              stateCounter = eeSave.goodTimeout-1;
-              if ((instate==0) && (vol >= eeSave.lowV))
-              {
-                gotoXY(0, 3);
-                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(); }
-              }
-            }
-        ++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("Wait %03d secs",stateCounter); oled.clearToEOL();
-        digitalWrite(WARNING, WARNINGON); // warning
-        digitalWrite(POWER, POWERON);
-        --stateCounter;
-        if (stateCounter == 0 ) {
-          stateCounter=eeSave.offTimeout;
-          state = OFF_STATE;
-        }
-        ++upSecs;
-        break;
-
-      case OFF_STATE : // everything off
-        if (stateCounter == eeSave.offTimeout) oled.clear();
-        --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("Wait %03d secs",stateCounter); oled.clearToEOL();
-          if (stateCounter == 0) {
-            stateCounter = eeSave.goodTimeout;
-            state = GOOD_STATE;
-          }
-        } else 
-          {
-            stateCounter = eeSave.offTimeout-1; // avoid clearscreen
-            gotoXY(0, 3); oled.clearToEOL();
-          }
-        ++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; }  
-    }
-  }
-}
+	// main 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) // special case long press is reset...
+		{
+			if (uKeypressed == 0)
+			{
+				uKeypressed = 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;
+					case 7 : eeSave.batteryOffset++; break;
+				}
+				updated = 0;
+				updateTimeout = 100;
+			}
+			else
+			{
+				if (++longpressedUp==LONGPRESS_UP) triggerShutdown=1;
+			}
+		}
+		else
+		{ uKeypressed = 0; longpressedUp = 0; }
+		
+		if (digitalRead(DOWN) == 0)
+		{
+			if (dKeypressed == 0)
+			{
+				dKeypressed = 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;
+					case 7 : eeSave.batteryOffset--; break;
+				}
+				updated = 0;
+				updateTimeout = 100;
+			}
+			else
+			{
+				if (++longpressedDown==LONGPRESS_DOWN) average=0; // temporarily drop the voltage and hence trigger warning
+			}
+		}
+		else
+		{ 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)
+		{
+			case 0:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 1:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				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, 3);
+				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, 3);
+				LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 4:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 5:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 6:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 7:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("SET: Battery %d",eeSave.batteryOffset-400); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			case 8:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("Version: %s",VER); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+			
+			case 9:  updated = 0; instate = 0; break;
+		}
+
+		if (updateTimeout)
+		{
+			if (--updateTimeout == 0)
+			{
+				instate = 0;
+				updated = 0;
+				EEPROM.put(0, eeSave);
+			}
+		}
+	}
+
+	// main 1 second loop here for determining where we are
+	if (mymillis <= millis())
+	{
+		mymillis += 1000; // every second we do this
+		mySecs++;
+		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; }
+
+		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("Wait %03d secs",stateCounter); oled.clearToEOL();
+			digitalWrite(WARNING, WARNINGON); // no warning
+			digitalWrite(POWER, POWEROFF);
+			if (vol > eeSave.highV)
+			{
+				if (stateCounter == 0) {
+					stateCounter = eeSave.goodTimeout;
+					state = GOOD_STATE;
+				}
+			} else
+			{
+				if (stateCounter==0) {
+					stateCounter = eeSave.offTimeout;
+					state = OFF_STATE;
+				}
+			}
+			break;
+
+			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();
+			digitalWrite(WARNING, WARNINGOFF); // no warning
+			digitalWrite(POWER, POWERON);
+			if (vol < eeSave.lowV)
+			{
+				gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
+				if (stateCounter == 0) {
+					stateCounter = eeSave.warningTimeout;
+					state = TIMEOUT_STATE;
+				}
+			} else
+			{
+				stateCounter = eeSave.goodTimeout-1;
+				if ((instate==0) && (vol >= eeSave.lowV))
+				{
+					gotoXY(0, 3);
+					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(); }
+				}
+			}
+			++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("Wait %03d secs",stateCounter); oled.clearToEOL();
+			digitalWrite(WARNING, WARNINGON); // warning
+			digitalWrite(POWER, POWERON);
+			--stateCounter;
+			if (stateCounter == 0 ) {
+				stateCounter=eeSave.offTimeout;
+				state = OFF_STATE;
+			}
+			++upSecs;
+			break;
+
+			case OFF_STATE : // everything off
+			if (stateCounter == eeSave.offTimeout) oled.clear();
+			--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("Wait %03d secs",stateCounter); oled.clearToEOL();
+				if (stateCounter == 0) {
+					stateCounter = eeSave.goodTimeout;
+					state = GOOD_STATE;
+				}
+			} else
+			{
+				stateCounter = eeSave.offTimeout-1; // avoid clearscreen
+				gotoXY(0, 3); oled.clearToEOL();
+			}
+			++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
+#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
 //
 //   temporarily - this triggers warning and shutdown - and eventual reboot
 //
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
-// now down to 4 lines - need to adjust for the 32ox display
+// Now set up for the 32-pixel displays - got everything down to 4 lines
 
 
-#define VER "v1.05"
+#define VER "v1.06"
 struct
 {
   uint16_t check;
 
 // Device address for the SSD1306 display
 #define I2C_ADDRESS 0x3C
+#define OPTIMIZE_I2C 1
 
 SSD1306AsciiWire oled;
 
   }
 
   Wire.begin();
-  oled.begin(&Adafruit128x64, I2C_ADDRESS);
+  oled.begin(&Adafruit128x32, I2C_ADDRESS);
   oled.set400kHz();
   oled.setFont(Adafruit5x7);
   uint32_t m = micros();
Updated by Peter Scargill

File uninterruptible.c Modified

  • 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
 //
 //   temporarily - this triggers warning and shutdown - and eventual reboot
 //
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
+// now down to 4 lines - need to adjust for the 32ox display
 
 
-#define VER "v1.04"
+#define VER "v1.05"
 struct
 {
   uint16_t check;
   uint8_t goodTimeout;
   uint8_t warningTimeout;
   uint8_t offTimeout;
-  uint8_t batteryPercent;
+  int16_t batteryOffset;
 } eeSave;
 
 // Device address for the SSD1306 display
 
 uint8_t triggerShutdown =0; 
 
+uint32_t mySecs=0;
+
 void LcdP(char *fmt, ...)
 {
   char buf[128]; // resulting string limited to 128 chars
 {
   EEPROM.get(0, eeSave);
   analogReference(INTERNAL);
-  if (eeSave.check != 0x52d1)
+  if (eeSave.check != 0x52d2)
   {
-    eeSave.check = 0x52d1;
+    eeSave.check = 0x52d2;
 
     eeSave.highV = SWITCHON;
     eeSave.lowV = SWITCHOFF;
     eeSave.goodTimeout = 5;
     eeSave.warningTimeout = 20;
     eeSave.offTimeout = 20;
-    eeSave.batteryPercent=100;
+    eeSave.batteryOffset=400;
 
     EEPROM.put(0, eeSave);
   }
           case 4 : eeSave.goodTimeout++; break;
           case 5 : eeSave.warningTimeout++; break;
           case 6 : eeSave.offTimeout++; break;
-          case 7 : eeSave.batteryPercent++; break;
+          case 7 : eeSave.batteryOffset++; break;
         }
         updated = 0;
         updateTimeout = 100;
           case 4 : eeSave.goodTimeout--; break;
           case 5 : eeSave.warningTimeout--; break;
           case 6 : eeSave.offTimeout--; break;
-          case 7 : eeSave.batteryPercent--; break;
+          case 7 : eeSave.batteryOffset--; break;
         }
         updated = 0;
         updateTimeout = 100;
     {
       case 0:  if (updated == 0)
         {
-          gotoXY(0, 5);
+          gotoXY(0, 3);
           oled.clearToEOL();
           updated = 1;
         }
         break;
       case 1:  if (updated == 0)
         {
-          gotoXY(0, 5);
+          gotoXY(0, 3);
           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);
+          gotoXY(0, 3);
           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);
+          gotoXY(0, 3);
           LcdP("SET: Start %d secs",eeSave.startTimeout); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 4:  if (updated == 0)
         {
-          gotoXY(0, 5);
+          gotoXY(0, 3);
           LcdP("SET: Good %d secs",eeSave.goodTimeout); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 5:  if (updated == 0)
         {
-          gotoXY(0, 5);
+          gotoXY(0, 3);
           LcdP("SET: Warn %d secs",eeSave.warningTimeout); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 6:  if (updated == 0)
         {
-          gotoXY(0, 5);
+          gotoXY(0, 3);
           LcdP("SET: Off %d secs",eeSave.offTimeout); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 7:  if (updated == 0)
         {
-          gotoXY(0, 5);
-          LcdP("SET: Battery %d%%",eeSave.batteryPercent); oled.clearToEOL();
+          gotoXY(0, 3);
+          LcdP("SET: Battery %d",eeSave.batteryOffset-400); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 8:  if (updated == 0)
         {
-          gotoXY(0, 5);
-          LcdP(VER); oled.clearToEOL();
+          gotoXY(0, 3);
+          LcdP("Version: %s",VER); oled.clearToEOL();
           updated = 1;
         }
         break;
   if (mymillis <= millis())
   {
     mymillis += 1000; // every second we do this
-
+    mySecs++;
     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.batteryPercent/100.0; //10k/2k2
+    vol = (((float)average) * 5.0 / 824.0) * (float)eeSave.batteryOffset/400.0; //10k/2k2
 
     if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
 
         if (vol < eeSave.lowV)
         {
           gotoXY(0, 3); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
-          gotoXY(0,4); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.warningTimeout;
             state = TIMEOUT_STATE;
         } 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();
+              if ((instate==0) && (vol >= eeSave.lowV))
+              {
+                gotoXY(0, 3);
+                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(); }
+              }
             }
         ++upSecs;
         break;
Updated by Peter Scargill

File uninterruptible.c Modified

  • Ignore whitespace
  • Hide word diff
 // - 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 
 //   temporarily - this triggers warning and shutdown - and eventual reboot
+//
+// Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
+
 
-#define VER "v1.03"
+#define VER "v1.04"
 struct
 {
   uint16_t check;
   uint8_t goodTimeout;
   uint8_t warningTimeout;
   uint8_t offTimeout;
+  uint8_t batteryPercent;
 } eeSave;
 
 // Device address for the SSD1306 display
     eeSave.goodTimeout = 5;
     eeSave.warningTimeout = 20;
     eeSave.offTimeout = 20;
+    eeSave.batteryPercent=100;
 
     EEPROM.put(0, eeSave);
   }
           case 4 : eeSave.goodTimeout++; break;
           case 5 : eeSave.warningTimeout++; break;
           case 6 : eeSave.offTimeout++; break;
+          case 7 : eeSave.batteryPercent++; break;
         }
         updated = 0;
         updateTimeout = 100;
           case 4 : eeSave.goodTimeout--; break;
           case 5 : eeSave.warningTimeout--; break;
           case 6 : eeSave.offTimeout--; break;
+          case 7 : eeSave.batteryPercent--; break;
         }
         updated = 0;
         updateTimeout = 100;
       case 7:  if (updated == 0)
         {
           gotoXY(0, 5);
+          LcdP("SET: Battery %d%%",eeSave.batteryPercent); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 8:  if (updated == 0)
+        {
+          gotoXY(0, 5);
           LcdP(VER); oled.clearToEOL();
           updated = 1;
         }
         break;
-      case 8:  updated = 0; instate = 0; break;
+        
+      case 9:  updated = 0; instate = 0; break;
     }
 
     if (updateTimeout)
     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
+
+    
+    vol = (((float)average) * 5.0 / 824.0) * (float)eeSave.batteryPercent/100.0; //10k/2k2
 
     if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
 
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
-//
+// 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 
+//   temporarily - this triggers warning and shutdown - and eventual reboot
 
 #define VER "v1.03"
 struct
  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.