Add user control over GPIOViewer sampling interval

This commit is contained in:
Theo Arends 2024-01-10 15:51:19 +01:00
parent c2d6bbf2c1
commit f30f3f8016
2 changed files with 50 additions and 14 deletions

View File

@ -471,6 +471,7 @@
// #define USE_WEBSEND_RESPONSE // Enable command WebSend response message (+1k code) // #define USE_WEBSEND_RESPONSE // Enable command WebSend response message (+1k code)
// #define USE_WEBGETCONFIG // Enable restoring config from external webserver (+0k6) // #define USE_WEBGETCONFIG // Enable restoring config from external webserver (+0k6)
// #define USE_GPIO_VIEWER // Enable GPIO Viewer to see realtime GPIO states (+4k code) // #define USE_GPIO_VIEWER // Enable GPIO Viewer to see realtime GPIO states (+4k code)
// #define GV_SAMPLING_INTERVAL 100 // [GvSampling] milliseconds - Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
#define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common) #define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
#define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
// #define USE_CCLOADER // Enable CCLoader FW upgrade tool (for CC25xx devices) // #define USE_CCLOADER // Enable CCLoader FW upgrade tool (for CC25xx devices)

View File

@ -17,8 +17,13 @@
#define XDRV_121 121 #define XDRV_121 121
#ifndef GV_PORT
#define GV_PORT 5557 #define GV_PORT 5557
#define GV_SAMPLING_INTERVAL 100 // milliseconds - Relates to FUNC_EVERY_100_MSECOND #endif
#ifndef GV_SAMPLING_INTERVAL
#define GV_SAMPLING_INTERVAL 100 // [GvSampling] milliseconds - Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
#endif
#define GV_KEEP_ALIVE 1000 // milliseconds - If no activity after this do a heap size event anyway #define GV_KEEP_ALIVE 1000 // milliseconds - If no activity after this do a heap size event anyway
#define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/" #define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/"
@ -40,7 +45,7 @@ const char HTTP_GV_PAGE[] PROGMEM =
"var serverPort=" STR(GV_PORT) ";" "var serverPort=" STR(GV_PORT) ";"
"var ip='%s';" // WiFi.localIP().toString().c_str() "var ip='%s';" // WiFi.localIP().toString().c_str()
"var source=new EventSource('http://%s:" STR(GV_PORT) "/events');" // WiFi.localIP().toString().c_str() "var source=new EventSource('http://%s:" STR(GV_PORT) "/events');" // WiFi.localIP().toString().c_str()
"var sampling_interval='" STR(GV_SAMPLING_INTERVAL) "';" "var sampling_interval='%d';" // Gv.sampling
#ifdef ESP32 #ifdef ESP32
"var psramSize='%d KB';" // ESP.getPsramSize() / 1024 "var psramSize='%d KB';" // ESP.getPsramSize() / 1024
#endif // ESP32 #endif // ESP32
@ -78,15 +83,21 @@ enum GVPinTypes {
struct { struct {
WiFiClient WebClient; WiFiClient WebClient;
ESP8266WebServer *WebServer; ESP8266WebServer *WebServer;
Ticker ticker;
int lastPinStates[MAX_GPIO_PIN]; int lastPinStates[MAX_GPIO_PIN];
uint32_t lastSentWithNoActivity; uint32_t lastSentWithNoActivity;
uint32_t freeHeap; uint32_t freeHeap;
uint32_t freePSRAM; uint32_t freePSRAM;
uint32_t sampling;
bool active; bool active;
bool sse_ready; bool sse_ready;
} GV; } GV;
void GVBegin(void) { void GVBegin(void) {
if (0 == GV.sampling) {
GV.sampling = (GV_SAMPLING_INTERVAL < 20) ? 20 : GV_SAMPLING_INTERVAL;
}
GV.WebServer = new ESP8266WebServer(GV_PORT); GV.WebServer = new ESP8266WebServer(GV_PORT);
// Set CORS headers for global responses // Set CORS headers for global responses
GV.WebServer->sendHeader("Access-Control-Allow-Origin", "*"); GV.WebServer->sendHeader("Access-Control-Allow-Origin", "*");
@ -103,6 +114,7 @@ void GVBegin(void) {
void GVStop(void) { void GVStop(void) {
GV.sse_ready = false; GV.sse_ready = false;
GV.ticker.detach();
GV.active = false; GV.active = false;
GV.WebServer->stop(); GV.WebServer->stop();
@ -118,8 +130,10 @@ void GVHandleEvents(void) {
GV.WebServer->sendContent_P(HTTP_GV_EVENT); GV.WebServer->sendContent_P(HTTP_GV_EVENT);
GV.sse_ready = true; // Ready for async updates GV.sse_ready = true; // Ready for async updates
if (GV.sampling != 100) {
AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Client connected")); GV.ticker.attach_ms(GV.sampling, GVMonitorTask); // Use Tasmota Scheduler (100) or Ticker (20..99,101..1000)
}
AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Connected"));
} }
void GVHandleRoot(void) { void GVHandleRoot(void) {
@ -127,6 +141,7 @@ void GVHandleRoot(void) {
SettingsTextEscaped(SET_DEVICENAME).c_str(), SettingsTextEscaped(SET_DEVICENAME).c_str(),
WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(),
WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(),
GV.sampling,
#ifdef ESP32 #ifdef ESP32
ESP.getPsramSize() / 1024, ESP.getPsramSize() / 1024,
#endif // ESP32 #endif // ESP32
@ -136,6 +151,7 @@ void GVHandleRoot(void) {
free(content); free(content);
GV.sse_ready = false; // Allow restart of updates on page load GV.sse_ready = false; // Allow restart of updates on page load
GV.ticker.detach();
} }
void GVHandleRelease(void) { void GVHandleRelease(void) {
@ -162,11 +178,11 @@ void GVEventSend(const char *message, const char *event, uint32_t id) {
GV.WebClient.printf_P(PSTR("id: %u\r\nevent: %s\r\ndata: %s\r\n\r\n"), id, event, message); GV.WebClient.printf_P(PSTR("id: %u\r\nevent: %s\r\ndata: %s\r\n\r\n"), id, event, message);
} else { } else {
if (GV.sse_ready) { if (GV.sse_ready) {
AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Client disconnected")); AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Disconnected"));
} }
// GVStop(); // This will stop the webserver but not all memory will be released resulting in memory leaks // GVStop(); // This will stop the webserver but not all memory will be released resulting in memory leaks
GV.sse_ready = false; // This just stops the event to be restarted by opening root page again GV.sse_ready = false; // This just stops the event to be restarted by opening root page again
GV.ticker.detach();
} }
} }
@ -274,12 +290,17 @@ void GVMonitorTask(void) {
\*********************************************************************************************/ \*********************************************************************************************/
const char kGVCommands[] PROGMEM = "GV|" // Prefix const char kGVCommands[] PROGMEM = "GV|" // Prefix
"Viewer"; "Viewer|Sampling";
void (* const GVCommand[])(void) PROGMEM = { void (* const GVCommand[])(void) PROGMEM = {
&CmndGVViewer}; &CmndGvViewer, &CmndGvSampling };
void CmndGVViewer(void) { void CmndGvViewer(void) {
/* GvViewer - Show current viewer state
GvViewer 0 - Turn viewer off
GvViewer 1 - Turn viewer On
GvViewer 2 - Toggle viewer state
*/
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
uint32_t state = XdrvMailbox.payload; uint32_t state = XdrvMailbox.payload;
if (2 == state) { // Toggle if (2 == state) { // Toggle
@ -298,6 +319,18 @@ void CmndGVViewer(void) {
} }
} }
void CmndGvSampling(void) {
/* GvSampling - Show current sampling interval
GvSampling 20 .. 1000 - Set sampling interval
*/
if ((XdrvMailbox.payload >= 20) && (XdrvMailbox.payload <= 1000)) {
GV.sse_ready = false; // Stop current updates
GV.ticker.detach();
GV.sampling = XdrvMailbox.payload; // 20 - 1000 milliseconds
}
ResponseCmndNumber(GV.sampling);
}
/*********************************************************************************************\ /*********************************************************************************************\
* GUI * GUI
\*********************************************************************************************/ \*********************************************************************************************/
@ -353,7 +386,9 @@ bool Xdrv121(uint32_t function) {
if (GV.WebServer) { GV.WebServer->handleClient(); } if (GV.WebServer) { GV.WebServer->handleClient(); }
break; break;
case FUNC_EVERY_100_MSECOND: case FUNC_EVERY_100_MSECOND:
if (GV.sse_ready) { GVMonitorTask(); } if (GV.sse_ready && (100 == GV.sampling)) {
GVMonitorTask();
}
break; break;
case FUNC_ACTIVE: case FUNC_ACTIVE:
result = true; result = true;