diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index d7e01c0c8..ca4b0a1f5 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -770,8 +770,6 @@ void setup(void) { if (bitRead(Settings->rule_enabled, 0)) Run_Scripter(">BS",3,0); #endif - XdrvCall(FUNC_ACTIVE); // FUNC_ACTIVE - TasmotaGlobal.rules_flag.system_init = 1; } @@ -887,6 +885,7 @@ void Scheduler(void) { if (TimeReached(state_second)) { SetNextTimeInterval(state_second, 1000); PerformEverySecond(); + XdrvCall(FUNC_ACTIVE); XdrvXsnsCall(FUNC_EVERY_SECOND); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino index b33d8a1a9..a2e16c0dc 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino @@ -65,18 +65,20 @@ enum GVPinTypes { #endif // GV_INPUT_DETECTION }; -struct { - WiFiClient WebClient; +typedef struct { ESP8266WebServer *WebServer; Ticker ticker; - int lastPinStates[MAX_GPIO_PIN]; + String baseUrl; uint32_t lastSentWithNoActivity; uint32_t freeHeap; uint32_t freePSRAM; uint32_t sampling; - bool active; + uint16_t port; + int8_t lastPinStates[MAX_GPIO_PIN]; bool sse_ready; -} GV; +} tGV; +tGV* GV = nullptr; +WiFiClient GVWebClient; #ifdef GV_INPUT_DETECTION @@ -98,35 +100,41 @@ int GetPinMode(uint8_t pin) { #endif // GV_INPUT_DETECTION -void GVStop(void) { - GV.sse_ready = false; - GV.ticker.detach(); - GV.active = false; +void GVInit(void) { + if (!GV) { + GV = (tGV*)calloc(sizeof(tGV), 1); + if (GV) { + GV->sampling = (GV_SAMPLING_INTERVAL < 20) ? 20 : GV_SAMPLING_INTERVAL; + GV->baseUrl = GV_BASE_URL; + GV->port = GV_PORT; + } + } +} - GV.WebServer->stop(); - GV.WebServer = nullptr; +void GVStop(void) { + GV->sse_ready = false; + GV->ticker.detach(); + + GV->WebServer->stop(); + GV->WebServer = nullptr; } void GVBegin(void) { - if (0 == GV.sampling) { - GV.sampling = (GV_SAMPLING_INTERVAL < 20) ? 20 : GV_SAMPLING_INTERVAL; + if (GV->WebServer == nullptr) { + GV->WebServer = new ESP8266WebServer(GV->port); + // Set CORS headers for global responses + // GV->WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); + // GV->WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST, OPTIONS")); + // GV->WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("Content-Type")); + GV->WebServer->on("/", GVHandleRoot); + GV->WebServer->on("/release", GVHandleRelease); + GV->WebServer->on("/free_psram", GVHandleFreePSRam); + GV->WebServer->on("/sampling", GVHandleSampling); + GV->WebServer->on("/espinfo", GVHandleEspInfo); + GV->WebServer->on("/partition", GVHandlePartition); + GV->WebServer->on("/events", GVHandleEvents); + GV->WebServer->begin(); } - - GV.WebServer = new ESP8266WebServer(GV_PORT); - // Set CORS headers for global responses -// GV.WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); -// GV.WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST, OPTIONS")); -// GV.WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("Content-Type")); - GV.WebServer->on("/", GVHandleRoot); - GV.WebServer->on("/release", GVHandleRelease); - GV.WebServer->on("/free_psram", GVHandleFreePSRam); - GV.WebServer->on("/sampling", GVHandleSampling); - GV.WebServer->on("/espinfo", GVHandleEspInfo); - GV.WebServer->on("/partition", GVHandlePartition); - GV.WebServer->on("/events", GVHandleEvents); - GV.WebServer->begin(); - - GV.active = true; } void GVHandleRoot(void) { @@ -134,17 +142,17 @@ void GVHandleRoot(void) { char* content = ext_snprintf_malloc_P(HTTP_GV_PAGE, SettingsTextEscaped(SET_DEVICENAME).c_str(), - GV_BASE_URL, - WiFi.localIP().toString().c_str(), - GV_PORT, + GV->baseUrl.c_str(), + NetworkAddress().toString().c_str(), + GV->port, ESP_getFreeSketchSpace() / 1024); if (content == nullptr) { return; } // Avoid crash - GV.WebServer->send_P(200, "text/html", content); + GV->WebServer->send_P(200, "text/html", content); free(content); } void GVWebserverSendJson(String &jsonResponse) { - GV.WebServer->send(200, "application/json", jsonResponse); + GV->WebServer->send(200, "application/json", jsonResponse); } void GVHandleRelease(void) { @@ -164,7 +172,7 @@ void GVHandleFreePSRam(void) { } void GVHandleSampling(void) { - String jsonResponse = "{\"sampling\":\"" + String(GV.sampling) + "\"}"; + String jsonResponse = "{\"sampling\":\"" + String(GV->sampling) + "\"}"; GVWebserverSendJson(jsonResponse); } @@ -242,39 +250,41 @@ void GVHandlePartition(void) { } void GVHandleEvents(void) { - GV.WebClient = GV.WebServer->client(); - GV.WebClient.setNoDelay(true); -// GV.WebClient.setSync(true); + GVWebClient = GV->WebServer->client(); + GVWebClient.setNoDelay(true); +// GVWebClient.setSync(true); - GV.WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // The payload can go on forever - GV.WebServer->sendContent_P(HTTP_GV_EVENT); + 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 - if (GV.sampling != 100) { - GV.ticker.attach_ms(GV.sampling, GVMonitorTask); // Use Tasmota Scheduler (100) or Ticker (20..99,101..1000) + GV->sse_ready = true; // Ready for async updates + if (GV->sampling != 100) { + 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 GVEventDisconnected(void) { - if (GV.sse_ready) { + if (GV->sse_ready) { AddLog(LOG_LEVEL_DEBUG, PSTR("IOV: Disconnected")); } - GV.sse_ready = false; // This just stops the event to be restarted by opening root page again - GV.ticker.detach(); + GV->sse_ready = false; // This just stops the event to be restarted by opening root page again + GV->ticker.detach(); } void GVCloseEvent(void) { - GVEventSend("{}", "close", millis()); // Closes web page - GVEventDisconnected(); + if (GV->WebServer) { + GVEventSend("{}", "close", millis()); // Closes web page + GVEventDisconnected(); + } } //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()) { + if (GVWebClient.connected()) { // generateEventMessage() in AsyncEventSource.cpp -// GV.WebClient.printf_P(PSTR("retry:1000\nid:%u\nevent:%s\ndata:%s\n\n"), id, event, message); - GV.WebClient.printf_P(PSTR("id:%u\nevent:%s\ndata:%s\n\n"), id, event, message); +// GVWebClient.printf_P(PSTR("retry:1000\nid:%u\nevent:%s\ndata:%s\n\n"), id, event, message); + GVWebClient.printf_P(PSTR("id:%u\nevent:%s\ndata:%s\n\n"), id, event, message); } else { GVEventDisconnected(); } @@ -353,10 +363,10 @@ void GVMonitorTask(void) { #endif } - if (originalValue != GV.lastPinStates[pin]) { + if (originalValue != GV->lastPinStates[pin]) { if (hasChanges) { jsonMessage += ","; } jsonMessage += "\"" + String(pin) + "\":{\"s\":" + currentState + ",\"v\":" + originalValue + ",\"t\":" + pintype + "}"; - GV.lastPinStates[pin] = originalValue; + GV->lastPinStates[pin] = originalValue; hasChanges = true; } } @@ -366,9 +376,9 @@ void GVMonitorTask(void) { } uint32_t heap = ESP_getFreeHeap(); - if (heap != GV.freeHeap) { + if (heap != GV->freeHeap) { // Send freeHeap - GV.freeHeap = heap; + GV->freeHeap = heap; char temp[20]; snprintf_P(temp, sizeof(temp), PSTR("%d KB"), heap / 1024); GVEventSend(temp, "free_heap", millis()); @@ -379,8 +389,8 @@ void GVMonitorTask(void) { if (UsePSRAM()) { // Send freePsram uint32_t psram = ESP.getFreePsram(); - if (psram != GV.freePSRAM) { - GV.freePSRAM = psram; + if (psram != GV->freePSRAM) { + GV->freePSRAM = psram; char temp[20]; snprintf_P(temp, sizeof(temp), PSTR("%d KB"), psram / 1024); GVEventSend(temp, "free_psram", millis()); @@ -391,16 +401,16 @@ void GVMonitorTask(void) { if (!hasChanges) { // Send freeHeap as keepAlive - uint32_t last_sent = millis() - GV.lastSentWithNoActivity; + uint32_t last_sent = millis() - GV->lastSentWithNoActivity; if (last_sent > GV_KEEP_ALIVE) { // No activity, resending for pulse char temp[20]; snprintf_P(temp, sizeof(temp), PSTR("%d KB"), heap / 1024); GVEventSend(temp, "free_heap", millis()); - GV.lastSentWithNoActivity = millis(); + GV->lastSentWithNoActivity = millis(); } } else { - GV.lastSentWithNoActivity = millis(); + GV->lastSentWithNoActivity = millis(); } } @@ -409,10 +419,10 @@ void GVMonitorTask(void) { \*********************************************************************************************/ const char kGVCommands[] PROGMEM = "GV|" // Prefix - "Viewer|Sampling"; + "Viewer|Sampling|Port|Url"; void (* const GVCommand[])(void) PROGMEM = { - &CmndGvViewer, &CmndGvSampling }; + &CmndGvViewer, &CmndGvSampling, &CmndGvPort, &CmndGvUrl }; void CmndGvViewer(void) { /* GvViewer - Show current viewer state @@ -420,10 +430,11 @@ void CmndGvViewer(void) { GvViewer 1 - Turn viewer On GvViewer 2 - Toggle viewer state */ + GVInit(); if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { uint32_t state = XdrvMailbox.payload; if (2 == state) { // Toggle - state = GV.active ^1; + state = (GV->WebServer == nullptr) ^1; } if (state) { // On GVBegin(); @@ -432,8 +443,8 @@ void CmndGvViewer(void) { GVStop(); } } - if (GV.active) { - Response_P(PSTR("{\"%s\":\"Active on http://%s:" STR(GV_PORT) "/\"}"), XdrvMailbox.command, WiFi.localIP().toString().c_str()); + if (GV->WebServer) { + Response_P(PSTR("{\"%s\":\"Active on http://%s:%d/\"}"), XdrvMailbox.command, NetworkAddress().toString().c_str(), GV->port); } else { ResponseCmndChar_P(PSTR("Stopped")); } @@ -443,11 +454,38 @@ void CmndGvSampling(void) { /* GvSampling - Show current sampling interval GvSampling 20 .. 1000 - Set sampling interval */ + GVInit(); if ((XdrvMailbox.payload >= 20) && (XdrvMailbox.payload <= 1000)) { GVCloseEvent(); // Stop current updates - GV.sampling = XdrvMailbox.payload; // 20 - 1000 milliseconds + GV->sampling = XdrvMailbox.payload; // 20 - 1000 milliseconds } - ResponseCmndNumber(GV.sampling); + ResponseCmndNumber(GV->sampling); +} + +void CmndGvPort(void) { + /* GvPort - Show vurrent port + GvPort 1 - Select default port + GvPort 5557 - Set port + */ + GVInit(); + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + GVCloseEvent(); // Stop current updates + GV->port = (SC_DEFAULT == XdrvMailbox.payload) ? GV_PORT : XdrvMailbox.payload; + } + ResponseCmndNumber(GV->port); +} + +void CmndGvUrl(void) { + /* GvUrl - Show current url + GvUrl 1 - Select default url + GvUrl https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer_1_5/ + */ + GVInit(); + if (XdrvMailbox.data_len > 0) { + GVCloseEvent(); // Stop current updates + GV->baseUrl = (SC_DEFAULT == XdrvMailbox.payload) ? GV_BASE_URL : XdrvMailbox.data; + } + ResponseCmndChar(GV->baseUrl.c_str()); } /*********************************************************************************************\ @@ -464,12 +502,11 @@ void GVSetupAndStart(void) { AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_GPIO_VIEWER)); - if (!GV.active) { // WebServer not started - GVBegin(); - } + GVInit(); + GVBegin(); // Start WebServer char redirect[100]; - snprintf_P(redirect, sizeof(redirect), PSTR("http://%s:" STR(GV_PORT) "/"), WiFi.localIP().toString().c_str()); + snprintf_P(redirect, sizeof(redirect), PSTR("http://%s:%d/"), NetworkAddress().toString().c_str(), GV->port); Webserver->sendHeader(F("Location"), redirect); Webserver->send(303); } @@ -499,18 +536,18 @@ bool Xdrv121(uint32_t function) { break; #endif // USE_WEBSERVER } - if (GV.active) { + if (GV && (GV->WebServer)) { switch (function) { case FUNC_LOOP: - if (GV.WebServer) { GV.WebServer->handleClient(); } + GV->WebServer->handleClient(); break; case FUNC_EVERY_100_MSECOND: - if (GV.sse_ready && (100 == GV.sampling)) { + if (GV->sse_ready && (100 == GV->sampling)) { GVMonitorTask(); } break; case FUNC_SAVE_BEFORE_RESTART: - GVCloseEvent(); // Stop current updates + GVCloseEvent(); // Stop current updates break; case FUNC_ACTIVE: result = true;