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
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
 // Now set up for the 32-pixel displays - got everything down to 4 lines
 
-#define VER "v1.10"
+#define VER "v1.11"
 struct
 {
   uint16_t check;
       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();
+          LcdP("SET: HiV=%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();
+          LcdP("SET: LoV=%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();
+          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();
+          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();
+          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();
+          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();
+          LcdP("SET: Boffs=%d", eeSave.batteryOffset - 400); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 8: if (updated == 0)
         {
           gotoXY(0, 3);
-          LcdP("Warning tone: %d", eeSave.tone); oled.clearToEOL();
+          LcdP("Warning tone=%dHz", eeSave.tone); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 9: if (updated == 0)
         {
           gotoXY(0, 3);
-          LcdP("Serial monitor: %d", eeSave.smon); oled.clearToEOL();
+          LcdP("Serial mon On/Off: %d", eeSave.smon); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 10: if (updated == 0)
         {
           gotoXY(0, 3);
-          LcdP("Watchdog: %d mins", eeSave.watchdog); oled.clearToEOL();
+          LcdP("Watchdog=%d mins", eeSave.watchdog); oled.clearToEOL();
           updated = 1;
         }
         break;
       case 11:  if (updated == 0)
         {
           gotoXY(0, 3);
-          LcdP("Version: %s", VER); oled.clearToEOL();
+          LcdP("Version=%s", VER); oled.clearToEOL();
           updated = 1;
         }
         break;
         secondsCounter = 0;
         if (stateCounter == eeSave.startTimeout) {
           gotoXY(0, 0);
-          LcdP("Starting up");
+          LcdP("Starting up..");
           oled.clearToEOL();
         }
         --stateCounter;
-        gotoXY(0, 2); LcdP("Battery: "); showBattery();
-        gotoXY(0, 3); LcdP("Wait %03d secs", stateCounter); oled.clearToEOL();
+        gotoXY(0, 1); LcdP("Bat="); showBattery();
+        gotoXY(0, 2); LcdP("Wait=%03d secs", stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGON); // no warning
         digitalWrite(POWER, POWEROFF);
         if (vol > eeSave.highV)
           stateCounter = eeSave.warningTimeout;
           state = TIMEOUT_STATE;
         }
-        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, 1); if (watchdogTimer) LcdP("WDT: %d:%02d ",watchdogTimer,59-secondsCounter); 
+                      LcdP("Bat: "); showBattery();
+        gotoXY(0, 2); LcdP("HiV=%d.%dv LoV=%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();
+          gotoXY(0, 3); LcdP("Wait=%03d secs", stateCounter); oled.clearToEOL();
           if (stateCounter == 0) {
             stateCounter = eeSave.warningTimeout;
             state = TIMEOUT_STATE;
           {
             gotoXY(0, 3);
             if (mySecs & 2) {
-              LcdP("On:  %04ldd %02ld:%02ld:%02ld", upSecs / 86400, (upSecs / 3600) % 24, (upSecs % 3600) / 60, upSecs % 60);
+              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);
+              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
         secondsCounter = 0;
         if (stateCounter == eeSave.warningTimeout) oled.clear();
-        gotoXY(0, 0); LcdP("Timing out. B: "); showBattery();
-        gotoXY(0, 1); LcdP("Wait %03d secs", stateCounter); oled.clearToEOL();
+        gotoXY(0, 0); LcdP("Timoutout. Bat="); showBattery();
+        gotoXY(0, 1); LcdP("Wait=%03d secs", stateCounter); oled.clearToEOL();
         digitalWrite(WARNING, WARNINGON); // warning
         digitalWrite(POWER, POWERON);
         if (eeSave.tone) tone(TONE, eeSave.tone, 100);
         --stateCounter;
         if (vol <= eeSave.highV)  {
           gotoXY(0, 0);
-          LcdP("Stdby. V=");
+          LcdP("Stdby. Bat=");
           showBattery();
         }
         digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
         digitalWrite(POWER, POWEROFF);
         if (vol > eeSave.highV)
         {
-          gotoXY(0, 0); LcdP("Warming up. V=");  showBattery();
-          gotoXY(0, 1); LcdP("Wait %03d secs", stateCounter); oled.clearToEOL();
+          gotoXY(0, 0); LcdP("Warming up. Bat=");  showBattery();
+          gotoXY(0, 1); 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
 //     temporarily - this triggers warning and shutdown - and eventual reboot
 // - Added optional, programmable warning beeper (Piezo)
 // - Added serial JSON (optional) monitor
+// - Watchdog timeout from 1 minute through 255 minutes - change of state pin WATCHDOG
 //  In normal operation shows total on time and total off time
 //
 // Use 328-based board similar regulator (pref low loss) and no USB chip. If possible
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
 // Now set up for the 32-pixel displays - got everything down to 4 lines
 
-#define VER "v1.08"
+#define VER "v1.10"
 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 tone;
-	uint8_t smon;
+  uint16_t check;
+  float highV;
+  float lowV;
+  uint8_t startTimeout;
+  uint8_t goodTimeout;
+  uint8_t warningTimeout;
+  uint8_t offTimeout;
+  int16_t batteryOffset;
+  uint16_t tone;
+  uint8_t smon;
+  uint8_t watchdog;
 } eeSave;
 
 // Device address for the SSD1306 display
 #define POWER 12  // inverted - ON - power available for my cheap version
 #define WARNING 5 // inverted
 #define TONE 13   // optional piezo beeper on warning
+#define WATCHDOG 11  //  watchdog in
 
 #define POWERUP_STATE 0
 #define GOOD_STATE 1
 uint16_t longpressedUp = 0 ;
 uint16_t longpressedDown = 0;
 
-uint8_t triggerShutdown =0;
+uint8_t triggerShutdown = 0;
 
-uint32_t mySecs=0;
+uint32_t mySecs = 0;
+uint8_t watchdogTrigger = 0;
+uint8_t watchdogTimer = 0;
+uint8_t watchdogState = 0;
+uint8_t secondsCounter = 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 != 0x52d3)
-	{
-		eeSave.check = 0x52d3;
-
-		eeSave.highV = SWITCHON;
-		eeSave.lowV = SWITCHOFF;
-		eeSave.startTimeout = 5;
-		eeSave.goodTimeout = 5;
-		eeSave.warningTimeout = 25;
-		eeSave.offTimeout = 20;
-		eeSave.batteryOffset=400;
-		eeSave.tone=0;
-		eeSave.smon=0;
-		EEPROM.put(0, eeSave);
-	}
-
-	Wire.begin();
-	oled.begin(&Adafruit128x32, I2C_ADDRESS);
-	oled.set400kHz();
-	oled.setFont(Adafruit5x7);
-	uint32_t m = micros();
-
-	Serial.begin(115200); // serial JSON data out if set
-
-	#define POWERON LOW
-	#define POWEROFF HIGH
-	#define WARNINGON LOW
-	#define WARNINGOFF HIGH
-
-	digitalWrite(POWER, POWEROFF);   pinMode(POWER, OUTPUT);
-	digitalWrite(WARNING, WARNINGOFF); pinMode(WARNING, OUTPUT);
-	digitalWrite(TONE,0); pinMode(TONE,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 != 0x52d5)
+  {
+    eeSave.check = 0x52d5;
+
+    eeSave.highV = SWITCHON;
+    eeSave.lowV = SWITCHOFF;
+    eeSave.startTimeout = 5;
+    eeSave.goodTimeout = 5;
+    eeSave.warningTimeout = 25;
+    eeSave.offTimeout = 20;
+    eeSave.batteryOffset = 400;
+    eeSave.tone = 0;
+    eeSave.smon = 0;
+    eeSave.watchdog = 0;
+    EEPROM.put(0, eeSave);
+  }
+
+  Wire.begin();
+  oled.begin(&Adafruit128x32, I2C_ADDRESS);
+  oled.set400kHz();
+  oled.setFont(Adafruit5x7);
+  uint32_t m = micros();
+
+  Serial.begin(115200); // serial JSON data out if set
+
+#define POWERON LOW
+#define POWEROFF HIGH
+#define WARNINGON LOW
+#define WARNINGOFF HIGH
+
+  digitalWrite(POWER, POWEROFF);   pinMode(POWER, OUTPUT);
+  digitalWrite(WARNING, WARNINGOFF); pinMode(WARNING, OUTPUT);
+  digitalWrite(TONE, 0); pinMode(TONE, OUTPUT);
+  pinMode(SET, INPUT_PULLUP);
+  pinMode(UP, INPUT_PULLUP);
+  pinMode(DOWN, INPUT_PULLUP);
+  pinMode(WATCHDOG, INPUT_PULLUP);
+  average = analogRead(VOLTAGE); // we start off with read value;
+  oled.clear();
+  my100millis = millis() + 1000;
+  state = 0;
+  stateCounter = eeSave.startTimeout;
+  watchdogState = digitalRead(WATCHDOG);
 }
 
-// Float to ASCII conversion
-char *ftoa(char *a, double f, int precision)
-{
+/*
+  // Float to ASCII conversion
+  char *ftoa(char *a, double f, int precision)
+  {
 	long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};
-	
+
 	char *ret = a;
 	long heiltal = (long)f;
 	itoa(heiltal, a, 10);
 	while (*a != '\0') a++;
-	*a++ = '.';
+	a++ = '.';
 	long desimal = abs((long)((f - heiltal) * p[precision]));
 	itoa(desimal, a, 10);
 	return ret;
 
-}
-
+  }
+*/
 
 
 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;
-					case 8 : if (eeSave.tone<5000) eeSave.tone+=500; else eeSave.tone=0; break;
-					case 9 : eeSave.smon=1; 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;
-					case 8 : if (eeSave.tone>=500) eeSave.tone-=500; break;
-					case 9 : eeSave.smon=0; 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("Warning tone: %d",eeSave.tone); oled.clearToEOL();
-				updated = 1;
-			}
-			break;
-			case 9: if (updated == 0)
-			{
-				gotoXY(0, 3);
-				LcdP("Serial monitor: %d",eeSave.smon); oled.clearToEOL();
-				updated = 1;
-			}
-			break;
-					case 10:  if (updated == 0)
-			{
-				gotoXY(0, 3);
-				LcdP("Version: %s",VER); oled.clearToEOL();
-				updated = 1;
-			}
-			break;
-			case 11:  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; }
-
-		char stringStore[10];
-		char bString[40];
-		ftoa(stringStore,vol,2);
-		sprintf(bString,"{\"state\": %d,\"battery\": %s,\"upsecs\":%ld,\"downsecs\":%ld}",state,stringStore,upSecs,downSecs);
-		if (eeSave.smon) Serial.println(bString);
-
-		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
-			if (stateCounter == eeSave.warningTimeout) oled.clear();
-			gotoXY(0, 0); LcdP("Timing out. B: "); showBattery();
-			gotoXY(0, 1); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
-			digitalWrite(WARNING, WARNINGON); // warning
-			digitalWrite(POWER, POWERON);
-			if (eeSave.tone) tone(TONE,eeSave.tone,100);
-			--stateCounter;
-			if (stateCounter == 0 ) {
-				stateCounter=eeSave.offTimeout;
-				state = OFF_STATE;
-			}
-			++upSecs;
-			break;
-
-			case OFF_STATE : // everything off
-			if (stateCounter == eeSave.offTimeout) oled.clear();
-			--stateCounter;
-			if (vol <= eeSave.highV)  { gotoXY(0, 0); LcdP("Stdby. V=");  showBattery(); }
-			digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
-			digitalWrite(POWER, POWEROFF);
-			if (vol > eeSave.highV)
-			{
-				gotoXY(0, 0); LcdP("Warming up. V=");  showBattery();
-				gotoXY(0, 1); 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 ((eeSave.watchdog) && (watchdogTrigger == 0))
+    {
+      if (watchdogState != digitalRead(WATCHDOG)) {
+        watchdogTimer = eeSave.watchdog;
+        watchdogTrigger = 0;
+        watchdogState = digitalRead(WATCHDOG);
+      }
+    }
+    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;
+          case 8 : if (eeSave.tone < 5000) eeSave.tone += 500; else eeSave.tone = 0; break;
+          case 9 : eeSave.smon = 1; break;
+          case 10 : eeSave.watchdog++;
+            watchdogTimer = eeSave.watchdog; watchdogTrigger = 0; 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;
+          case 8 : if (eeSave.tone >= 500) eeSave.tone -= 500; break;
+          case 9 : eeSave.smon = 0; break;
+          case 10 : if (eeSave.watchdog) eeSave.watchdog--;
+            watchdogTimer = eeSave.watchdog; watchdogTrigger = 0; 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("Warning tone: %d", eeSave.tone); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 9: if (updated == 0)
+        {
+          gotoXY(0, 3);
+          LcdP("Serial monitor: %d", eeSave.smon); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 10: if (updated == 0)
+        {
+          gotoXY(0, 3);
+          LcdP("Watchdog: %d mins", eeSave.watchdog); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 11:  if (updated == 0)
+        {
+          gotoXY(0, 3);
+          LcdP("Version: %s", VER); oled.clearToEOL();
+          updated = 1;
+        }
+        break;
+      case 12:  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;
+    }
+
+    char bString[64];
+    sprintf(bString, "{\"state\": %d,\"battery\": %d.%02d,\"upsecs\":%ld,\"downsecs\":%ld}", state, int(vol), (int)(vol * 100) % 100, upSecs, downSecs);
+
+    if (eeSave.smon) Serial.println(bString);
+
+    switch (state)
+    {
+      case POWERUP_STATE : // powered up everything off
+        secondsCounter = 0;
+        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();
+          watchdogTimer = eeSave.watchdog;
+        }
+        --stateCounter;
+        if (++secondsCounter == 60)
+        {
+          secondsCounter = 0;
+          if (watchdogTimer) {
+            watchdogTimer--;  // only do this when battery is ok
+            if (watchdogTimer == 0) watchdogTrigger = 1;
+          }
+        }
+        if (watchdogTrigger) {
+          watchdogTrigger = 0;  // reboot
+          stateCounter = eeSave.warningTimeout;
+          state = TIMEOUT_STATE;
+        }
+        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
+        secondsCounter = 0;
+        if (stateCounter == eeSave.warningTimeout) oled.clear();
+        gotoXY(0, 0); LcdP("Timing out. B: "); showBattery();
+        gotoXY(0, 1); LcdP("Wait %03d secs", stateCounter); oled.clearToEOL();
+        digitalWrite(WARNING, WARNINGON); // warning
+        digitalWrite(POWER, POWERON);
+        if (eeSave.tone) tone(TONE, eeSave.tone, 100);
+        --stateCounter;
+        if (stateCounter == 0 ) {
+          stateCounter = eeSave.offTimeout;
+          state = OFF_STATE;
+        }
+        ++upSecs;
+        break;
+
+      case OFF_STATE : // everything off
+        secondsCounter = 0;
+        if (stateCounter == eeSave.offTimeout) oled.clear();
+        --stateCounter;
+        if (vol <= eeSave.highV)  {
+          gotoXY(0, 0);
+          LcdP("Stdby. V=");
+          showBattery();
+        }
+        digitalWrite(WARNING, WARNINGON); // LEAVE ON ie LOW
+        digitalWrite(POWER, POWEROFF);
+        if (vol > eeSave.highV)
+        {
+          gotoXY(0, 0); LcdP("Warming up. V=");  showBattery();
+          gotoXY(0, 1); 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
+        secondsCounter = 0;
+        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
 
 		if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
 
-		char stringStore[5];
+		char stringStore[10];
 		char bString[40];
 		ftoa(stringStore,vol,2);
 		sprintf(bString,"{\"state\": %d,\"battery\": %s,\"upsecs\":%ld,\"downsecs\":%ld}",state,stringStore,upSecs,downSecs);
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
-//   Added optional, programmable warning beeper (Piezo)
+// - Added optional, programmable warning beeper (Piezo)
+// - Added serial JSON (optional) monitor
 //  In normal operation shows total on time and total off time
 //
 // Use 328-based board similar regulator (pref low loss) and no USB chip. If possible
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
 // Now set up for the 32-pixel displays - got everything down to 4 lines
 
-#define VER "v1.07"
+#define VER "v1.08"
 struct
 {
 	uint16_t check;
 	uint8_t offTimeout;
 	int16_t batteryOffset;
 	uint16_t tone;
+	uint8_t smon;
 } eeSave;
 
 // Device address for the SSD1306 display
 {
 	EEPROM.get(0, eeSave);
 	analogReference(INTERNAL);
-	if (eeSave.check != 0x52d2)
+	if (eeSave.check != 0x52d3)
 	{
-		eeSave.check = 0x52d2;
+		eeSave.check = 0x52d3;
 
 		eeSave.highV = SWITCHON;
 		eeSave.lowV = SWITCHOFF;
 		eeSave.offTimeout = 20;
 		eeSave.batteryOffset=400;
 		eeSave.tone=0;
+		eeSave.smon=0;
 		EEPROM.put(0, eeSave);
 	}
 
 	oled.setFont(Adafruit5x7);
 	uint32_t m = micros();
 
+	Serial.begin(115200); // serial JSON data out if set
+
 	#define POWERON LOW
 	#define POWEROFF HIGH
 	#define WARNINGON LOW
 	stateCounter = eeSave.startTimeout;
 }
 
+// Float to ASCII conversion
+char *ftoa(char *a, double f, int precision)
+{
+	long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};
+	
+	char *ret = a;
+	long heiltal = (long)f;
+	itoa(heiltal, a, 10);
+	while (*a != '\0') a++;
+	*a++ = '.';
+	long desimal = abs((long)((f - heiltal) * p[precision]));
+	itoa(desimal, a, 10);
+	return ret;
+
+}
+
+
 
 void showBattery(void)
 {
 					case 6 : eeSave.offTimeout++; break;
 					case 7 : eeSave.batteryOffset++; break;
 					case 8 : if (eeSave.tone<5000) eeSave.tone+=500; else eeSave.tone=0; break;
+					case 9 : eeSave.smon=1; break;
 				}
 				updated = 0;
 				updateTimeout = 100;
 					case 6 : eeSave.offTimeout--; break;
 					case 7 : eeSave.batteryOffset--; break;
 					case 8 : if (eeSave.tone>=500) eeSave.tone-=500; break;
+					case 9 : eeSave.smon=0; break;
 				}
 				updated = 0;
 				updateTimeout = 100;
 				updated = 1;
 			}
 			break;
-			case 9:  if (updated == 0)
+			case 9: if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("Serial monitor: %d",eeSave.smon); oled.clearToEOL();
+				updated = 1;
+			}
+			break;
+					case 10:  if (updated == 0)
 			{
 				gotoXY(0, 3);
 				LcdP("Version: %s",VER); oled.clearToEOL();
 				updated = 1;
-			}	
-			break;					
-			case 10:  updated = 0; instate = 0; break;
+			}
+			break;
+			case 11:  updated = 0; instate = 0; break;
 		}
 
 		if (updateTimeout)
 
 		if (triggerShutdown) { triggerShutdown=0; state=SHUTDOWN_STATE; stateCounter=SHUTDOWN_PERIOD; }
 
+		char stringStore[5];
+		char bString[40];
+		ftoa(stringStore,vol,2);
+		sprintf(bString,"{\"state\": %d,\"battery\": %s,\"upsecs\":%ld,\"downsecs\":%ld}",state,stringStore,upSecs,downSecs);
+		if (eeSave.smon) Serial.println(bString);
+
 		switch (state)
 		{
 			case POWERUP_STATE : // powered up everything off
 			digitalWrite(POWER, POWEROFF);
 			if (vol > eeSave.highV)
 			{
-				gotoXY(0, 0); LcdP("Warming up. V=");  showBattery(); 
+				gotoXY(0, 0); LcdP("Warming up. V=");  showBattery();
 				gotoXY(0, 1); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
 				if (stateCounter == 0) {
 					stateCounter = eeSave.goodTimeout;
 		}
 	}
 }
-
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"
+// UPS (un-interruptible supply) software
 // 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
+// This Atmega 328 low cost solution powers up with an SSD1306 display, showing the
+// battery voltage (needs dividers - I used 2k2 ground to input, 10k input to battery)
+// with programmable offset to compensate for resistor variation.
+// Averages voltage over 8 seconds 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
+// Starts up - if voltage above upper threshold for a while turns
+// output bit on - that controls a P-channel MOSFET on output
+// of voltage booster - power the board from booster BEFORE the MOSFET
+// There is a warning bit- to warn micro of impending
+// shutdown. Shutdown when LOW.
 //
 // 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
+// also shuts down after this point EVEN if the power came back on after warning bit
+// went low. While off uses minimal display area to save current (OLED)
+// Then turns back on when battery is up to it. Big programmable hysteresis between
 // shutdown voltage and wakeup voltage
 //
 // 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
+//     temporarily - this triggers warning and shutdown - and eventual reboot
+//   Added optional, programmable warning beeper (Piezo)
+//  In normal operation shows total on time and total off time
+//
+// Use 328-based board similar regulator (pref low loss) and no USB chip. If possible
+// run OLED off 3v3.
 //
 // Blog has more - as does this video https://www.youtube.com/watch?v=44Lvdf7o4GQ
 // Now set up for the 32-pixel displays - got everything down to 4 lines
 
-
-#define VER "v1.06"
+#define VER "v1.07"
 struct
 {
 	uint16_t check;
 	uint8_t warningTimeout;
 	uint8_t offTimeout;
 	int16_t batteryOffset;
+	uint16_t tone;
 } eeSave;
 
 // Device address for the SSD1306 display
 
 #define POWER 12  // inverted - ON - power available for my cheap version
 #define WARNING 5 // inverted
+#define TONE 13   // optional piezo beeper on warning
 
 #define POWERUP_STATE 0
 #define GOOD_STATE 1
 {
 	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.warningTimeout = 25;
 		eeSave.offTimeout = 20;
 		eeSave.batteryOffset=400;
-
+		eeSave.tone=0;
 		EEPROM.put(0, eeSave);
 	}
 
 
 	digitalWrite(POWER, POWEROFF);   pinMode(POWER, OUTPUT);
 	digitalWrite(WARNING, WARNINGOFF); pinMode(WARNING, OUTPUT);
-
+	digitalWrite(TONE,0); pinMode(TONE,OUTPUT);
 	pinMode(SET, INPUT_PULLUP);
 	pinMode(UP, INPUT_PULLUP);
 	pinMode(DOWN, INPUT_PULLUP);
 					case 5 : eeSave.warningTimeout++; break;
 					case 6 : eeSave.offTimeout++; break;
 					case 7 : eeSave.batteryOffset++; break;
+					case 8 : if (eeSave.tone<5000) eeSave.tone+=500; else eeSave.tone=0; break;
 				}
 				updated = 0;
 				updateTimeout = 100;
 					case 5 : eeSave.warningTimeout--; break;
 					case 6 : eeSave.offTimeout--; break;
 					case 7 : eeSave.batteryOffset--; break;
+					case 8 : if (eeSave.tone>=500) eeSave.tone-=500; break;
 				}
 				updated = 0;
 				updateTimeout = 100;
 				updated = 1;
 			}
 			break;
-			case 8:  if (updated == 0)
+			case 8: if (updated == 0)
 			{
 				gotoXY(0, 3);
-				LcdP("Version: %s",VER); oled.clearToEOL();
+				LcdP("Warning tone: %d",eeSave.tone); oled.clearToEOL();
 				updated = 1;
 			}
 			break;
-			
-			case 9:  updated = 0; instate = 0; break;
+			case 9:  if (updated == 0)
+			{
+				gotoXY(0, 3);
+				LcdP("Version: %s",VER); oled.clearToEOL();
+				updated = 1;
+			}	
+			break;					
+			case 10:  updated = 0; instate = 0; break;
 		}
 
 		if (updateTimeout)
 			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();
+			if (stateCounter == eeSave.warningTimeout) oled.clear();
+			gotoXY(0, 0); LcdP("Timing out. B: "); showBattery();
+			gotoXY(0, 1); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
 			digitalWrite(WARNING, WARNINGON); // warning
 			digitalWrite(POWER, POWERON);
+			if (eeSave.tone) tone(TONE,eeSave.tone,100);
 			--stateCounter;
 			if (stateCounter == 0 ) {
 				stateCounter=eeSave.offTimeout;
 			case OFF_STATE : // everything off
 			if (stateCounter == eeSave.offTimeout) oled.clear();
 			--stateCounter;
-			gotoXY(0, 2); LcdP("Stdby. V=");  showBattery();
+			if (vol <= eeSave.highV)  { gotoXY(0, 0); 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();
+				gotoXY(0, 0); LcdP("Warming up. V=");  showBattery(); 
+				gotoXY(0, 1); LcdP("Wait %03d secs",stateCounter); oled.clearToEOL();
 				if (stateCounter == 0) {
 					stateCounter = eeSave.goodTimeout;
 					state = GOOD_STATE;
 		}
 	}
 }
+
  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.