From af9d4cadd8ef524d3156e9e509ab935d87bf572c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:58:00 +0100 Subject: [PATCH] GPIO Viewer update from 1.0.7 to 1.5.0 --- tasmota/html_compressed/HTTP_GV_PAGE_150.h | 25 +++ tasmota/html_uncompressed/HTTP_GV_PAGE_150.h | 24 +++ tasmota/my_user_config.h | 3 +- tasmota/tasmota_support/support_esp32.ino | 28 +++ tasmota/tasmota_support/support_esp8266.ino | 62 +++++++ .../xdrv_121_gpioviewer.ino | 163 ++++++++++++++---- 6 files changed, 273 insertions(+), 32 deletions(-) create mode 100644 tasmota/html_compressed/HTTP_GV_PAGE_150.h create mode 100644 tasmota/html_uncompressed/HTTP_GV_PAGE_150.h diff --git a/tasmota/html_compressed/HTTP_GV_PAGE_150.h b/tasmota/html_compressed/HTTP_GV_PAGE_150.h new file mode 100644 index 000000000..b0200b768 --- /dev/null +++ b/tasmota/html_compressed/HTTP_GV_PAGE_150.h @@ -0,0 +1,25 @@ +///////////////////////////////////////////////////////////////////// +// compressed by tools/unishox/compress-html-uncompressed.py +///////////////////////////////////////////////////////////////////// + +const size_t HTTP_GV_PAGE_SIZE = 465; +const char HTTP_GV_PAGE_COMPRESSED[] PROGMEM = "\x3D\x0F\xE1\x10\x98\x1D\x19\x0C\x64\x85\x50\xD0\x8F\xC3\xD0\x55\x0D\x09\x05\x7C" + "\x3C\x7C\x3E\xDF\x1F\x67\xE1\xE8\x29\xD8\x87\xE1\xE9\x5B\x41\x33\xF0\xFA\xF2\x3A" + "\xD1\x0F\x10\xC2\xD2\xC4\x46\x7A\xC7\xDF\xCF\x43\xB0\x10\x6A\x1E\x83\x5D\x5A\x0C" + "\x55\xFF\xCE\x8F\x87\xD9\x0B\x2A\x2B\x23\x07\x59\xCF\x9F\x67\xE1\xE8\x43\xAF\xCD" + "\x0A\xFB\x30\x7C\x3E\xCF\xAF\x1F\x67\xE1\xE8\x16\xF1\xA2\xFB\x08\xF8\x7D\xE8\x79" + "\xC7\xD8\x21\x57\x31\x66\x7A\x1E\x71\xD3\x43\xC3\xEC\x10\xB2\x6E\xB1\xAC\xF8\x7D" + "\x99\xEB\x1F\x19\x9F\xA3\xED\x07\x9D\x4F\xA8\xF8\x7D\x8F\xE8\x94\x28\xF8\x23\x33" + "\xD0\xD9\xD6\x3F\xA2\x50\xA3\xA9\x6F\x6D\x6D\x84\x75\xF8\x3B\x09\x9F\x0E\x43\xA3" + "\x3E\xCF\xC3\xD0\xBC\x1F\xF4\x65\x2A\x2B\x32\x18\xCF\x87\xD8\xDC\x10\x58\x4C\xFB" + "\x41\xFF\x3D\xEC\xFE\x8F\x6F\x2F\x7C\x33\xE1\xF6\x43\xC4\x30\xB4\xB0\x10\x78\x25" + "\x23\x31\x6C\xE8\x6B\xF1\xF6\x7E\x1E\x87\x60\x20\xE2\xC0\x8F\xC0\x47\xA8\xC8\x27" + "\xE1\x4D\xD0\x24\xE3\xE0\x83\xC0\xD5\xFB\xCE\xBC\x76\x0D\xBD\xE3\xA0\x7E\xF1\xF6" + "\x7E\x1E\x87\x60\x20\xF1\x1C\x3B\x04\x32\x3F\x0F\x41\x34\xCD\x68\x87\xC3\xEE\xC6" + "\x0C\x3E\xCF\xC3\xD0\xEC\x13\x4C\xC1\x0F\x79\x3F\x07\xF7\x84\xC1\xF3\xA0\xF0\xCD" + "\xC3\xF7\xE7\x55\xBC\x3D\xE4\x7C\x47\xB7\x46\x1E\x67\xD9\xF5\xE3\xEC\xEA\x19\x9F" + "\xA3\xCC\xFB\x3E\x84\x3E\xCE\xA3\x1F\x6C\xBC\x68\xE8\x31\x45\xFB\x1A\x79\x9F\x67" + "\xD0\x88\x8D\x08\x71\xF6\x7B\x8F\x2A\x01\x8B\xC0\x49\xD8\x08\x3A\x49\xD8\x2A\x2B" + "\x86\x84\x7E\x1B"; + +#define HTTP_GV_PAGE Decompress(HTTP_GV_PAGE_COMPRESSED,HTTP_GV_PAGE_SIZE).c_str() \ No newline at end of file diff --git a/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h b/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h new file mode 100644 index 000000000..8fed388bd --- /dev/null +++ b/tasmota/html_uncompressed/HTTP_GV_PAGE_150.h @@ -0,0 +1,24 @@ +const char HTTP_GV_PAGE[] PROGMEM = + "" + "" + "" + "%s - GPIO Viewer" // SettingsTextEscaped(SET_DEVICENAME).c_str() + "" + "" // GV_BASE_URL + "" + "" + "" + "" + "" + "" + "
" + "" + "" + ""; diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 93da42e15..ce1a1f3f2 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -470,8 +470,7 @@ #define USE_ENHANCED_GUI_WIFI_SCAN // Enable Wi-Fi scan output with BSSID (+0k5 code) // #define USE_WEBSEND_RESPONSE // Enable command WebSend response message (+1k code) // #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 GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/" +// #define USE_GPIO_VIEWER // Enable GPIO Viewer to see realtime GPIO states (+6k 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_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) diff --git a/tasmota/tasmota_support/support_esp32.ino b/tasmota/tasmota_support/support_esp32.ino index f3e362ee3..365f4d0f7 100644 --- a/tasmota/tasmota_support/support_esp32.ino +++ b/tasmota/tasmota_support/support_esp32.ino @@ -501,6 +501,10 @@ uint32_t ESP_getFreeSketchSpace(void) { return ESP.getFreeSketchSpace(); } +uint32_t ESP_getHeapSize(void) { + return ESP.getHeapSize(); +} + uint32_t ESP_getFreeHeap(void) { // ESP_getFreeHeap() returns also IRAM which we don't use return heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); @@ -558,6 +562,18 @@ uint8_t* FlashDirectAccess(void) { return data; } +uint32_t ESP_getPsramSize(void) { + return ESP.getPsramSize(); +} + +uint32_t ESP_getFreePsram(void) { + return ESP.getFreePsram(); +} + +uint32_t ESP_getMaxAllocPsram(void) { + return ESP.getMaxAllocPsram(); +} + extern "C" { #if ESP_IDF_VERSION_MAJOR >= 5 // bool IRAM_ATTR __attribute__((pure)) esp_psram_is_initialized(void) @@ -948,6 +964,18 @@ String GetCodeCores(void) { #endif } +uint32_t ESP_getChipCores(void) { + return ESP.getChipCores(); +} + +uint32_t ESP_getChipRevision(void) { + return ESP.getChipRevision(); +} + +String ESP_getEfuseMac(void) { + return String(ESP.getEfuseMac()); +} + /*********************************************************************************************\ * High entropy hardware random generator * Thanks to DigitalAlchemist diff --git a/tasmota/tasmota_support/support_esp8266.ino b/tasmota/tasmota_support/support_esp8266.ino index 00df1731b..d21184d74 100644 --- a/tasmota/tasmota_support/support_esp8266.ino +++ b/tasmota/tasmota_support/support_esp8266.ino @@ -85,6 +85,10 @@ uint32_t ESP_getSketchSize(void) { return ESP.getSketchSize(); } +uint32_t ESP_getHeapSize(void) { + return 32768; // Using default heap (No PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED) +} + uint32_t ESP_getFreeHeap(void) { return ESP.getFreeHeap(); } @@ -98,6 +102,10 @@ float ESP_getFreeHeap1024(void) { } */ +uint32_t ESP_getMaxAllocHeap(void) { + return ESP.getFreeHeap(); +} + uint32_t ESP_getFlashChipId(void) { return ESP.getFlashChipId(); } @@ -110,6 +118,18 @@ uint32_t ESP_getFlashChipSize(void) { return ESP.getFlashChipSize(); } +uint32_t ESP_getPsramSize(void) { + return 0; +} + +uint32_t ESP_getFreePsram(void) { + return 0; +} + +uint32_t ESP_getMaxAllocPsram(void) { + return 0; +} + void ESP_Restart(void) { // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); @@ -197,6 +217,48 @@ String GetCodeCores(void) { return F(""); } +uint32_t ESP_getChipCores(void) { + return 1; +} + +uint32_t ESP_getChipRevision(void) { + return 1; +} + +String ESP_getEfuseMac(void) { + uint32_t mac0 = *(uint32_t*)(0x3FF00050); + uint32_t mac1 = *(uint32_t*)(0x3FF00054); + uint32_t mac3 = *(uint32_t*)(0x3FF0005C); + + uint32_t mach = 0; + uint32_t macl = 0; + if (mac3 != 0) { + mach = ((mac3 >> 16) & 0xFF) << 16; + mach |= ((mac3 >> 8) & 0xFF) << 8; + mach |= mac3 & 0xFF; + } + else if (((mac1 >> 16) & 0xFF) == 0) { + mach = 0x18FE34; + } + else if (((mac1 >> 16) & 0xFF) == 1) { + mach = 0xACD074; + } + String macStr = ""; + if (mach > 0) { + macl = ((mac1 >> 8) & 0xFF) << 16; + macl |= (mac1 & 0xFF) << 8; + macl |= (mac0 >> 24) & 0xFF; + + uint64_t maca = ((uint64_t)mach << 24) | macl; + // Need uint64ToString with base 10 as ESP8266 WStrings does not support uint64_t + while (maca > 0) { + macStr = String((uint32_t)(maca % 10), 10) + macStr; + maca /= 10; + } + } + return String(macStr); +} + /*********************************************************************************************\ * High entropy hardware random generator * Thanks to DigitalAlchemist diff --git a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino index 96281511c..e7f7508e9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_121_gpioviewer.ino @@ -17,6 +17,15 @@ #define XDRV_121 121 +//#define GV_INPUT_DETECTION // Report type of digital input + +#define GV_USE_ESPINFO // Provide ESP info +#ifdef ESP32 +#ifndef GV_USE_ESPINFO +#define GV_USE_ESPINFO // Provide ESP info +#endif +#endif + #ifndef GV_PORT #define GV_PORT 5557 // SSE webserver port #endif @@ -27,29 +36,15 @@ #define GV_KEEP_ALIVE 1000 // milliseconds - If no activity after this do a heap size event anyway #ifndef GV_BASE_URL -#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_1_5/" #endif -/* -#ifdef ESP8266 -#ifndef GV_BASE_URL -#undef GV_BASE_URL // Fix compiler warning -#define GV_BASE_URL "https://ota.tasmota.com/tasmota/gpio_viewer/assets/" -#endif -#endif // ESP8266 -#ifdef ESP32 -#ifndef GV_BASE_URL -#undef GV_BASE_URL // Fix compiler warning -#define GV_BASE_URL "https://ota.tasmota.com/tasmota32/gpio_viewer/assets/" -#endif -#endif // ESP32 -*/ -const char *GVRelease = "1.0.7"; +const char *GVRelease = "1.5.0"; #ifdef USE_UNISHOX_COMPRESSION - #include "./html_compressed/HTTP_GV_PAGE.h" + #include "./html_compressed/HTTP_GV_PAGE_150.h" #else - #include "./html_uncompressed/HTTP_GV_PAGE.h" + #include "./html_uncompressed/HTTP_GV_PAGE_150.h" #endif const char HTTP_GV_EVENT[] PROGMEM = @@ -62,7 +57,12 @@ const char HTTP_GV_EVENT[] PROGMEM = enum GVPinTypes { GV_DigitalPin = 0, GV_PWMPin = 1, - GV_AnalogPin = 2 + GV_AnalogPin = 2, +#ifdef GV_INPUT_DETECTION + GV_InputPin = 3, + GV_InputPullUp = 4, + GV_InputPullDn = 5 +#endif // GV_INPUT_DETECTION }; struct { @@ -78,6 +78,26 @@ struct { bool sse_ready; } GV; +#ifdef GV_INPUT_DETECTION + +int GetPinMode(uint8_t pin) { +#ifdef ESP8266 + if (pin > MAX_GPIO_PIN -2) { return -1; } // Skip GPIO16 and Analog0 +#endif // ESP8266 +#ifdef ESP32 + if (pin > MAX_GPIO_PIN) { return -1; } +#endif // ESP32 + + uint32_t bit = digitalPinToBitMask(pin); + uint32_t port = digitalPinToPort(pin); + volatile uint32_t *reg = portModeRegister(port); + if (*reg & bit) { return OUTPUT; } // ESP8266 = 0x01, ESP32 = 0x03 + volatile uint32_t *out = portOutputRegister(port); + return ((*out & bit) ? INPUT_PULLUP : INPUT); // ESP8266 = 0x02 : 0x00, ESP32 = 0x05 : x01 +} + +#endif // GV_INPUT_DETECTION + void GVStop(void) { GV.sse_ready = false; GV.ticker.detach(); @@ -100,6 +120,9 @@ void GVBegin(void) { 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(); @@ -112,24 +135,21 @@ void GVHandleRoot(void) { char* content = ext_snprintf_malloc_P(HTTP_GV_PAGE, SettingsTextEscaped(SET_DEVICENAME).c_str(), GV_BASE_URL, - GV_PORT, WiFi.localIP().toString().c_str(), - WiFi.localIP().toString().c_str(), GV_PORT, - GV.sampling, -#ifdef ESP32 - ESP.getPsramSize() / 1024, -#else - 0, -#endif // ESP32 + GV_PORT, ESP_getFreeSketchSpace() / 1024); if (content == nullptr) { return; } // Avoid crash GV.WebServer->send_P(200, "text/html", content); free(content); } +void GVWebserverSendJson(String &jsonResponse) { + GV.WebServer->send(200, "application/json", jsonResponse); +} + void GVHandleRelease(void) { String jsonResponse = "{\"release\":\"" + String(GVRelease) + "\"}"; - GV.WebServer->send(200, "application/json", jsonResponse); + GVWebserverSendJson(jsonResponse); } void GVHandleFreePSRam(void) { @@ -140,7 +160,85 @@ void GVHandleFreePSRam(void) { } else #endif jsonResponse += "No PSRAM\"}"; - GV.WebServer->send(200, "application/json", jsonResponse); + GVWebserverSendJson(jsonResponse); +} + +void GVHandleSampling(void) { + String jsonResponse = "{\"sampling\": \"" + String(GV.sampling) + "\"}"; + GVWebserverSendJson(jsonResponse); +} + +void GVHandleEspInfo(void) { +#ifdef GV_USE_ESPINFO + const FlashMode_t flashMode = ESP.getFlashChipMode(); // enum + + String jsonResponse = "{\"chip_model\":\"" + GetDeviceHardware(); + jsonResponse += "\",\"cores_count\":\"" + String(ESP_getChipCores()); + jsonResponse += "\",\"chip_revision\":\"" + String(ESP_getChipRevision()); + jsonResponse += "\",\"cpu_frequency\":\"" + String(ESP.getCpuFreqMHz()); + jsonResponse += "\",\"cycle_count\":" + String(ESP.getCycleCount()); + jsonResponse += ",\"mac\":\"" + ESP_getEfuseMac(); + jsonResponse += "\",\"flash_mode\":" + String(flashMode); +#ifdef ESP8266 + jsonResponse += ",\"flash_chip_size\":" + String(ESP.getFlashChipRealSize()); +#else + jsonResponse += ",\"flash_chip_size\":" + String(ESP.getFlashChipSize()); +#endif + jsonResponse += ",\"flash_chip_speed\":" + String(ESP.getFlashChipSpeed()); + jsonResponse += ",\"heap_size\":" + String(ESP_getHeapSize()); + jsonResponse += ",\"heap_max_alloc\":" + String(ESP_getMaxAllocHeap()); + jsonResponse += ",\"psram_size\":" + String(ESP_getPsramSize()); + jsonResponse += ",\"free_psram\":" + String(ESP_getFreePsram()); + jsonResponse += ",\"psram_max_alloc\":" + String(ESP_getMaxAllocPsram()); + jsonResponse += ",\"free_heap\":" + String(ESP_getFreeHeap()); + jsonResponse += ",\"up_time\":\"" + String(millis()); + jsonResponse += "\",\"sketch_size\":" + String(ESP_getSketchSize()); + jsonResponse += ",\"free_sketch\":" + String(ESP_getFreeSketchSpace()); + jsonResponse += "}"; +#else + String jsonResponse = "{\"chip_model\":\"" + GetDeviceHardware() + "\"}"; +#endif // GV_USE_ESPINFO + GVWebserverSendJson(jsonResponse); +} + +void GVHandlePartition(void) { + String jsonResponse = "["; // Start of JSON array +#ifdef ESP32 + bool firstEntry = true; // Used to format the JSON array correctly + + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); +// esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + + // Loop through partitions + while (iter != NULL) { + const esp_partition_t *partition = esp_partition_get(iter); + + // Add comma before the next entry if it's not the first + if (!firstEntry) + { + jsonResponse += ","; + } + firstEntry = false; + + // Append partition information in JSON format + jsonResponse += "{"; + jsonResponse += "\"label\":\"" + String(partition->label) + "\","; + jsonResponse += "\"type\":" + String(partition->type) + ","; + jsonResponse += "\"subtype\":" + String(partition->subtype) + ","; + jsonResponse += "\"address\":\"0x" + String(partition->address, HEX) + "\","; + jsonResponse += "\"size\":" + String(partition->size); + jsonResponse += "}"; + + // Move to next partition + iter = esp_partition_next(iter); + } + + // Clean up the iterator + esp_partition_iterator_release(iter); +#endif // ESP32 + + jsonResponse += "]"; // End of JSON array + GVWebserverSendJson(jsonResponse); } void GVHandleEvents(void) { @@ -240,7 +338,6 @@ void GVMonitorTask(void) { else { // Read digital GPIO - pintype = GV_DigitalPin; int value = digitalRead(pin); originalValue = value; if (value == 1) { @@ -248,6 +345,12 @@ void GVMonitorTask(void) { // } else { // currentState = 0; } +#ifdef GV_INPUT_DETECTION + int pin_mode = GetPinMode(pin); + pintype = (INPUT == pin_mode) ? GV_InputPin : (INPUT_PULLUP == pin_mode) ? GV_InputPullUp : GV_DigitalPin; +#else + pintype = GV_DigitalPin; +#endif } if (originalValue != GV.lastPinStates[pin]) {