diff --git a/CHANGELOG.md b/CHANGELOG.md index d87631f60..dd2e9d1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to this project will be documented in this file. - Berry add support for `tcpclientasync` in `tcpserver` (#20401) - Berry add `tasmota.urlbecload(url:string) -> bool` (#20412) - GPIO Viewer to see realtime GPIO states. Enable with define USE_GPIO_VIEWER -- Berry `gpio.read_pwm` and `gpio.read_pwm_resolution` +- Berry `gpio.read_pwm` and `gpio.read_pwm_resolution` (#20414) ### Breaking Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index de5928873..e13594cf8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -129,6 +129,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry `introspect.set()` for class attributes [#20339](https://github.com/arendst/Tasmota/issues/20339) - Berry add support for `tcpclientasync` in `tcpserver` [#20401](https://github.com/arendst/Tasmota/issues/20401) - Berry add `tasmota.urlbecload(url:string) -> bool` [#20412](https://github.com/arendst/Tasmota/issues/20412) +- Berry `gpio.read_pwm` and `gpio.read_pwm_resolution` [#20414](https://github.com/arendst/Tasmota/issues/20414) - HASPmota `haspmota.page_show()` to change page [#20333](https://github.com/arendst/Tasmota/issues/20333) - HASPmota type `chart` [#20372](https://github.com/arendst/Tasmota/issues/20372) - Matter support for password for remote Tasmota devices [#20296](https://github.com/arendst/Tasmota/issues/20296) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino index e2295044d..b996808f5 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino @@ -23,12 +23,10 @@ const char *GVRelease = "1.0.5"; #define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/" #ifdef ESP32 -const int GVMaxGPIOPins = 49; // Global variables to capture PMW pins const int GVMaxChannels = 64; #endif // ESP32 #ifdef ESP8266 -const int GVMaxGPIOPins = 18; // Global variables to capture PMW pins const int GVMaxChannels = MAX_PWMS; #endif // ESP8266 @@ -49,7 +47,7 @@ const char HTTP_GV_PAGE[] PROGMEM = "var ip='%s';" // 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 freeSketchSpace='%d';" // GV.freeRAM + "var freeSketchSpace='%s';" // GVFormatBytes(ESP_getFreeSketchSpace()).c_str() "" "" "" @@ -66,6 +64,13 @@ const char HTTP_GV_PAGE[] PROGMEM = "" ""; +const char HTTP_GV_EVENT[] PROGMEM = + "HTTP/1.1 200 OK\n" + "Content-Type: text/event-stream;\n" + "Connection: keep-alive\n" + "Cache-Control: no-cache\n" + "Access-Control-Allow-Origin: *\n\n"; + enum GVPinTypes { digitalPin = 0, PWMPin = 1, @@ -76,12 +81,12 @@ struct { WiFiClient WebClient; ESP8266WebServer *WebServer; int freeHeap; - uint32_t lastPinStates[GVMaxGPIOPins]; + uint32_t lastPinStates[MAX_GPIO_PIN]; int ledcChannelPin[GVMaxChannels][2]; int ledcChannelPinCount; int ledcChannelResolution[GVMaxChannels][2]; int ledcChannelResolutionCount; - bool first; + bool sse_ready; bool active; } GV; @@ -160,7 +165,7 @@ int GVMapLedcReadTo8Bit(int channel, uint32_t *originalValue) { uint32_t maxDutyCycle = (1 << GVGetChannelResolution(channel)) - 1; #ifdef ESP32 - *originalValue = ledcRead(channel); + *originalValue = ledcRead2(channel); #endif // ESP32 #ifdef ESP8266 if (17 == channel) { @@ -215,13 +220,13 @@ void GVResetStatePins(void) { uint32_t pintype; AddLog(LOG_LEVEL_INFO, "IOV: GPIOViewer Connected, sampling interval is " STR(GV_SAMPLING_INTERVAL) "ms"); - for (int i = 0; i < GVMaxGPIOPins; i++) { + for (int i = 0; i < MAX_GPIO_PIN; i++) { GV.lastPinStates[i] = GVReadGPIO(i, &originalValue, &pintype); } } -//void GVEventsSend(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); -void GVEventsSend(const char *message, const char *event, uint32_t id) { +//void GVEventSend(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); +void GVEventSend(const char *message, const char *event, uint32_t id) { if (GV.WebClient.connected()) { // generateEventMessage() in AsyncEventSource.cpp // GV.WebClient.printf_P(PSTR("retry: 0\r\nid: %u\r\nevent: %s\r\ndata: %s\r\n\r\n"), id, event, message); @@ -237,7 +242,7 @@ void GVMonitorTask(void) { String jsonMessage = "{"; bool hasChanges = false; - for (int i = 0; i < GVMaxGPIOPins; i++) { + for (int i = 0; i < MAX_GPIO_PIN; i++) { int currentState = GVReadGPIO(i, &originalValue, &pintype); if (originalValue != GV.lastPinStates[i]) { @@ -254,14 +259,14 @@ void GVMonitorTask(void) { if (hasChanges) { // events->send(jsonMessage.c_str(), "gpio-state", millis()); - GVEventsSend(jsonMessage.c_str(), "gpio-state", millis()); + GVEventSend(jsonMessage.c_str(), "gpio-state", millis()); } uint32_t heap = ESP_getFreeHeap(); if (heap != GV.freeHeap) { GV.freeHeap = heap; // events->send(GVFormatBytes(GV.freeHeap).c_str(), "free_heap", millis()); - GVEventsSend(GVFormatBytes(GV.freeHeap).c_str(), "free_heap", millis()); + GVEventSend(GVFormatBytes(GV.freeHeap).c_str(), "free_heap", millis()); } } @@ -269,44 +274,40 @@ void GVBegin(void) { GVPrintPWNTraps(); GV.WebServer = new ESP8266WebServer(GV_PORT); - -// GV.WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever - // Set CORS headers for global responses GV.WebServer->sendHeader("Access-Control-Allow-Origin", "*"); GV.WebServer->sendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); GV.WebServer->sendHeader("Access-Control-Allow-Headers", "Content-Type"); - GV.WebServer->on("/events", GVHandleEvents); GV.WebServer->on("/", GVHandleRoot); GV.WebServer->on("/release", GVHandleRelease); - GV.WebServer->begin(); } void GVHandleEvents(void) { - if (!GV.first) { - GVResetStatePins(); - GV.first = true; + GVResetStatePins(); - GV.WebClient = GV.WebServer->client(); - GV.WebClient.setNoDelay(true); -// GV.WebClient.setSync(true); + GV.WebClient = GV.WebServer->client(); + GV.WebClient.setNoDelay(true); +// GV.WebClient.setSync(true); - GV.WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // The payload can go on forever - GV.WebServer->sendContent_P(PSTR("HTTP/1.1 200 OK\nContent-Type: text/event-stream;\nConnection: keep-alive\nCache-Control: no-cache\nAccess-Control-Allow-Origin: *\n\n")); - } + GV.WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // The payload can go on forever + GV.WebServer->sendContent_P(HTTP_GV_EVENT); + + GV.sse_ready = true; // Ready for async updates } void GVHandleRoot(void) { char* content = ext_snprintf_malloc_P(HTTP_GV_PAGE, WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(), - GVFormatBytes(ESP.getFreeSketchSpace()).c_str()); - if (content == nullptr) { return; } // Avoid crash + GVFormatBytes(ESP_getFreeSketchSpace()).c_str()); + if (content == nullptr) { return; } // Avoid crash GV.WebServer->send_P(200, "text/html", content); free(content); + + GV.sse_ready = false; // Allow restart of updates on page load } void GVHandleRelease(void) { @@ -328,7 +329,7 @@ bool Xdrv121(uint32_t function) { if (GV.WebServer) { GV.WebServer->handleClient(); } break; case FUNC_EVERY_100_MSECOND: - if (GV.first) { GVMonitorTask(); } + if (GV.sse_ready) { GVMonitorTask(); } break; case FUNC_ACTIVE: result = true;