diff --git a/tinyGS/src/ConfigManager/ConfigManager.cpp b/tinyGS/src/ConfigManager/ConfigManager.cpp index abf799fa..c4e5cf75 100644 --- a/tinyGS/src/ConfigManager/ConfigManager.cpp +++ b/tinyGS/src/ConfigManager/ConfigManager.cpp @@ -23,6 +23,7 @@ #include "../Radio/Radio.h" #include "../Display/graphics.h" #include "../Mqtt/MQTT_credentials.h" +#include "../Power/Battery.h" #include "ArduinoJson.h" #if ARDUINOJSON_USE_LONG_LONG == 0 && !PLATFORMIO #error "Using Arduino IDE is not recommended, please follow this guide https://github.com/G4lile0/tinyGS/wiki/Arduino-IDE or edit /ArduinoJson/src/ArduinoJson/Configuration.hpp and amend to #define ARDUINOJSON_USE_LONG_LONG 1 around line 68" @@ -50,39 +51,39 @@ na SX1281 2.4–2.5Ghz 130 5.5 2000 0.476-202 ConfigManager::ConfigManager() : IotWebConf2(thingName, &dnsServer, &server, initialApPassword, configVersion), server(80), gsConfigHtmlFormatProvider(*this), boards{ - //OLED_add, OLED_SDA, OLED_SCL, OLED_RST, PROG_BUTTON, BOARD_LED, L_SX127X?, L_NSS, L_DI00, L_DI01, L_BUSSY, L_RST, L_MISO, L_MOSI, L_SCK, L_TCXO_V, RX_EN, TX_EN, BOARD + //OLED_add, OLED_SDA, OLED_SCL, OLED_RST, PROG_BUTTON, BOARD_LED, L_SX127X?, L_NSS, L_DI00, L_DI01, L_BUSSY, L_RST, L_MISO, L_MOSI, L_SCK, L_TCXO_V, RX_EN, TX_EN, ADC_CTL, BAT_AIN, VBAT_SCALE, BOARD #if CONFIG_IDF_TARGET_ESP32S3 - { 0x3c, 17, 18, 21, 0, 35, RADIO_SX1262, 8, UNUSED, 14, 13, 12, 11, 10, 9, 1.6f, UNUSED, UNUSED, "150–960Mhz - HELTEC LORA32 V3 SX1262" }, // SX1262 - { 0x3c, 17, 18, UNUSED, 0, 35, RADIO_SX1278, 8, 6, 14, UNUSED, 12, 11, 10, 9, 0.0f, UNUSED, UNUSED, "Custom ESP32-S3 433MHz SX1278" }, // SX1278 @g4lile0 - { 0x3c, 17, 18, UNUSED, 0, 3, RADIO_SX1262, 10, UNUSED, 1, 4, 5, 13, 11, 12, 1.6f, UNUSED, UNUSED, "433 Mhz TTGO T-Beam Sup SX1262 V1.0" }, // SX1268 @ Stephen - { 0x3c, 17, 18, UNUSED, 0, 37, RADIO_SX1280, 7, UNUSED, 9, UNUSED, 8, 3, 6, 5, 0.0f, 21, 10, "2.4Ghz LILYGO SX1280" }, // SX1280 @ K4KDR + { 0x3c, 17, 18, 21, 0, 35, RADIO_SX1262, 8, UNUSED, 14, 13, 12, 11, 10, 9, 1.6f, UNUSED, UNUSED, 37, 1, 5.1205f, "150–960Mhz - HELTEC LORA32 V3 SX1262" }, // SX1262 + { 0x3c, 17, 18, UNUSED, 0, 35, RADIO_SX1278, 8, 6, 14, UNUSED, 12, 11, 10, 9, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32-S3 433MHz SX1278" }, // SX1278 @g4lile0 + { 0x3c, 17, 18, UNUSED, 0, 3, RADIO_SX1262, 10, UNUSED, 1, 4, 5, 13, 11, 12, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433 Mhz TTGO T-Beam Sup SX1262 V1.0" }, // SX1268 @ Stephen + { 0x3c, 17, 18, UNUSED, 0, 37, RADIO_SX1280, 7, UNUSED, 9, UNUSED, 8, 3, 6, 5, 0.0f, 21, 10, UNUSED, UNUSED, UNUSED, "2.4Ghz LILYGO SX1280" }, // SX1280 @ K4KDR #elif CONFIG_IDF_TARGET_ESP32C3 - { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1262, 8, UNUSED, 3, 4, 5, 6, 7, 10, 1.6f, UNUSED, UNUSED, "433MHz HELTEC LORA32 HT-CT62 SX1262" }, // SX1262 @gargomoma - { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1278, 8, 4, UNUSED, UNUSED, 5, 6, 7, 10, 0.0f, UNUSED, UNUSED, "Custom ESP32-C3 433MHz SX1278" }, // SX1278 @gargomoma + { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1262, 8, UNUSED, 3, 4, 5, 6, 7, 10, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz HELTEC LORA32 HT-CT62 SX1262" }, // SX1262 @gargomoma + { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1278, 8, 4, UNUSED, UNUSED, 5, 6, 7, 10, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32-C3 433MHz SX1278" }, // SX1278 @gargomoma #else - { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V1" }, // SX1278 @4m1g0 - { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V1" }, // SX1276 - { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V2" }, // SX1278 @4m1g0 - { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V2" }, // SX1276 - { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1278, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433Mhz TTGO LoRa 32 v1" }, // SX1278 @g4lile0 - { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1276, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz TTGO LoRa 32 v1" }, // SX1276 - { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz TTGO LoRA 32 v2" }, // SX1278 @TCRobotics - { 0x3c, 21, 22, 16, 0, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz TTGO LoRA 32 v2" }, // SX1276 - { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1278, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz T-BEAM + OLED" }, // SX1278 - { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1276, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz T-BEAM + OLED" }, // SX1276 - { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 27, 26, 14, 19, 23, 18, 0.0f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x (Crystal)" }, // SX1268 @4m1g0, @lillefyr - { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1268, 18, UNUSED, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "TTGO LoRa 32 V2 Modified with module SX126x (crystal)" }, // SX1268 @TCRobotics - { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 2, 13, 26, 19, 23, 18, 1.6f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 2, 26, 13)" }, // SX1268 @sdey76 - { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 26, 12, 14, 19, 23, 18, 1.6f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 26, 14, 12)" }, // SX1268 @imants - { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz T-BEAM V1.0 + OLED" }, // SX1278 @fafu - { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1268, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, "433MHz FOSSA 1W Ground Station" }, // SX1268 @jgromes - { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1276, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, "868-915MHz FOSSA 1W Ground Station" }, //SX1276 @jgromes - { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1280, 5, 26, 34, 32, 14, 19, 27, 18, 0.0f, UNUSED, UNUSED, "2.4GHz ESP32 + SX1280" }, //SX1280 @g4lile0 - { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz T-BEAM V1.0 + OLED" }, // SX1276 @fafu - { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1278, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz LILYGO T3_V1.6.1" }, // SX1278 - { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1" }, // SX1276 - { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, UNUSED, 32, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1 TCXO" }, // SX1276 - { 0x3c, 21, 22, UNUSED, 38, 4, RADIO_SX1268, 18, 26, 33, 32, 23, 19, 27, 5, 1.6f, UNUSED, UNUSED, "433 Mhz T-Beam SX1268 V1.0" }, // SX1268 @ Antonio + { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V1" }, // SX1278 @4m1g0 + { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V1" }, // SX1276 + { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, 21, 37, 0.00283f, "433MHz HELTEC WiFi LoRA 32 V2" }, // SX1278 @4m1g0 + { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, 21, 37, 0.00283f, "863-928MHz HELTEC WiFi LoRA 32 V2" }, // SX1276 + { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1278, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433Mhz TTGO LoRa 32 v1" }, // SX1278 @g4lile0 + { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1276, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz TTGO LoRa 32 v1" }, // SX1276 + { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz TTGO LoRA 32 v2" }, // SX1278 @TCRobotics + { 0x3c, 21, 22, 16, 0, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz TTGO LoRA 32 v2" }, // SX1276 + { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1278, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz T-BEAM + OLED" }, // SX1278 + { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1276, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz T-BEAM + OLED" }, // SX1276 + { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 27, 26, 14, 19, 23, 18, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x (Crystal)" }, // SX1268 @4m1g0, @lillefyr + { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1268, 18, UNUSED, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "TTGO LoRa 32 V2 Modified with module SX126x (crystal)" }, // SX1268 @TCRobotics + { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 2, 13, 26, 19, 23, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 2, 26, 13)" }, // SX1268 @sdey76 + { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 26, 12, 14, 19, 23, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 26, 14, 12)" }, // SX1268 @imants + { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz T-BEAM V1.0 + OLED" }, // SX1278 @fafu + { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1268, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz FOSSA 1W Ground Station" }, // SX1268 @jgromes + { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1276, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz FOSSA 1W Ground Station" }, //SX1276 @jgromes + { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1280, 5, 26, 34, 32, 14, 19, 27, 18, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "2.4GHz ESP32 + SX1280" }, //SX1280 @g4lile0 + { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz T-BEAM V1.0 + OLED" }, // SX1276 @fafu + { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1278, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz LILYGO T3_V1.6.1" }, // SX1278 + { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1" }, // SX1276 + { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, UNUSED, 32, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1 TCXO" }, // SX1276 + { 0x3c, 21, 22, UNUSED, 38, 4, RADIO_SX1268, 18, 26, 33, 32, 23, 19, 27, 5, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433 Mhz T-Beam SX1268 V1.0" }, // SX1268 @ Antonio #endif @@ -260,7 +261,17 @@ void ConfigManager::handleDashboard() s += "MQTT Server " + String(status.mqtt_connected ? "CONNECTED" : "NOT CONNECTED") + ""; s += "WiFi RSSI " + String(WiFi.isConnected() ? "CONNECTED" : "NOT CONNECTED") + ""; s += "Radio " + String(Radio::getInstance().isReady() ? "READY" : "NOT READY") + ""; - s += "Noise floor " + String(status.modeminfo.currentRssi) + ""; + s += "Noise floor " + String(status.modeminfo.currentRssi) + ""; + if (status.vbat > 0) { + if(getBatteryPercentage() > 100.0f) { // Charging + s += "Power USB " + String(status.vbat) + "V"; + } else { + s += "Power Bat " + String(getBatteryPercentage(), 0) + "%" + String(status.vbat) + "V"; + } + } else { + // Empty if battery monitoring not enabled + s += ""; + } s += F(""); @@ -507,6 +518,16 @@ void ConfigManager::handleRefreshWorldmap() if (status.radio_ready) radio.currentRssi (); data_string += String(status.modeminfo.currentRssi) + ","; + if (status.vbat > 0) { + if(getBatteryPercentage() > 100.0f) { // Charging + data_string += "USB " + String(status.vbat) + "V,"; + } else { + data_string += "Bat " + String(getBatteryPercentage(), 0) + "% " + String(status.vbat) + "V,"; + } + } else { + // if battery monitoring not enabled + data_string += ","; + } // sat_info @@ -547,9 +568,8 @@ else { else { timeStr[0] = '\0'; } - data_string += String(timeStr) + "," ; + data_string += String(timeStr) + "," ; - // last packet received data (for lastpacket id table data) data_string += String(status.lastPacketInfo.time) + ","; data_string += String(status.lastPacketInfo.rssi) + ","; @@ -753,8 +773,8 @@ void ConfigManager::printConfig() Log::debug(PSTR("tz: %s\nOLED Bright: %u\nTX %s"), getTZ(), getOledBright(), getAllowTx() ? "Enable" : "Disable"); if (getBoardTemplate()[0] != '\0') Log::debug(PSTR("board_template: %s"),getBoardTemplate()); - else - Log::debug(PSTR("board: %u --> %s\n:"),getBoard(), boards[getBoard()].BOARD.c_str()); + else + Log::debug(PSTR("board_template: %s"), boardTemplateToJSON(boards[getBoard()]).c_str() ); } // void ConfigManager::setMqttServer(const char *server) @@ -1022,6 +1042,51 @@ bool ConfigManager::parseBoardTemplate(board_t &board) board.TX_EN = doc["TXEN"]; else board.TX_EN = UNUSED; + if (doc.containsKey("ADC_CTL")) + board.ADC_CTL = doc["ADC_CTL"]; + else + board.ADC_CTL = UNUSED; + if (doc.containsKey("VBAT")) + board.VBAT_AIN = doc["VBAT"]; + else + board.VBAT_AIN = UNUSED; + if (doc.containsKey("VBAT_SCALE")) + board.VBAT_SCALE = doc["VBAT_SCALE"]; + else + board.VBAT_SCALE = UNUSED; return true; } + +String ConfigManager::boardTemplateToJSON(board_t &board) +{ + size_t size = JSON_OBJECT_SIZE(22) + 256; + DynamicJsonDocument doc(size); + String output; + + doc["aADDR"] = board.OLED__address; + doc["oSDA"] = board.OLED__SDA; + doc["oSCL"] = board.OLED__SCL; + doc["oRST"] = board.OLED__RST; + doc["pBut"] = board.PROG__BUTTON; + doc["led"] = board.BOARD_LED; + doc["radio"] = board.L_radio; + doc["lNSS"] = board.L_NSS; + doc["lDIO0"] = board.L_DI00; + doc["lDIO1"] = board.L_DI01; + doc["lBUSSY"] = board.L_BUSSY; + doc["lRST"] = board.L_RST; + doc["lMISO"] = board.L_MISO; + doc["lMOSI"] = board.L_MOSI; + doc["lSCK"] = board.L_SCK; + doc["lTCXOV"] = board.L_TCXO_V; + doc["RXEN"] = board.RX_EN; + doc["TXEN"] = board.TX_EN; + doc["ADC_CTL"] = board.ADC_CTL; + doc["VBAT"] = board.VBAT_AIN; + doc["VBAT_SCALE"] = board.VBAT_SCALE; + doc["name"] = board.BOARD; + + serializeJson(doc, output); + return output; +} diff --git a/tinyGS/src/ConfigManager/ConfigManager.h b/tinyGS/src/ConfigManager/ConfigManager.h index b56dfb1a..3c6f7781 100644 --- a/tinyGS/src/ConfigManager/ConfigManager.h +++ b/tinyGS/src/ConfigManager/ConfigManager.h @@ -126,18 +126,21 @@ struct board_t uint8_t L_MISO; uint8_t L_MOSI; uint8_t L_SCK; - float L_TCXO_V; + float L_TCXO_V; uint8_t RX_EN; uint8_t TX_EN; - String BOARD; + uint8_t ADC_CTL; // GPIO pin to enable ADC reading + uint8_t VBAT_AIN; // GPIO pin for VBAT monitoring + float VBAT_SCALE; // potential divider between battery and GPIO pin + String BOARD; board_t() = default; board_t(uint8_t oled_addr, uint8_t oled_sda, uint8_t oled_scl, uint8_t oled_rst, uint8_t prog_btn, uint8_t board_led, uint8_t l_radio, uint8_t l_nss, uint8_t l_di00, uint8_t l_di01, uint8_t l_bussy, uint8_t l_rst, uint8_t l_miso, uint8_t l_mosi, uint8_t l_sck, - float l_tcxo_v, uint8_t rx_en, uint8_t tx_en, String board_name) + float l_tcxo_v, uint8_t rx_en, uint8_t tx_en, uint8_t adc_ctl, uint8_t vbat_ain, float vbat_scale, String board_name) : OLED__address(oled_addr), OLED__SDA(oled_sda), OLED__SCL(oled_scl), OLED__RST(oled_rst), PROG__BUTTON(prog_btn), BOARD_LED(board_led), L_radio(l_radio), L_NSS(l_nss), L_DI00(l_di00), L_DI01(l_di01), L_BUSSY(l_bussy), L_RST(l_rst), L_MISO(l_miso), L_MOSI(l_mosi), L_SCK(l_sck), - L_TCXO_V(l_tcxo_v), RX_EN(rx_en), TX_EN(tx_en), BOARD(board_name) {} + L_TCXO_V(l_tcxo_v), RX_EN(rx_en), TX_EN(tx_en), ADC_CTL(adc_ctl), VBAT_AIN(vbat_ain), VBAT_SCALE(vbat_scale), BOARD(board_name) {} }; const uint8_t UNUSED = -1; @@ -306,6 +309,7 @@ class ConfigManager : public IotWebConf2 void parseAdvancedConf(); void parseModemStartup(); bool parseBoardTemplate(board_t &); + String boardTemplateToJSON(board_t &board); std::function formValidatorStd; DNSServer dnsServer; diff --git a/tinyGS/src/ConfigManager/html.h b/tinyGS/src/ConfigManager/html.h index 560b08c5..c5eb1408 100644 --- a/tinyGS/src/ConfigManager/html.h +++ b/tinyGS/src/ConfigManager/html.h @@ -71,7 +71,7 @@ const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1" }; 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"}; #endif -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}"; +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}"; const char IOTWEBCONF_DASHBOARD_BODY_INNER[] PROGMEM = "
\n"; const char IOTWEBCONF_CONSOLE_BODY_INNER[] PROGMEM = "

\n"; 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 = "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; }" "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 = '
' + dictTable(dict) + '
'; ed.insertAdjacentHTML('afterend', tblhtml); current_ctrl=ed; } } }" "var current_id, current_ctrl=null; window.addEventListener('load', function() {setup_click('board_template'); setup_click('modem_startup');});"; -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);"; -const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}"; \ No newline at end of file +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);"; +const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}"; diff --git a/tinyGS/src/Display/Display.cpp b/tinyGS/src/Display/Display.cpp index c4ffbedb..ee9a79dc 100644 --- a/tinyGS/src/Display/Display.cpp +++ b/tinyGS/src/Display/Display.cpp @@ -22,6 +22,7 @@ #include "../ConfigManager/ConfigManager.h" #include "../Mqtt/MQTT_credentials.h" #include "../Logger/Logger.h" +#include "../Power/Battery.h" SSD1306* display; OLEDDisplayUi* ui = NULL; @@ -232,6 +233,15 @@ void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int1 snprintf(displayBuffer, sizeof(displayBuffer), "%.1fkbps", status.modeminfo.bitrate); display->drawString(128 + x, 34 + y, displayBuffer); } + if (status.vbat != 0.0) + { + display->setTextAlignment(TEXT_ALIGN_LEFT); + if(getBatteryPercentage() > 100.0f) { + display->drawString(x, 45 + y, "Pow: USB " + String(status.vbat) + "V"); + } else { + display->drawString(x, 45 + y, "Pow: Bat " + String(getBatteryPercentage(), 0) + "% " + String(status.vbat) + "V"); + } + } } void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) diff --git a/tinyGS/src/Mqtt/MQTT_Client.cpp b/tinyGS/src/Mqtt/MQTT_Client.cpp index f1c587a6..a5d898bf 100644 --- a/tinyGS/src/Mqtt/MQTT_Client.cpp +++ b/tinyGS/src/Mqtt/MQTT_Client.cpp @@ -108,7 +108,7 @@ void MQTT_Client::loop() else { StaticJsonDocument<192> doc; - doc["Vbat"] = voltage(); + doc["Vbat"] = status.vbat; doc["Mem"] = ESP.getFreeHeap(); doc["MinMem"] = ESP.getMinFreeHeap(); // Mínimo histórico doc["MaxBlk"] = ESP.getMaxAllocHeap(); // Bloque más grande disponible @@ -249,7 +249,7 @@ void MQTT_Client::sendWelcome() doc["board"] = configManager.getBoard(); doc["mac"] = clientId; doc["seconds"] = millis()/1000; - doc["Vbat"] = voltage(); + doc["Vbat"] = status.vbat; doc["chip"] = ESP.getChipModel(); doc["slot"] = esp_ota_get_running_partition ()->label; doc["pSize"] = esp_ota_get_running_partition ()->size; @@ -1170,37 +1170,3 @@ void MQTT_Client::begin() setServer(configManager.getMqttServer(), configManager.getMqttPort()); setCallback(manageMQTTDataCallback); } - - - -int MQTT_Client::voltage() { - int medianVoltage; - int length = 21; - int voltages[22]; - - for (int i = 0; i < 21; i++) - { - voltages[i] = analogRead(36); - } - - // BubbleSortAsc from https://www.luisllamas.es/arduino-bubble-sort/ - int i, j, flag = 1; - int temp; - for (i = 1; (i <= length) && flag; i++) - { - flag = 0; - for (j = 0; j < (length - 1); j++) - { - if (voltages[j + 1] < voltages[j]) - { - temp = voltages[j]; - voltages[j] = voltages[j + 1]; - voltages[j + 1] = temp; - flag = 1; - } - } - } - medianVoltage = voltages[10]; - return medianVoltage; -} - diff --git a/tinyGS/src/Power/Battery.cpp b/tinyGS/src/Power/Battery.cpp new file mode 100644 index 00000000..86b5331c --- /dev/null +++ b/tinyGS/src/Power/Battery.cpp @@ -0,0 +1,126 @@ +// Battery.cpp +// Battery monitoring helpers for tinyGS + +#include +#include "Battery.h" +#include "../ConfigManager/ConfigManager.h" +#include "../Logger/Logger.h" +#include +#include + +adc_cali_handle_t cali_handle = NULL; +#define DEFAULT_VREF 1100 + + +// Arduino Framework Battery Writeup +// https://digitalconcepts.net.au/arduino/index.php?op=Battery + +// Initialize ADC pins and ADC configuration for the board's VBAT input. +// If the board provides an `ADC_CTL` pin (to enable/disable the voltage +// divider), that pin is configured as an output. If the `VBAT_AIN` analog +// input is available, attach it to the ADC and set resolution/attenuation +// appropriate for battery voltage measurement. +void initBatteryMonitoring(void) +{ + board_t board; + if (ConfigManager::getInstance().getBoardConfig(board)) { + // If ADC_CTL is defined, set it up as the enable pin for ADC reading + if (board.ADC_CTL != UNUSED) { + pinMode(board.ADC_CTL, OUTPUT); // control pin for ADC/voltage divider + } + if (board.VBAT_AIN != UNUSED) { + // Configure the analog input used for VBAT monitoring + pinMode(board.VBAT_AIN, INPUT); + // Use 12-bit resolution and 11dB attenuation for ~0-3.6V range + analogReadResolution(12); + analogSetAttenuation(ADC_11db); + // Characterize ADC for voltage conversion + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = ADC_UNIT_1, + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + adc_cali_create_scheme_curve_fitting(&cali_config, &cali_handle); + } + checkBattery(); // Initial read to seed status.vbat + } +} + +// Drive the ADC control pin to the enabled state (if present) and wait +// briefly for voltages to stabilize before taking a reading. +inline void enableADCReading(board_t board) +{ + if (board.ADC_CTL != UNUSED) { + digitalWrite(board.ADC_CTL, ADC_READ_ENABLE); + delay(50); // Allow voltage to stabilize before sampling + } +} + +// Disable the ADC control pin to remove standby current from the +// voltage divider (if the board uses one). No delay is necessary here. +inline void disableADCReading(board_t board) +{ + if (board.ADC_CTL != UNUSED) { + digitalWrite(board.ADC_CTL, ADC_READ_DISABLE); + } +} + +// Periodically read the VBAT analog input and update the global +// `status.vbat` value. Reads are throttled by `BATTERY_CHECK_INTERVAL`. +// The raw ADC value is scaled by `board.VBAT_SCALE` (provided by the +// board configuration) to convert it into volts. +void checkBattery(void) +{ + static unsigned long lastReadTime = 0; + board_t board; + + // Throttle the battery check interval + if (millis() - lastReadTime > BATTERY_CHECK_INTERVAL) { + lastReadTime = millis(); + if (ConfigManager::getInstance().getBoardConfig(board)) { + if (board.VBAT_AIN != UNUSED && board.VBAT_SCALE != UNUSED) { + int voltage = 0; + + enableADCReading(board); + + // Read raw ADC + int raw = analogRead(board.VBAT_AIN); + + disableADCReading(board); + + //Convert adc_reading to voltage in mV + adc_cali_raw_to_voltage(cali_handle, raw, &voltage); + + // Convert and smooth: seed or simple average with previous value + float measured = (board.VBAT_SCALE * voltage) / 1000.0f; // convert mV to V + Log::debug(PSTR("adc raw: %d, voltage: %f V"), raw, measured); + if (status.vbat == 0.0) { + status.vbat = measured; // initial seed + } else { + status.vbat = (status.vbat + measured) / 2.0f; // simple smoothing + } + } + } + } +} + +// Convert the current battery voltage status.vbat to a percentage. +// Assumes a linear discharge curve between 3.0V (0%) and 4.15V (100%). +// Returns 999.0f if the voltage is above 4.2V, indicating charging state. +float getBatteryPercentage(void) +{ + const float poweredVoltage = 4.23f; // Voltage above 100% + const float maxVoltage = 4.15f; // Voltage at 100% + const float minVoltage = 3.5f; // Voltage at 0% + float voltage = status.vbat; + if (voltage >= poweredVoltage) { + return 999.0f; // Plugged in / charging + } else if (voltage >= maxVoltage) { + return 100.0f; + } else if (voltage <= minVoltage) { + return 0.0f; + } else { + return ((voltage - minVoltage) / (maxVoltage - minVoltage)) * 100.0f; + } +} \ No newline at end of file diff --git a/tinyGS/src/Power/Battery.h b/tinyGS/src/Power/Battery.h new file mode 100644 index 00000000..bb0b549d --- /dev/null +++ b/tinyGS/src/Power/Battery.h @@ -0,0 +1,31 @@ +#ifndef BATTERY_H +#define BATTERY_H +#include "Arduino.h" +#include +#include "../Status.h" +#include "../ConfigManager/ConfigManager.h" + +// Throttle reads of battery voltage (in ms) +#define BATTERY_CHECK_INTERVAL 10000 + +// ADC control pin polarity differs between some boards (ESP32-S3 vs +// others). `ADC_READ_ENABLE` drives the board-specific pin value that +// enables the voltage divider or ADC front-end; `ADC_READ_DISABLE` +// disables it. These are used by enableADCReading()/disableADCReading(). +#if CONFIG_IDF_TARGET_ESP32S3 + #define ADC_READ_ENABLE HIGH + #define ADC_READ_DISABLE LOW +#else + #define ADC_READ_ENABLE LOW + #define ADC_READ_DISABLE HIGH +#endif + +// Global device status structure defined elsewhere; `status.vbat` is +// used/updated by the battery monitoring code in Battery.cpp. +extern Status status; + +void initBatteryMonitoring(void); +void checkBattery(void); +float getBatteryPercentage(void); + +#endif \ No newline at end of file diff --git a/tinyGS/src/Status.h b/tinyGS/src/Status.h index eaf424c2..6a9eac50 100644 --- a/tinyGS/src/Status.h +++ b/tinyGS/src/Status.h @@ -100,6 +100,7 @@ struct Status { bool mqtt_connected = false; bool radio_ready = false; int16_t radio_error = 0; + float vbat = 0.0; PacketInfo lastPacketInfo; ModemInfo modeminfo; ModemInfo modeminfolastpckt; diff --git a/tinyGS/tinyGS.ino b/tinyGS/tinyGS.ino index 8b78dff4..5df77071 100644 --- a/tinyGS/tinyGS.ino +++ b/tinyGS/tinyGS.ino @@ -80,6 +80,7 @@ #include "time.h" #include "src/Mqtt/MQTT_credentials.h" #include "src/Improv/tinygs_improv.h" +#include "src/Power/Battery.h" #if RADIOLIB_VERSION_MAJOR != (0x07) || RADIOLIB_VERSION_MINOR != (0x05) || RADIOLIB_VERSION_PATCH != (0x00) || RADIOLIB_VERSION_EXTRA != (0x00) @@ -166,8 +167,10 @@ void setup() // make sure to call doLoop at least once before starting to use the configManager configManager.doLoop(); board_t board; - if(configManager.getBoardConfig(board)) + if(configManager.getBoardConfig(board)) { pinMode (board.PROG__BUTTON, INPUT_PULLUP); + initBatteryMonitoring(); + } displayInit(); displayShowInitialCredits(); configManager.delay(1000); @@ -321,6 +324,8 @@ void loop() { return; } + checkBattery(); + // connected mqtt.loop();