Skip to content

Commit 2c15cf9

Browse files
mdkendallbjpetit
authored andcommitted
Add battery voltage monitoring feature
1 parent d00d517 commit 2c15cf9

File tree

9 files changed

+127
-76
lines changed

9 files changed

+127
-76
lines changed

tinyGS/src/ConfigManager/ConfigManager.cpp

Lines changed: 47 additions & 34 deletions
Large diffs are not rendered by default.

tinyGS/src/ConfigManager/ConfigManager.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ typedef struct
126126
uint8_t L_MISO;
127127
uint8_t L_MOSI;
128128
uint8_t L_SCK;
129-
float L_TCXO_V;
129+
float L_TCXO_V;
130130
uint8_t RX_EN;
131131
uint8_t TX_EN;
132-
String BOARD;
132+
uint8_t ADC_CTL; // GPIO pin to enable ADC reading
133+
uint8_t VBAT_AIN; // GPIO pin for VBAT monitoring
134+
float VBAT_SCALE; // potential divider between battery and GPIO pin
135+
String BOARD;
133136
} board_t;
134137

135138
const uint8_t UNUSED = -1;

tinyGS/src/ConfigManager/html.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1" };
7171
const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" , "21", "22"};
7272
#endif
7373

74-
const char IOTWEBCONF_DASHBOARD_STYLE_INNER[] PROGMEM = "table{margin:20px auto;}h3{text-align:center;}.card{height:12em;margin:10px;text-align:left;font-family:Arial;border:3px groove;border-radius:0.3rem;display:inline-block;padding:10px;min-width:260px;}td{padding:0 10px;}textarea{resize:vertical;width:100%;margin:0;height:318px;padding:5px;overflow:auto;}#c1{width:98%;padding:5px;}#t1{width:98%}.console{display:inline-block;text-align:center;margin:10px 0;width:98%;max-width:1080px;}.G{color:green;}.R{color:red}";
74+
const char IOTWEBCONF_DASHBOARD_STYLE_INNER[] PROGMEM = "table{margin:20px auto;}h3{text-align:center;}.card{height:13em;margin:10px;text-align:left;font-family:Arial;border:3px groove;border-radius:0.3rem;display:inline-block;padding:10px;min-width:260px;}td{padding:0 10px;}textarea{resize:vertical;width:100%;margin:0;height:318px;padding:5px;overflow:auto;}#c1{width:98%;padding:5px;}#t1{width:98%}.console{display:inline-block;text-align:center;margin:10px 0;width:98%;max-width:1080px;}.G{color:green;}.R{color:red}";
7575
const char IOTWEBCONF_DASHBOARD_BODY_INNER[] PROGMEM = "<div style='text-align:center;min-width:260px;'>\n";
7676
const char IOTWEBCONF_CONSOLE_BODY_INNER[] PROGMEM = "<br /><div class='console'><textarea readonly='' id='t1' wrap='off' name='t1'></textarea><form method='get' onsubmit='return f(1);'><input id='c1' placeholder='Enter command' autofocus='' name='c1'><br></form></div>\n";
7777
const char IOTWEBCONF_CONSOLE_SCRIPT[] PROGMEM = "var x=null,lt,to,tp,pc='';var sn=0,id=0;function f(p){var c,o='',t;clearTimeout(lt);t = document.getElementById('t1');if (p==1) {c =document.getElementById('c1');o='&c1='+encodeURIComponent(c.value);c.value='';t.scrollTop=99999;sn=t.scrollTop;}if (t.scrollTop >= sn){if (x!=null){x.abort();}x=new XMLHttpRequest();x.onreadystatechange=function() {if(x.readyState==4&&x.status==200){var z,d;var a=x.responseText;console.log(a);id=a.substr(0,a.indexOf('\\n'));z=a.substr(a.indexOf('\\n')+1);if(z.length>0){t.value+=z;}t.scrollTop=99999;sn=t.scrollTop;}};x.open('GET','cs?c2='+id+o,true);x.send();}lt=setTimeout(f,2345);return false;}window.addEventListener('load', f);";
@@ -82,5 +82,5 @@ const char ADVANCED_CONFIG_SCRIPT[] PROGMEM =
8282
"function tableDoneHandler(btn){var tbd=document.getElementById('current-table'); var ds=tableDictString(tbd); current_ctrl.value=ds; document.getElementById('dt-' + current_id).remove(); current_ctrl=null; current_id=null; }"
8383
"function editElementDict(ed){if (current_ctrl===null){var ph=ed.getAttribute('placeholder'); var dstring = ed.value!='' ? ed.value : ph; if(dstring !== ''){ current_id=ed.id; var dict = JSON.parse(dstring); var tblhtml = '<div id=""dt-' + current_id + '"">' + dictTable(dict) + '<input type=""button"" value=""Done ' + current_id + '"" onclick=""tableDoneHandler()"" style=""height:35px;width:100px;background-color:lightblue;""></div>'; ed.insertAdjacentHTML('afterend', tblhtml); current_ctrl=ed; } } }"
8484
"var current_id, current_ctrl=null; window.addEventListener('load', function() {setup_click('board_template'); setup_click('modem_startup');});";
85-
const char IOTWEBCONF_WORLDMAP_SCRIPT[] PROGMEM ="var wmx=null,wmt;function wmf(p){var sp,mc,gs,lp;clearTimeout(wmt);wmx=new XMLHttpRequest();wmx.onreadystatechange=function() {if(wmx.readyState==4&&x.status==200){var wma=wmx.responseText;var wmp = wma.split(',');sp=document.getElementById('wmsatpos');sp.setAttribute('cx', wmp[0]);sp.setAttribute('cy', wmp[1]);mc=document.getElementById('modemconfig');for(let r=0;r<6;r++){mc.rows[r].cells[1].innerHTML=wmp[r+2]};if(wmp[2]=='LoRa'){mc.rows[3].cells[0].innerHTML='Spreading Factor ';mc.rows[4].cells[0].innerHTML='Coding Rate ';}else{mc.rows[3].cells[0].innerHTML='Bitrate ';mc.rows[4].cells[0].innerHTML='Frequency dev ';};gs=document.getElementById('gsstatus');for(let r=0;r<6;r++){gs.rows[r].cells[1].innerHTML=wmp[r+8];};sd=document.getElementById('satdata');for(let r=0;r<6;r++){sd.rows[r].cells[1].innerHTML=wmp[r+14];};lp=document.getElementById('lastpacket');for(let r=0;r<4;r++){lp.rows[r].cells[1].innerHTML=wmp[r+20];};lp.rows[4].cells[0].innerHTML=wmp[24];}};wmx.open('GET','wm',true);wmx.send();wmt=setTimeout(wmf,5000);return false;}window.addEventListener('load', wmf);";
86-
const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}";
85+
const char IOTWEBCONF_WORLDMAP_SCRIPT[] PROGMEM ="var wmx=null,wmt;function wmf(p){var sp,mc,gs,lp;clearTimeout(wmt);wmx=new XMLHttpRequest();wmx.onreadystatechange=function() {if(wmx.readyState==4&&x.status==200){var wma=wmx.responseText;var wmp = wma.split(',');sp=document.getElementById('wmsatpos');sp.setAttribute('cx', wmp[0]);sp.setAttribute('cy', wmp[1]);mc=document.getElementById('modemconfig');for(let r=0;r<6;r++){mc.rows[r].cells[1].innerHTML=wmp[r+2]};if(wmp[2]=='LoRa'){mc.rows[3].cells[0].innerHTML='Spreading Factor ';mc.rows[4].cells[0].innerHTML='Coding Rate ';}else{mc.rows[3].cells[0].innerHTML='Bitrate ';mc.rows[4].cells[0].innerHTML='Frequency dev ';};gs=document.getElementById('gsstatus');for(let r=0;r<7;r++){gs.rows[r].cells[1].innerHTML=wmp[r+8];};sd=document.getElementById('satdata');for(let r=0;r<6;r++){sd.rows[r].cells[1].innerHTML=wmp[r+15];};lp=document.getElementById('lastpacket');for(let r=0;r<4;r++){lp.rows[r].cells[1].innerHTML=wmp[r+21];};lp.rows[4].cells[0].innerHTML=wmp[25];}};wmx.open('GET','wm',true);wmx.send();wmt=setTimeout(wmf,5000);return false;}window.addEventListener('load', wmf);";
86+
const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}";

tinyGS/src/Display/Display.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,11 @@ void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int1
235235
display->drawString(128 + x, 23 + y, String(status.modeminfo.freqDev)+ "/" + String(status.modeminfo.bw)+ "kHz");
236236
display->drawString(128 + x, 34 + y, String(status.modeminfo.bitrate)+ "kbps");
237237
}
238+
if (status.vbat != 0.0)
239+
{
240+
display->setTextAlignment(TEXT_ALIGN_LEFT);
241+
display->drawString(x, 45 + y, "Bat: " + String(status.vbat) + "V");
242+
}
238243
}
239244

240245
void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y)

tinyGS/src/Mqtt/MQTT_Client.cpp

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void MQTT_Client::loop()
9898
else
9999
{
100100
StaticJsonDocument<128> doc;
101-
doc["Vbat"] = voltage();
101+
doc["Vbat"] = status.vbat;
102102
doc["Mem"] = ESP.getFreeHeap();
103103
doc["RSSI"] =WiFi.RSSI();
104104
doc["radio"]= status.radio_error;
@@ -208,7 +208,7 @@ void MQTT_Client::sendWelcome()
208208
doc["board"] = configManager.getBoard();
209209
doc["mac"] = clientId;
210210
doc["seconds"] = millis()/1000;
211-
doc["Vbat"] = voltage();
211+
doc["Vbat"] = status.vbat;
212212
doc["chip"] = ESP.getChipModel();
213213
doc["slot"] = esp_ota_get_running_partition ()->label;
214214
doc["pSize"] = esp_ota_get_running_partition ()->size;
@@ -944,37 +944,3 @@ void MQTT_Client::begin()
944944
setServer(configManager.getMqttServer(), configManager.getMqttPort());
945945
setCallback(manageMQTTDataCallback);
946946
}
947-
948-
949-
950-
int MQTT_Client::voltage() {
951-
int medianVoltage;
952-
int length = 21;
953-
int voltages[22];
954-
955-
for (int i = 0; i < 22; i++)
956-
{
957-
voltages[i] = analogRead(36);
958-
}
959-
960-
// BubbleSortAsc from https://www.luisllamas.es/arduino-bubble-sort/
961-
int i, j, flag = 1;
962-
int temp;
963-
for (i = 1; (i <= length) && flag; i++)
964-
{
965-
flag = 0;
966-
for (j = 0; j < (length - 1); j++)
967-
{
968-
if (voltages[j + 1] < voltages[j])
969-
{
970-
temp = voltages[j];
971-
voltages[j] = voltages[j + 1];
972-
voltages[j + 1] = temp;
973-
flag = 1;
974-
}
975-
}
976-
}
977-
medianVoltage = voltages[10];
978-
return medianVoltage;
979-
}
980-

tinyGS/src/Power/Battery.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "Battery.h"
2+
#include "../ConfigManager/ConfigManager.h"
3+
#include "../Logger/Logger.h"
4+
5+
void initBatteryMonitoring(void)
6+
{
7+
board_t board;
8+
if (ConfigManager::getInstance().getBoardConfig(board)) {
9+
Log::console (PSTR ("Setting up battery monitoring pin"));
10+
if (board.ADC_CTL != UNUSED)
11+
pinMode(board.ADC_CTL, OUTPUT); // Enable pin for ADC reading
12+
if (board.VBAT_AIN != UNUSED) {
13+
pinMode(board.VBAT_AIN, INPUT); // VBAT monitoring pin
14+
adcAttachPin(board.VBAT_AIN);
15+
analogReadResolution(12);
16+
analogSetAttenuation(ADC_11db);
17+
}
18+
}
19+
}
20+
21+
void checkBattery(void)
22+
{
23+
static unsigned long lastReadTime = 0;
24+
board_t board;
25+
26+
// Throttle the battery check interval
27+
if (millis() - lastReadTime > BATTERY_CHECK_INTERVAL) {
28+
lastReadTime = millis();
29+
if (ConfigManager::getInstance().getBoardConfig(board)) {
30+
if (board.VBAT_AIN != UNUSED && board.VBAT_SCALE != UNUSED) {
31+
digitalWrite(board.ADC_CTL, HIGH); // Enable ADC reading
32+
delay(100); // Allow voltage to stabilize
33+
uint16_t temp = analogRead(board.VBAT_AIN);
34+
digitalWrite(board.ADC_CTL, LOW); // Disable ADC reading to save power
35+
if (status.vbat == 0.0) {
36+
status.vbat = board.VBAT_SCALE * temp;
37+
} else {
38+
status.vbat = (status.vbat + (board.VBAT_SCALE * temp))/2;
39+
}
40+
}
41+
}
42+
}
43+
}

tinyGS/src/Power/Battery.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef BATTERY_H
2+
#define BATTERY_H
3+
#include "Arduino.h"
4+
#include <Wire.h>
5+
#include "../Status.h"
6+
#include "../ConfigManager/ConfigManager.h"
7+
8+
#define BATTERY_CHECK_INTERVAL 10000
9+
10+
extern Status status;
11+
12+
void initBatteryMonitoring(void);
13+
void checkBattery(void);
14+
15+
#endif

tinyGS/src/Status.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ struct Status {
100100
bool mqtt_connected = false;
101101
bool radio_ready = false;
102102
int16_t radio_error = 0;
103+
float vbat = 0.0;
103104
PacketInfo lastPacketInfo;
104105
ModemInfo modeminfo;
105106
ModemInfo modeminfolastpckt;

tinyGS/tinyGS.ino

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include "time.h"
8181
#include "src/Mqtt/MQTT_credentials.h"
8282
#include "src/Improv/tinygs_improv.h"
83+
#include "src/Power/Battery.h"
8384

8485

8586
#if RADIOLIB_VERSION_MAJOR != (0x07) || RADIOLIB_VERSION_MINOR != (0x04) || RADIOLIB_VERSION_PATCH != (0x00) || RADIOLIB_VERSION_EXTRA != (0x00)
@@ -162,8 +163,10 @@ void setup()
162163
// make sure to call doLoop at least once before starting to use the configManager
163164
configManager.doLoop();
164165
board_t board;
165-
if(configManager.getBoardConfig(board))
166+
if(configManager.getBoardConfig(board)) {
166167
pinMode (board.PROG__BUTTON, INPUT_PULLUP);
168+
initBatteryMonitoring();
169+
}
167170
displayInit();
168171
displayShowInitialCredits();
169172
configManager.delay(1000);
@@ -317,6 +320,8 @@ void loop() {
317320
return;
318321
}
319322

323+
checkBattery();
324+
320325
// connected
321326

322327
mqtt.loop();

0 commit comments

Comments
 (0)