The project was for big-button web page control of up to 4 relays, each of which is individually configurable for momentary or toggle operation, with over-riding emergency All ON or All OFF options if wished.
Additionally, an easy-to-read analog bar guage reads battery voltage on the ADC input and includes a low-threshold pre-set alarm facility.
The project was developed to monitor battery voltage of a camper vehicle and provide remote control of onboard lighting and heating via mobile phone or tablet without getting out of bed (but it would of course be just as suitable for household stay-in-bed use!).
Analog input for the battery voltmeter uses an appropriate resistor voltage divider (1K + 15K for 12v systems). If wishing to use an optional hardware toggle switch or momentary push-button it will require a pullup resistor (between 5K and 20K). The optional hardware button is currently configured to toggle relay2 with a short press, and toggle the low volts alarm on or off with a long (>1sec press). A heartbeat pulse can turn a warning LED and an optional audion alarm on ond off every second if wished. One or two general indicator LEDs have also been catered for if needed.
I had plans to add inside and outside temperatures etc, but the growing complexity was causing an uphill struggle against the quantum-weirdness barrier, as you can see here.
The pic above shows how the battery meter component changes from green for full battery to red for flat battery - you don't require any hardware to check this by the way, sufficient just to touch the analog input pin to pick up enough noise to show a full battery (which might also be a handy way of making a simple touch switch!).
Above is just a sample of the quantum weirdness effects.
Everything displayed above the centralised battery meter should be a centralised 2 wide grid of buttons etc, but each time the script is run the unpredictable quantum weirdness effects make the changing display unusable even though everything actually functions ok - ie: all the buttons work, the alarm can be triggered by low-volts and turned on and off, and the hearbeat led keeps ticking away tirelessly.
I should mention that I think the quantum weirdness has progressed into script corruption, because I've had occasions where RUNning the script causes just a blank screen and sometimes an esp reboot, and there has also been an odd time that RUNning the script has failed due to a reported error on some line or other, but an immediate re-RUN does not fail on error (it's very easy to be chasing ghosts in these situations). So please bear in mind that this poor old script has been bashed around a lot trying to push overloaded dents back into shape, so it won't win any beauty contests.
To get to this normal working user control screen you need to comment out the line 3 debug = "y" entry which causes the user screen to be bypassed to a manual battery voltage test screen.
This manual voltage test screen has a slider that can emulate a voltage on the analog input pin even with nothing connected, allowing the low voltage alarm threshhold to be tested, and the meter colours to be tailored to personal preferences if wished. This last point needs explaining for anyone who may wish to hack out the battery meter for their own uses...
If a 12v vehicle battery measures 12v it is actually faulty or half flat (50% discharged).
A good 100% fully charged battery should be at least 12.7v and can be over 14v while being charged.
A fully discharged battery should still be 10.5v, and anything less is likely to cause cell damage unless it's a special deep-cycle leisure battery that is intended to drop below 10.5 volts without incurring damage.
It should be noted that '12v' electronic items such as mains inverters and TV's are normally only intended to operate down to the standard vehicle 10.5v flat battery level, and must survive long perionds with the engine running and generating charging voltages that may exceed 15v.
Therefore if I'm cosily laying in bed watching TV in a camper, all I really need to know is when the full battery is getting flat enough to soon cause problems... which for my purposes is green for 12.7 or greater, dropping down to red at 10.5v.
So I have arranged for my battery meter to work up to 16v, and display green fully charged down to 13v, then drop down through warning yellow to danger red with low voltage warning alarm at 10.5 volts.
All these levels can be tested using the slider at this manual bypass screen. The next pic shows how things should look,
followed by a few samples of quantum weirdness.
The way I have been coping with the bug is by selecting wifi to connect automatically to the esp device and using Firefox browser.
The grey SAVE button - which turns blue when clicked - would normally return grey when the "Saved ok" confirmation window pops up, but if no confirmation window appears due to a failed SAVE causing reboot and disconnection, the button will remain blue even after browser reconnection unless the mouse cursor is moved on the screen... so I occasionaly wiggle the mouse after a reboot to know when the SAVE button becomes responsive again.
Once the SAVE button returns grey after esp reboot and reconnection, if it is clicked again it will probably now result in the "Saved OK" confirmation without rebooting. However, I prefer to immediately SAVE a second time, because the second 'good' SAVE seems less prone to the quantum weirdess problems.
Something to point out - even when powered off and back on, it is still possible that the interpreter reconnects but fails to become responsive again, in which case there is no way to progress other than disconnect wifi then re-connect wifi once more, after which the interpret should come to life again. If getting to that point, it's probably time to start suspecting interpreter corruption. If in any doubt, the best action is to reformat and reflash the device, which provides the most dependable set of circumstances for continuing - unfortunately there's probably not much point in continuing by then, because the situation is just likely to keep getting worse.
Anyway, hopefully there'll be something in the script which might be of use to someone.
'BANANA (nickname of my yellow ex-ambulance camper) - developed on V3 A69
memclear
debug = "y"
vlow = 10.5 'Optional low-voltage alarm value
alarm = "on"
vtest = 0
buttonpin = 0 'Uses gpio00 flashing button by default, change to suit (needs pullup resistor).
buttonoff = 1 'Default button OFF state
alarmpin = 15 'Uses gpio15 to flash a low voltage alarm indication
alarmoff = 0 'Default alarm led OFF state
if alarmoff = 1 then io(po,alarmpin,1) else io(po,alarmpin,0) 'initialise low voltage alarm to its off state
led1pin = 1 'Uses onboard gpio01 blue led by default, but be aware that using gpio01 mucks up the serial port
led1off = 1 'Default led pin off state (allows configuring led pin for active high or active low operation)
if led1off = 1 then io(po,led1pin,1) else io(po,led1pin,0) 'initialise led1 to its off state
led2pin = 4 'Led2 gpio04
led2off = 0 'Default led2 pin off state (allows configuring led pin for active high or active low operation)
if led2off = 1 then io(po,led2pin,1) else io(po,led2pin,0) 'initialise led2 to its off state
timer 1000, [PULSE]
pulsepin = 16 'Optional Pulse timer led
relay1pin = 2 'Set to the required Relay1 control pin
let r1off = 1 'Default relay OFF state - Allows for use of active high or active low relay
relay2pin = 12 'Set to the required Relay2 control pin
let r2off = 1 'Default relay OFF state - Allows for use of active high or active low relay
relay3pin = 13 'Set to the required Relay2 control pin
let r3off = 1 'Default relay OFF state - Allows for use of active high or active low relay
relay4pin = 14 'Set to the required Relay2 control pin
let r4off = 1 'Default relay OFF state - Allows for use of active high or active low relay
gosub [RELAY1OFF]
gosub [RELAY2OFF]
gosub [RELAY3OFF]
gosub [RELAY4OFF]
interrupt buttonpin, [PRESSED]
a2d = 0 'Analog input variable, read from centre of 15K + 1K resistor divider for 12v systems, results in 1v per 1K to 16v max at 16K
volts = 0 '1v max ADC pin across 1K resistor gives a2d value of 1023 representing 16v max in, therefore volts = a2d * (1024 / 16 = 64)
vconvert = 6.4 'Can be fine-tuned to improve conversion accuracy for resistor divider tolerance imbalance (59.5 worked best for me)
if debug = "y" then goto [BYPASS]
[HOME]
cls
html "<BR>"
'scss = "padding: 20px; align: center; text-align: center; margin:auto; width:61%;border-top:3px solid lightgrey; background: grey;"
'scss = scss & "-webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px;height:90;"
'scss = scss & "box-shadow: rgba(0,0,0,1) 0 3px 0;text-shadow: rgba(0,0,0,.4) 0 1px 0; color: black;font-size: 34px;"
html |<table style="text-align: center; margin:auto; width: 90%;" border="0"|
html | <tbody>|
html | <tr>|
html | <td style="text-align: center;">|
butcss = "padding: 20px; align: center; margin:auto; width:60%;border-top:3px solid lightgrey; background: darkblue;"
butcss = butcss & "-webkit-border-radius: 60px; -moz-border-radius: 60px; border-radius: 60px;"
butcss = butcss & "box-shadow: rgba(0,0,0,1) 0 3px 0;text-shadow: rgba(0,0,0,.4) 0 1px 0;color: darkgray;"
butcss = butcss & "font-size: 48px;font-family: arial, serif;text-decoration: none;vertical-align: middle;"
cssclass "button", butcss
button "Lights", [BUTTON1]
html | </td>|
html | <td style="text-align: center;">|
button "Heater", [BUTTON2]
cssid htmlid(), "background:darkred;"
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
html | <BR><BR> |
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;">|
button "Inverter", [BUTTON3]
cssid htmlid(), "background:teal;"
html | </td>|
html | <td style="text-align: center;">|
button "Spare", [BUTTON4]
cssid htmlid(), "background:darkgoldenrod;"
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
html | <BR><BR> |
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;">|
button "All OFF", [ALLOFF]
cssid htmlid(), "background:white;"
html | </td>|
html | <td style="text-align: center;">|
button "All ON", [ALLON]
cssid htmlid(), "background:grey;"
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
html | <BR><BR> |
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;">|
button "Alarm", [ALARMONOFF]
cssid htmlid(), "font-size: 42px; background:black;"
html | </td>|
html | <td style="text-align: center;">|
vcss = "padding: 10px; align: center; margin:auto;width:20%;border-top:3px solid lightgrey; background: cyan;"
vcss = vcss & "-webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px;"
vcss = vcss & "box-shadow: rgba(0,0,0,1) 0 3px 0;text-shadow: rgba(0,0,0,.4) 0 1px 0;color: darkblue;"
vcss = vcss & "font-size: 54px;font-family: arial, serif;text-decoration: none;vertical-align: middle;"
textbox vlow
cssid htmlid(), vcss & "background:black;color:red;"
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
html | <BR><BR> |
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
metercss = "-moz-linear-gradient(right, red 80%, yellow); background: linear-gradient(to right, red 80%, yellow);"
metercss = metercss & "margin:auto;width:60%;height:70px; border: 5px solid darkgrey;"
meter volts, 10, 13
cssid htmlid(), metercss
html | </td>|
html | </tr>|
html | <tr>|
html | <td style="text-align: center;" colspan="2" rowspan="1">|
html "<BR><BR>"
'textbox volts
'cssid htmlid(), vcss
html | </td>|
html | </tr>|
html | </tbody>|
html |</table>
'if debug = "y" then
'html "<BR><BR>"
'slider volts, 0, 16
'html "<BR><BR>"
'html "<BR><BR>"
'textbox a2d
'endif
wait
[BYPASS]
html "<BR><BR>"
vcss = "padding: 10px; align: center; margin:auto;width:14%;border-top:3px solid lightgrey; background: cyan;"
vcss = vcss & "-webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px;"
vcss = vcss & "box-shadow: rgba(0,0,0,1) 0 3px 0;text-shadow: rgba(0,0,0,.4) 0 1px 0;color: darkblue;"
vcss = vcss & "font-size: 54px;font-family: arial, serif;text-decoration: none;vertical-align: middle;"
textbox volts
cssid htmlid(), vcss
html "<BR><BR>"
metercss = "-moz-linear-gradient(right, red 80%, yellow); background: linear-gradient(to right, red 80%, yellow);"
metercss = metercss & "margin:auto;width:60%;height:90px; border: 5px solid darkgrey;"
meter volts, 0, 13
cssid htmlid(), metercss
html "<BR><BR>"
scss = "padding: 20px; align: center; text-align: center; margin:auto; width:61%;border-top:3px solid lightgrey; background: grey;"
scss = scss & "-webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px;height:90;"
scss = scss & "box-shadow: rgba(0,0,0,1) 0 3px 0;text-shadow: rgba(0,0,0,.4) 0 1px 0; color: black;font-size: 34px;"
slider vtest, 0, 32
'cssid htmlid(), metercss
cssid htmlid(), scss
wait
[PULSE]
pulsepin
io(po,pulsepin,0)
delay 30
io(po,pulsepin,1)
a2d = io(ai)
'a2d = 0
volts = round(a2d / vconvert) / 10 + (vtest/2)
'volts = round(volts)
'+ 10
'volts = volts + 10
if volts <= vlow and alarm = "on" then gosub [LOWBATTERY]
wait
[LOWBATTERY]
'Place to put low battery warning alert, ie: light an led or sound a buzzer
if io(laststat,alarmpin) = alarmoff then io(po,alarmpin,1) else io(po,alarmpin,0)
return
[PRESSED]
'Allows different actions for short and long button presses
if io(laststat,buttonpin) = 0 then start = millis() else stop = millis()
if stop > start then
if stop-start < 1000 then goto [SHORTPRESS] else goto [LONGPRESS]
endif
wait
[SHORTPRESS]
'Place to put users short-press button action
'eg: gosub [MOMENTARY1] or gosub [ALARMONOFF]
gosub [TOGGLE2]
wait
return
[LONGPRESS]
'Place to put users long-press button action
'eg: gosub [TOGGLE3]
goto [ALARMONOFF]
wait
return
[ALARMONOFF]
io(po,alarmpin,alarmoff)
if alarm = "on" then alarm = "off" else alarm = "on"
wait
[BUTTON1]
gosub [MOMENTARY1] 'change to gosub [TOGGLE1] if toggle action is preferred
delay 500
wait
[BUTTON2]
gosub [TOGGLE2] 'change to gosub [MOMENTARY2] if momentary action is preferred
delay 500
wait
[BUTTON3]
gosub [TOGGLE3]
delay 500
wait
[BUTTON4]
gosub [TOGGLE4]
delay 500
wait
[ALLOFF]
'html "ALL OFF pressed <BR>"
gosub [RELAY1OFF]
gosub [RELAY2OFF]
gosub [RELAY3OFF]
gosub [RELAY4OFF]
wait
[ALLON]
'html "ALL ON pressed <BR>"
gosub [RELAY1ON]
gosub [RELAY2ON]
gosub [RELAY3ON]
gosub [RELAY4ON]
wait
[RELAY1ON]
if io(laststat,relay1pin) <> r1off then return
if r1off = 0 then io(po,relay1pin,1) else io(po,relay1pin,0)
return
[RELAY1OFF]
if io(laststat,relay1pin) = r1off then return
if r1off = 0 then io(po,relay1pin,0) else io(po,relay1pin,1)
return
[MOMENTARY1]
gosub [RELAY1ON]
delay 400
gosub [RELAY1OFF]
return
[TOGGLE1]
if io(laststat,relay1pin) = 1 then io(po,relay1pin,0) else io(po,relay1pin,1)
'wait
return
[RELAY2ON]
if io(laststat,relay2pin) <> r2off then return
if r2off = 0 then io(po,relay2pin,1) else io(po,relay2pin,0)
return
[RELAY2OFF]
if io(laststat,relay2pin) = r2off then return
if r2off = 0 then io(po,relay2pin,0) else io(po,relay2pin,1)
return
[MOMENTARY2]
gosub [RELAY2ON]
delay 400
gosub [RELAY2OFF]
return
[TOGGLE2]
if io(laststat,relay2pin)= 1 then io(po,relay2pin,0) else io(po,relay2pin,1)
return
[RELAY3ON]
if io(laststat,relay3pin) <> r3off then return
if r3off = 0 then io(po,relay3pin,1) else io(po,relay3pin,0)
return
[RELAY3OFF]
if io(laststat,relay3pin) = r3off then return
if r3off = 0 then io(po,relay3pin,0) else io(po,relay3pin,1)
return
[MOMENTARY3]
gosub [RELAY3ON]
delay 400
gosub [RELAY3OFF]
return
[TOGGLE3]
if io(laststat,relay3pin) = 1 then io(po,relay3pin,0) else io(po,relay3pin,1)
return
[RELAY4ON]
if io(laststat,relay4pin) <> r4off then return
if r4off = 0 then io(po,relay4pin,1) else io(po,relay4pin,0)
return
[RELAY4OFF]
if io(laststat,relay4pin) = r4off then return
if r4off = 0 then io(po,relay4pin,0) else io(po,relay4pin,1)
return
[MOMENTARY4]
gosub [RELAY4ON]
delay 400
gosub [RELAY4OFF]
return
[TOGGLE4]
if io(laststat,relay4pin) = 1 then io(po,relay4pin,0) else io(po,relay4pin,1)
return