From c9d0d0360f0c360e100d6709f1aa3af5e3d8b9a2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <67171972+IoTManagerProject@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:10:58 +0100 Subject: [PATCH 01/14] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=20=D0=BD=D0=B0=20=D0=B0=D1=81=D0=B8=D0=BD=D1=85=D1=80?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D1=8B=D0=B9=20ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_svelte/items.json | 192 ++++++---------------- include/Const.h | 4 +- myProfile.json | 22 ++- platformio.ini | 11 +- src/modules/API.cpp | 10 -- src/modules/display/Ws2812b/modinfo.json | 67 ++++---- src/modules/sensors/GY21/modinfo.json | 2 +- src/modules/sensors/Hdc1080/modinfo.json | 2 +- src/modules/sensors/Max6675/modinfo.json | 2 +- src/modules/sensors/RCswitch/modinfo.json | 45 ++--- 10 files changed, 132 insertions(+), 225 deletions(-) diff --git a/data_svelte/items.json b/data_svelte/items.json index 218963dd..19c723b2 100644 --- a/data_svelte/items.json +++ b/data_svelte/items.json @@ -333,76 +333,7 @@ }, { "global": 0, - "name": "23. GY21 Температура", - "type": "Reading", - "subtype": "GY21t", - "id": "tmp4", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Температура", - "round": 1, - "int": 15, - "num": 23 - }, - { - "global": 0, - "name": "24. GY21 Влажность", - "type": "Reading", - "subtype": "GY21h", - "id": "Hum4", - "widget": "anydataHum", - "page": "Сенсоры", - "descr": "Влажность", - "round": 1, - "int": 15, - "num": 24 - }, - { - "global": 0, - "name": "25. HDC1080 Температура", - "type": "Reading", - "subtype": "Hdc1080t", - "id": "Temp1080", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "1080 Температура", - "int": 15, - "addr": "0x40", - "round": 1, - "num": 25 - }, - { - "global": 0, - "name": "26. HDC1080 Влажность", - "type": "Reading", - "subtype": "Hdc1080h", - "id": "Hum1080", - "widget": "anydataHum", - "page": "Сенсоры", - "descr": "1080 Влажность", - "int": 15, - "addr": "0x40", - "round": 1, - "num": 26 - }, - { - "global": 0, - "name": "27. MAX6675 Температура", - "type": "Reading", - "subtype": "Max6675t", - "id": "maxtmp", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "MAX Температура", - "int": 15, - "DO": 12, - "CS": 13, - "CLK": 14, - "num": 27 - }, - { - "global": 0, - "name": "28. PZEM 004t Напряжение", + "name": "23. PZEM 004t Напряжение", "type": "Reading", "subtype": "Pzem004v", "id": "v", @@ -412,11 +343,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 28 + "num": 23 }, { "global": 0, - "name": "29. PZEM 004t Сила тока", + "name": "24. PZEM 004t Сила тока", "type": "Reading", "subtype": "Pzem004a", "id": "a", @@ -426,11 +357,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 29 + "num": 24 }, { "global": 0, - "name": "30. PZEM 004t Мощность", + "name": "25. PZEM 004t Мощность", "type": "Reading", "subtype": "Pzem004w", "id": "w", @@ -440,11 +371,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 30 + "num": 25 }, { "global": 0, - "name": "31. PZEM 004t Энергия", + "name": "26. PZEM 004t Энергия", "type": "Reading", "subtype": "Pzem004wh", "id": "wh", @@ -454,11 +385,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 31 + "num": 26 }, { "global": 0, - "name": "32. PZEM 004t Частота", + "name": "27. PZEM 004t Частота", "type": "Reading", "subtype": "Pzem004hz", "id": "hz", @@ -468,11 +399,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 32 + "num": 27 }, { "global": 0, - "name": "33. PZEM 004t Косинус", + "name": "28. PZEM 004t Косинус", "type": "Reading", "subtype": "Pzem004pf", "id": "pf", @@ -482,22 +413,11 @@ "int": 15, "addr": "0xF8", "round": 1, - "num": 33 + "num": 28 }, { "global": 0, - "name": "34. Сканер кнопок 433 MHz", - "num": 34, - "type": "Reading", - "subtype": "RCswitch", - "id": "rsw", - "int": 500, - "pinRx": 12, - "pinTx": 12 - }, - { - "global": 0, - "name": "35. Sht20 Температура", + "name": "29. Sht20 Температура", "type": "Reading", "subtype": "Sht20t", "id": "tmp2", @@ -506,11 +426,11 @@ "descr": "Температура", "int": 15, "round": 1, - "num": 35 + "num": 29 }, { "global": 0, - "name": "36. Sht20 Влажность", + "name": "30. Sht20 Влажность", "type": "Reading", "subtype": "Sht20h", "id": "Hum2", @@ -519,11 +439,11 @@ "descr": "Влажность", "int": 15, "round": 1, - "num": 36 + "num": 30 }, { "global": 0, - "name": "37. Sht30 Температура", + "name": "31. Sht30 Температура", "type": "Reading", "subtype": "Sht30t", "id": "tmp30", @@ -532,11 +452,11 @@ "descr": "SHT30 Температура", "int": 15, "round": 1, - "num": 37 + "num": 31 }, { "global": 0, - "name": "38. Sht30 Влажность", + "name": "32. Sht30 Влажность", "type": "Reading", "subtype": "Sht30h", "id": "Hum30", @@ -545,12 +465,12 @@ "descr": "SHT30 Влажность", "int": 15, "round": 1, - "num": 38 + "num": 32 }, { "global": 0, - "name": "39. HC-SR04 Ультразвуковой дальномер", - "num": 39, + "name": "33. HC-SR04 Ультразвуковой дальномер", + "num": 33, "type": "Reading", "subtype": "Sonar", "id": "sonar", @@ -562,7 +482,7 @@ "int": 5 }, { - "name": "40. UART", + "name": "34. UART", "type": "Reading", "subtype": "UART", "page": "", @@ -574,14 +494,14 @@ "line": 2, "speed": 9600, "eventFormat": 0, - "num": 40 + "num": 34 }, { "header": "Исполнительные устройства" }, { "global": 0, - "name": "41. Кнопка подключенная к пину", + "name": "35. Кнопка подключенная к пину", "type": "Writing", "subtype": "ButtonIn", "id": "btn", @@ -595,11 +515,11 @@ "pinMode": "INPUT", "debounceDelay": 50, "fixState": 0, - "num": 41 + "num": 35 }, { "global": 0, - "name": "42. Управление пином", + "name": "36. Управление пином", "type": "Writing", "subtype": "ButtonOut", "needSave": 0, @@ -610,11 +530,11 @@ "int": 0, "inv": 0, "pin": 2, - "num": 42 + "num": 36 }, { "global": 0, - "name": "43. Сервопривод", + "name": "37. Сервопривод", "type": "Writing", "subtype": "IoTServo", "id": "servo", @@ -625,11 +545,11 @@ "pin": 12, "apin": -1, "amap": "0, 4096, 0, 180", - "num": 43 + "num": 37 }, { "global": 0, - "name": "44. Расширитель портов Mcp23017", + "name": "38. Расширитель портов Mcp23017", "type": "Reading", "subtype": "Mcp23017", "id": "Mcp", @@ -639,11 +559,11 @@ "int": "0", "addr": "0x20", "index": 1, - "num": 44 + "num": 38 }, { "global": 0, - "name": "45. MP3 плеер", + "name": "39. MP3 плеер", "type": "Reading", "subtype": "Mp3", "id": "mp3", @@ -653,11 +573,11 @@ "int": 1, "pins": "14,12", "volume": 20, - "num": 45 + "num": 39 }, { "global": 0, - "name": "46. Сенсорная кнопка", + "name": "40. Сенсорная кнопка", "type": "Writing", "subtype": "Multitouch", "id": "impulse", @@ -671,11 +591,11 @@ "pinMode": "INPUT", "debounceDelay": 50, "PWMDelay": 500, - "num": 46 + "num": 40 }, { "global": 0, - "name": "47. Расширитель портов Pcf8574", + "name": "41. Расширитель портов Pcf8574", "type": "Reading", "subtype": "Pcf8574", "id": "Pcf", @@ -685,11 +605,11 @@ "int": "0", "addr": "0x20", "index": 1, - "num": 47 + "num": 41 }, { "global": 0, - "name": "48. PWM ESP8266", + "name": "42. PWM ESP8266", "type": "Writing", "subtype": "Pwm8266", "id": "pwm", @@ -701,11 +621,11 @@ "freq": 5000, "val": 0, "apin": -1, - "num": 48 + "num": 42 }, { "global": 0, - "name": "49. Телеграм-Лайт", + "name": "43. Телеграм-Лайт", "type": "Writing", "subtype": "TelegramLT", "id": "tg", @@ -714,14 +634,14 @@ "descr": "", "token": "", "chatID": "", - "num": 49 + "num": 43 }, { "header": "Экраны" }, { "global": 0, - "name": "50. LCD экран 2004", + "name": "44. LCD экран 2004", "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", @@ -733,10 +653,10 @@ "size": "20,4", "coord": "0,0", "id2show": "id датчика", - "num": 50 + "num": 44 }, { - "name": "51. LCD экран 1602", + "name": "45. LCD экран 1602", "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", @@ -748,26 +668,6 @@ "size": "16,2", "coord": "0,0", "id2show": "id датчика", - "num": 51 - }, - { - "global": 0, - "name": "52. Strip ws2812b", - "type": "Reading", - "subtype": "Ws2812b", - "id": "strip", - "widget": "range", - "page": "Кнопки", - "descr": "Лента", - "int": 15, - "needSave": 0, - "pin": "4", - "numLeds": "8", - "brightness": "100", - "mode": "1", - "min": "15", - "max": "30", - "idshow": "t", - "num": 52 + "num": 45 } ] \ No newline at end of file diff --git a/include/Const.h b/include/Const.h index ba4cd200..0f75a5bc 100644 --- a/include/Const.h +++ b/include/Const.h @@ -26,9 +26,9 @@ //#define LOOP_DEBUG //выбор сервера -//#define ASYNC_WEB_SERVER +#define ASYNC_WEB_SERVER //#define ASYNC_WEB_SOCKETS -#define STANDARD_WEB_SERVER +//#define STANDARD_WEB_SERVER #define STANDARD_WEB_SOCKETS #define UDP_ENABLED diff --git a/myProfile.json b/myProfile.json index 132010d9..ee9f18ed 100644 --- a/myProfile.json +++ b/myProfile.json @@ -101,11 +101,19 @@ }, { "path": "src/modules/sensors/GY21", - "active": true + "active": false }, { "path": "src/modules/sensors/Hdc1080", - "active": true + "active": false + }, + { + "path": "src/modules/sensors/Hx710", + "active": false + }, + { + "path": "src/modules/sensors/Hx711", + "active": false }, { "path": "src/modules/sensors/Ina219", @@ -117,7 +125,7 @@ }, { "path": "src/modules/sensors/Max6675", - "active": true + "active": false }, { "path": "src/modules/sensors/Mhz19", @@ -129,12 +137,16 @@ }, { "path": "src/modules/sensors/RCswitch", - "active": true + "active": false }, { "path": "src/modules/sensors/Sds011", "active": false }, + { + "path": "src/modules/sensors/Sgp30", + "active": false + }, { "path": "src/modules/sensors/Sht20", "active": true @@ -217,7 +229,7 @@ }, { "path": "src/modules/display/Ws2812b", - "active": true + "active": false } ] } diff --git a/platformio.ini b/platformio.ini index 3a5c9816..fe866f3c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -90,6 +90,7 @@ data_dir = data_svelte lib_deps_external = bblanchon/ArduinoJson @6.18.0 knolleary/PubSubClient + me-no-dev/ESP Async WebServer [env:esp8266_1mb_ota_fromitems] lib_deps = @@ -144,10 +145,6 @@ lib_deps = adafruit/Adafruit BMP280 Library beegee-tokyo/DHT sensor library for ESPx milesburton/DallasTemperature@^3.9.1 - https://github.com/JonasGMorsch/GY-21.git - ClosedCube HDC1080 - adafruit/MAX6675 library - rc-switch @ ^2.6.4 robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 plerup/espsoftwareserial @@ -156,7 +153,6 @@ lib_deps = dfrobot/DFRobotDFPlayerMini @ ^1.0.5 adafruit/Adafruit BusIO @ ^1.13.2 marcoschwartz/LiquidCrystal_I2C@^1.1.4 - adafruit/Adafruit NeoPixel@^1.10.6 build_src_filter = + + @@ -171,11 +167,7 @@ build_src_filter = + + + - + - + - + + - + + + + @@ -190,7 +182,6 @@ build_src_filter = + + + - + [env:esp32_4mb_fromitems] lib_deps = diff --git a/src/modules/API.cpp b/src/modules/API.cpp index 2935ae36..12df2c22 100644 --- a/src/modules/API.cpp +++ b/src/modules/API.cpp @@ -13,11 +13,7 @@ void* getAPI_Bme280(String subtype, String params); void* getAPI_Bmp280(String subtype, String params); void* getAPI_Dht1122(String subtype, String params); void* getAPI_Ds18b20(String subtype, String params); -void* getAPI_GY21(String subtype, String params); -void* getAPI_Hdc1080(String subtype, String params); -void* getAPI_Max6675(String subtype, String params); void* getAPI_Pzem004(String subtype, String params); -void* getAPI_RCswitch(String subtype, String params); void* getAPI_Sht20(String subtype, String params); void* getAPI_Sht30(String subtype, String params); void* getAPI_Sonar(String subtype, String params); @@ -32,7 +28,6 @@ void* getAPI_Pcf8574(String subtype, String params); void* getAPI_Pwm8266(String subtype, String params); void* getAPI_TelegramLT(String subtype, String params); void* getAPI_Lcd2004(String subtype, String params); -void* getAPI_Ws2812b(String subtype, String params); void* getAPI(String subtype, String params) { void* tmpAPI; @@ -49,11 +44,7 @@ if ((tmpAPI = getAPI_Bme280(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Bmp280(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Dht1122(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Ds18b20(subtype, params)) != nullptr) return tmpAPI; -if ((tmpAPI = getAPI_GY21(subtype, params)) != nullptr) return tmpAPI; -if ((tmpAPI = getAPI_Hdc1080(subtype, params)) != nullptr) return tmpAPI; -if ((tmpAPI = getAPI_Max6675(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Pzem004(subtype, params)) != nullptr) return tmpAPI; -if ((tmpAPI = getAPI_RCswitch(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Sht20(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Sht30(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Sonar(subtype, params)) != nullptr) return tmpAPI; @@ -68,6 +59,5 @@ if ((tmpAPI = getAPI_Pcf8574(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Pwm8266(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_TelegramLT(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Lcd2004(subtype, params)) != nullptr) return tmpAPI; -if ((tmpAPI = getAPI_Ws2812b(subtype, params)) != nullptr) return tmpAPI; return nullptr; } \ No newline at end of file diff --git a/src/modules/display/Ws2812b/modinfo.json b/src/modules/display/Ws2812b/modinfo.json index b7565646..6ff7dd56 100644 --- a/src/modules/display/Ws2812b/modinfo.json +++ b/src/modules/display/Ws2812b/modinfo.json @@ -1,26 +1,26 @@ -{ +{ "menuSection": "Экраны", "configItem": [ - { - "global": 0, - "name": "Strip ws2812b", - "type": "Reading", - "subtype": "Ws2812b", - "id": "strip", - "widget": "range", - "page": "Кнопки", - "descr": "Лента", - "int": 15, - "needSave": 0, - "pin": "4", - "numLeds": "8", - "brightness": "100", - "mode": "1", - "min": "15", - "max": "30", - "idshow": "t" - }], - + { + "global": 0, + "name": "Strip ws2812b", + "type": "Reading", + "subtype": "Ws2812b", + "id": "strip", + "widget": "range", + "page": "Кнопки", + "descr": "Лента", + "int": 15, + "needSave": 0, + "pin": "4", + "numLeds": "8", + "brightness": "100", + "mode": "1", + "min": "15", + "max": "30", + "idshow": "t" + } + ], "about": { "authorName": "Yuriy Kuneev", "authorContact": "https://t.me/Kuneev07", @@ -44,7 +44,9 @@ { "name": "noShow", "descr": "Выключить ленту", - "params": ["номер пикселя"] + "params": [ + "номер пикселя" + ] }, { "name": "noShowOne", @@ -59,17 +61,26 @@ { "name": "showLed", "descr": "Зажечь один диод", - "params": ["номер пикселя","цвет 255,255,255 или red,green"] + "params": [ + "номер пикселя", + "цвет 255,255,255 или red,green" + ] }, { "name": "showLedAll", "descr": "Зажечь все диоды", - "params": ["Цвет красного светодиода от 0 до 255","Цвет зеленого светодиода от 0 до 255","Цвет синего светодиода от 0 до 255"] + "params": [ + "Цвет красного светодиода от 0 до 255", + "Цвет зеленого светодиода от 0 до 255", + "Цвет синего светодиода от 0 до 255" + ] }, { "name": "Brightness", "descr": "Устанавливает общую яркость ленты от 0 до 255", - "params": ["яркость от 0 до 255"] + "params": [ + "яркость от 0 до 255" + ] }, { "name": "enableIndication", @@ -83,15 +94,13 @@ } ] }, - - "defActive": true, - + "defActive": false, "usedLibs": { "esp32_4mb": [ "adafruit/Adafruit NeoPixel@^1.10.6" ], "esp8266_4mb": [ - "adafruit/Adafruit NeoPixel@^1.10.6" + "adafruit/Adafruit NeoPixel@^1.10.6" ] } } \ No newline at end of file diff --git a/src/modules/sensors/GY21/modinfo.json b/src/modules/sensors/GY21/modinfo.json index 7878e037..affd1e44 100644 --- a/src/modules/sensors/GY21/modinfo.json +++ b/src/modules/sensors/GY21/modinfo.json @@ -47,7 +47,7 @@ "int": "Количество секунд между опросами датчика." } }, - "defActive": true, + "defActive": false, "usedLibs": { "esp32_4mb": [ "https://github.com/JonasGMorsch/GY-21.git" diff --git a/src/modules/sensors/Hdc1080/modinfo.json b/src/modules/sensors/Hdc1080/modinfo.json index 3250c7a9..5b27c795 100644 --- a/src/modules/sensors/Hdc1080/modinfo.json +++ b/src/modules/sensors/Hdc1080/modinfo.json @@ -50,7 +50,7 @@ "int": "Количество секунд между опросами датчика." } }, - "defActive": true, + "defActive": false, "usedLibs": { "esp32_4mb": [ "ClosedCube HDC1080" diff --git a/src/modules/sensors/Max6675/modinfo.json b/src/modules/sensors/Max6675/modinfo.json index 2523a69d..69a61a32 100644 --- a/src/modules/sensors/Max6675/modinfo.json +++ b/src/modules/sensors/Max6675/modinfo.json @@ -36,7 +36,7 @@ "int": "Количество секунд между опросами датчика." } }, - "defActive": true, + "defActive": false, "usedLibs": { "esp32_4mb": [ "adafruit/MAX6675 library" diff --git a/src/modules/sensors/RCswitch/modinfo.json b/src/modules/sensors/RCswitch/modinfo.json index f5a6dc97..3e307a7a 100644 --- a/src/modules/sensors/RCswitch/modinfo.json +++ b/src/modules/sensors/RCswitch/modinfo.json @@ -1,18 +1,18 @@ -{ +{ "menuSection": "Сенсоры", - - "configItem": [{ - "global": 0, - "name": "Сканер кнопок 433 MHz", - "num": 31, - "type": "Reading", - "subtype": "RCswitch", - "id": "rsw", - "int": 500, - "pinRx": 12, - "pinTx": 12 - }], - + "configItem": [ + { + "global": 0, + "name": "Сканер кнопок 433 MHz", + "num": 31, + "type": "Reading", + "subtype": "RCswitch", + "id": "rsw", + "int": 500, + "pinRx": 12, + "pinTx": 12 + } + ], "about": { "authorName": "Serghei Crasnicov", "authorContact": "https://t.me/Serghei63", @@ -36,23 +36,28 @@ { "name": "sendBitStr", "descr": "Отправляем строку вида 000000000001010100010001", - "params": ["Строка 000000000001010100010001"] + "params": [ + "Строка 000000000001010100010001" + ] }, { "name": "sendTriState", "descr": "отправляем строку вида 00000FFF0F0F", - "params": ["Строка 00000FFF0F0F"] + "params": [ + "Строка 00000FFF0F0F" + ] }, { "name": "sendDecimal", "descr": "отправляем строку вида 5393", - "params": ["Код в виде числа", "Количество бит чтоб заполнить нулями"] + "params": [ + "Код в виде числа", + "Количество бит чтоб заполнить нулями" + ] } ] }, - - "defActive": true, - + "defActive": false, "usedLibs": { "esp32_4mb": [ "rc-switch @ ^2.6.4" From 863103b3f991f081b01cbfeaffa8f5bb195ae187 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <67171972+IoTManagerProject@users.noreply.github.com> Date: Wed, 23 Nov 2022 19:41:49 +0100 Subject: [PATCH 02/14] =?UTF-8?q?=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0?= =?UTF-8?q?=D1=82=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Const.h | 6 +++--- platformio.ini | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/Const.h b/include/Const.h index 0f75a5bc..34884dc4 100644 --- a/include/Const.h +++ b/include/Const.h @@ -20,15 +20,15 @@ #endif //Размер буфера json -#define JSON_BUFFER_SIZE 2048 +#define JSON_BUFFER_SIZE 2048 //держим 2 кб не меняем #define WEB_SOCKETS_FRAME_SIZE 2048 //#define LOOP_DEBUG //выбор сервера -#define ASYNC_WEB_SERVER +//#define ASYNC_WEB_SERVER //#define ASYNC_WEB_SOCKETS -//#define STANDARD_WEB_SERVER +#define STANDARD_WEB_SERVER #define STANDARD_WEB_SOCKETS #define UDP_ENABLED diff --git a/platformio.ini b/platformio.ini index fe866f3c..a4858dc6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -90,7 +90,6 @@ data_dir = data_svelte lib_deps_external = bblanchon/ArduinoJson @6.18.0 knolleary/PubSubClient - me-no-dev/ESP Async WebServer [env:esp8266_1mb_ota_fromitems] lib_deps = From 8d5d284e02508114614ebc8c15bc32cbfe86c920 Mon Sep 17 00:00:00 2001 From: biver Date: Wed, 23 Nov 2022 21:51:13 +0300 Subject: [PATCH 03/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=B2=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B8=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B5=D1=80=D1=8B=D0=B2=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=20=D0=B4=D0=BB=D1=8F=20=D0=BE=D1=82=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B0=20=D1=81=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D0=BC=D0=BD=D1=8B=D0=B5=20=D0=BD=D1=83=D0=B6?= =?UTF-8?q?=D0=B4=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/classes/IoTScenario.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 39d4954a..0d30e477 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -4,6 +4,16 @@ #include "utils/FileUtils.h" #include "NTP.h" + +// вызываем все нужное пока зависаем в сценариях +void scenario_yield() { + yield(); + + #ifdef STANDARD_WEB_SERVER + HTTP.handleClient(); + #endif +} + bool isIotScenException = false; // признак исключения и попытки прекратить выполнение сценария заранее // Лексический анализатор возвращает токены [0-255], если это неизвестны, @@ -573,6 +583,9 @@ class IfExprAST : public ExprAST { // else if (res_ret->isDecimal) Serial.printf("Call from IfExprAST: Cond result = %f, result = %f\n", cond_ret->valD, res_ret->valD); // else Serial.printf("Call from IfExprAST: Cond result = %f, result = %s\n", cond_ret->valD, res_ret->valS.c_str()); //Serial.printf("\n"); + + scenario_yield(); + return cond_ret; } @@ -762,6 +775,7 @@ int IoTScenario::gettok() { /// токен, просматриваемый парсером. getNextToken получает следующий токен от /// лексического анализатора и обновляет CurTok. int IoTScenario::getNextToken() { + scenario_yield(); return CurTok = gettok(); } From 0d2fe420f5a9795996bcbe34ae3208e58eb9904b Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <67171972+IoTManagerProject@users.noreply.github.com> Date: Wed, 23 Nov 2022 19:52:00 +0100 Subject: [PATCH 04/14] =?UTF-8?q?=D0=B1=D0=B0=D0=B3=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=BE=D0=B3=D0=BE=20=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_svelte/layout.json | 1 + data_svelte_lite/layout.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 data_svelte/layout.json create mode 100644 data_svelte_lite/layout.json diff --git a/data_svelte/layout.json b/data_svelte/layout.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/data_svelte/layout.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/data_svelte_lite/layout.json b/data_svelte_lite/layout.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/data_svelte_lite/layout.json @@ -0,0 +1 @@ +[] \ No newline at end of file From 1a575e0f615dc10856b09bc692d7e9b11874366a Mon Sep 17 00:00:00 2001 From: biver Date: Fri, 25 Nov 2022 12:31:40 +0300 Subject: [PATCH 05/14] =?UTF-8?q?=D0=A3=D0=BC=D0=B5=D0=BD=D1=8C=D1=88?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=20=D1=87=D0=B8=D1=81=D0=BB=D0=BE=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B2=D0=BB=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0=D1=80=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=83=D0=B2=D0=B5=D0=BB=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D0=BA=D0=BE=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/classes/IoTScenario.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 0d30e477..0140a158 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -775,7 +775,6 @@ int IoTScenario::gettok() { /// токен, просматриваемый парсером. getNextToken получает следующий токен от /// лексического анализатора и обновляет CurTok. int IoTScenario::getNextToken() { - scenario_yield(); return CurTok = gettok(); } From 1f8bb4aa6e56cd8cff360011fd01607a621032be Mon Sep 17 00:00:00 2001 From: biver Date: Fri, 25 Nov 2022 12:32:20 +0300 Subject: [PATCH 06/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=B2=20=D0=BF=D0=B0=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=80=D1=83=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=B7=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/virtual/Variable/modinfo.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/modules/virtual/Variable/modinfo.json b/src/modules/virtual/Variable/modinfo.json index 85f957b4..9b01d4d8 100644 --- a/src/modules/virtual/Variable/modinfo.json +++ b/src/modules/virtual/Variable/modinfo.json @@ -56,6 +56,23 @@ "descr": "Введите текст", "int": "0", "val": "текст" + }, + { + "global": 0, + "name": "Вывод значения", + "type": "Reading", + "subtype": "Variable", + "id": "vout", + "needSave": 0, + "widget": "anydataDef", + "page": "Вывод", + "descr": "Значение", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 } ], "about": { From 5dce817580abbe13089ff907e4e420360488268d Mon Sep 17 00:00:00 2001 From: biver Date: Fri, 25 Nov 2022 13:29:48 +0300 Subject: [PATCH 07/14] =?UTF-8?q?=D0=9E=D0=BF=D1=82=D0=B8=D0=BC=D0=B8?= =?UTF-8?q?=D0=B7=D0=B8=D1=80=D1=83=D0=B5=D0=BC=20=D0=BD=D0=B5=D0=BA=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D1=83=D1=8E=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D1=83=20=D1=81=D0=BE=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/classes/IoTScenario.h | 2 +- src/classes/IoTScenario.cpp | 54 +++++++++++++++++------------------ src/utils/SerialPrint.cpp | 4 +-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/classes/IoTScenario.h b/include/classes/IoTScenario.h index c1a6cf83..bee35bb0 100644 --- a/include/classes/IoTScenario.h +++ b/include/classes/IoTScenario.h @@ -51,7 +51,7 @@ class IoTScenario { int GetTokPrecedence(); /// Error* - Это небольшие вспомогательные функции для обработки ошибок. - ExprAST *Error(const char *Str); + ExprAST *Error(const String& Str); /// identifierexpr /// ::= identifier diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 0140a158..8173ffb9 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -296,7 +296,7 @@ class CallExprAST : public ExprAST { //if (!ItemIsLocal) Item = findIoTItem(Callee); // пробуем найти переменную если она не локальная (могла придти по сети в процессе) if (!Item) return nullptr; // ret = zeroIotVal; // если все же не пришла, то либо опечатка, либо уже стерлась - выходим - if (Cmd == "getIntFromNet") { + if (Cmd == F("getIntFromNet")) { ret.valD = Item->getIntFromNet(); ret.isDecimal = true; return &ret; @@ -467,43 +467,43 @@ class SysCallExprAST : public ExprAST { public: SysCallExprAST(const String &callee, std::vector &args) : Callee(callee), Args(args) { - if (Callee == "reboot") + if (Callee == F("reboot")) operation = sysop_reboot; - else if (Callee == "digitalRead") + else if (Callee == F("digitalRead")) operation = sysop_digitalRead; - else if (Callee == "analogRead") + else if (Callee == F("analogRead")) operation = sysop_analogRead; - else if (Callee == "digitalWrite") + else if (Callee == F("digitalWrite")) operation = sysop_digitalWrite; - else if (Callee == "digitalInvert") + else if (Callee == F("digitalInvert")) operation = sysop_digitalInvert; - else if (Callee == "deepSleep") + else if (Callee == F("deepSleep")) operation = sysop_deepSleep; - else if (Callee == "getTime") + else if (Callee == F("getTime")) operation = sysop_getTime; - else if (Callee == "getHours") + else if (Callee == F("getHours")) operation = sysop_getHours; - else if (Callee == "getMinutes") + else if (Callee == F("getMinutes")) operation = sysop_getMinutes; - else if (Callee == "getSeconds") + else if (Callee == F("getSeconds")) operation = sysop_getSeconds; - else if (Callee == "getMonth") + else if (Callee == F("getMonth")) operation = sysop_getMonth; - else if (Callee == "getDay") + else if (Callee == F("getDay")) operation = sysop_getDay; - else if (Callee == "getRSSI") + else if (Callee == F("getRSSI")) operation = sysop_getRSSI; - else if (Callee == "getIP") + else if (Callee == F("getIP")) operation = sysop_getIP; - else if (Callee == "mqttPub") + else if (Callee == F("mqttPub")) operation = sysop_mqttPub; - else if (Callee == "gethhmm") + else if (Callee == F("gethhmm")) operation = sysop_gethhmm; - else if (Callee == "gethhmmss") + else if (Callee == F("gethhmmss")) operation = sysop_gethhmmss; - else if (Callee == "getTime") + else if (Callee == F("getTime")) operation = sysop_getTime; - else if (Callee == "getUptime") + else if (Callee == F("getUptime")) operation = sysop_getUptime; else operation = sysop_notfound; @@ -790,8 +790,8 @@ int IoTScenario::GetTokPrecedence() { } /// Error* - Это небольшие вспомогательные функции для обработки ошибок. -ExprAST *IoTScenario::Error(const char *Str) { - Serial.printf("Scenario error in line %d: %s\n", curLine, Str); +ExprAST *IoTScenario::Error(const String& Str) { + Serial.printf("Scenario error in line %d: %s\n", curLine, Str.c_str()); isIotScenException = true; return nullptr; } @@ -834,7 +834,7 @@ ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames, bool callFromConditio if (CurTok == ')') break; if (CurTok != ',') { - return Error("Expected ')' or ',' in argument list"); + return Error(F("Expected ')' or ',' in argument list")); } getNextToken(); } @@ -891,7 +891,7 @@ ExprAST *IoTScenario::ParseBracketsExpr(String *IDNames, bool callFromCondition) // int ttok = getNextToken(); if (CurTok == tok_eof) { - return Error("Expected '}'"); + return Error(F("Expected '}'")); } } @@ -946,7 +946,7 @@ ExprAST *IoTScenario::ParsePrimary(String *IDNames, bool callFromCondition) { switch (CurTok) { default: Serial.println(CurTok); - return Error("unknown token when expecting an expression"); + return Error(F("unknown token when expecting an expression")); case tok_identifier: { if (callFromCondition && IDNames) { String tmpstr = *IDNames; @@ -1018,13 +1018,13 @@ void IoTScenario::loadScenario(String fileName) { // подготавливае if (file) file.close(); file = FileFS.open(fileName.c_str(), "r"); if (!file) { - Error("Open file scenario error"); + Error(F("Open file scenario error")); return; } } else if (mode == 1) { file = FileFS.open(fileName.c_str(), "r"); if (!file) { - Error("Open file scenario error"); + Error(F("Open file scenario error")); return; } strFromFile = file.readString(); diff --git a/src/utils/SerialPrint.cpp b/src/utils/SerialPrint.cpp index a1e8fde2..8ed907e2 100644 --- a/src/utils/SerialPrint.cpp +++ b/src/utils/SerialPrint.cpp @@ -13,7 +13,7 @@ void SerialPrint(const String& errorLevel, const String& module, const String& m if (isNetworkActive()) { if (jsonReadInt(settingsFlashJson, F("log")) != 0) { - sendStringToWs("corelg", tosend, -1); + sendStringToWs(F("corelg"), tosend, -1); } } @@ -21,7 +21,7 @@ void SerialPrint(const String& errorLevel, const String& module, const String& m cleanString(tosend); // создаем событие об ошибке для возможной реакции в сценарии if (itemId != "") { - createItemFromNet(itemId + "_onError", tosend, -4); + createItemFromNet(itemId + F("_onError"), tosend, -4); } else { // createItemFromNet("onError", tosend, -4); } From ae389b770e9687f1946ab99c25659c98090dc97d Mon Sep 17 00:00:00 2001 From: biver Date: Fri, 25 Nov 2022 13:45:50 +0300 Subject: [PATCH 08/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D1=85=D1=83=D0=BA=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=B0=D0=B3=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B0=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=D0=B7=20Mqtt=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=BB=D1=8E=D0=B1=D0=BE=D0=B3=D0=BE=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D1=83=D0=BB=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/classes/IoTItem.h | 1 + src/MqttClient.cpp | 7 ++++++- src/classes/IoTItem.cpp | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/classes/IoTItem.h b/include/classes/IoTItem.h index c87f925c..890173d5 100644 --- a/include/classes/IoTItem.h +++ b/include/classes/IoTItem.h @@ -51,6 +51,7 @@ class IoTItem { // хуки для системных событий virtual void onRegEvent(IoTItem* item); + virtual void onMqttRecive(char* topic, uint8_t* payload, size_t length); //методы для графиков virtual void publishValue(); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index e86131ce..77a89e6f 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -117,6 +117,11 @@ void mqttSubscribe() { } void mqttCallback(char* topic, uint8_t* payload, size_t length) { + // распространяем принятое сообщение через хуки + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + (*it)->onMqttRecive(topic, payload, length); + } + String topicStr = String(topic); // SerialPrint("i", "=>MQTT", topicStr); String payloadStr; @@ -188,7 +193,7 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { // loadScenario(); // SerialPrint("i", F("=>MQTT"), F("Scenario received")); // } - //} + //} } boolean publish(const String& topic, const String& data) { diff --git a/src/classes/IoTItem.cpp b/src/classes/IoTItem.cpp index 095e6c18..063ae3cb 100644 --- a/src/classes/IoTItem.cpp +++ b/src/classes/IoTItem.cpp @@ -175,6 +175,8 @@ void IoTItem::checkIntFromNet() { void IoTItem::onRegEvent(IoTItem* item) {} +void IoTItem::onMqttRecive(char* topic, uint8_t* payload, size_t length) {} + void IoTItem::publishValue() {} void IoTItem::clearValue() {} From 3d504b01dd881ba6f7092e2dbf02e9f079f83fb2 Mon Sep 17 00:00:00 2001 From: biver Date: Fri, 25 Nov 2022 22:00:33 +0300 Subject: [PATCH 09/14] =?UTF-8?q?=D0=A3=D0=BF=D1=80=D0=BE=D1=89=D0=B0?= =?UTF-8?q?=D0=B5=D0=BC=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D1=81=20?= =?UTF-8?q?=D1=85=D1=83=D0=BA=D0=BE=D0=BC=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20MQTT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/classes/IoTItem.h | 2 +- src/MqttClient.cpp | 12 ++++-------- src/classes/IoTItem.cpp | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/classes/IoTItem.h b/include/classes/IoTItem.h index 890173d5..57d42a22 100644 --- a/include/classes/IoTItem.h +++ b/include/classes/IoTItem.h @@ -51,7 +51,7 @@ class IoTItem { // хуки для системных событий virtual void onRegEvent(IoTItem* item); - virtual void onMqttRecive(char* topic, uint8_t* payload, size_t length); + virtual void onMqttRecive(String& topic, String& msg); //методы для графиков virtual void publishValue(); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 77a89e6f..1811ac46 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -117,11 +117,6 @@ void mqttSubscribe() { } void mqttCallback(char* topic, uint8_t* payload, size_t length) { - // распространяем принятое сообщение через хуки - for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { - (*it)->onMqttRecive(topic, payload, length); - } - String topicStr = String(topic); // SerialPrint("i", "=>MQTT", topicStr); String payloadStr; @@ -130,9 +125,10 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { payloadStr += (char)payload[i]; } - // SerialPrint("i", "=>MQTT", payloadStr); - - // SerialPrint("i", F("=>MQTT"), "Msg from iotmanager: " + topicStr); + // распространяем принятое сообщение через хуки + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + (*it)->onMqttRecive(topicStr, payloadStr); + } if (payloadStr.startsWith("HELLO")) { SerialPrint("i", F("MQTT"), F("Full update")); diff --git a/src/classes/IoTItem.cpp b/src/classes/IoTItem.cpp index 063ae3cb..e8afe357 100644 --- a/src/classes/IoTItem.cpp +++ b/src/classes/IoTItem.cpp @@ -175,7 +175,7 @@ void IoTItem::checkIntFromNet() { void IoTItem::onRegEvent(IoTItem* item) {} -void IoTItem::onMqttRecive(char* topic, uint8_t* payload, size_t length) {} +void IoTItem::onMqttRecive(String& topic, String& msg) {} void IoTItem::publishValue() {} From 374073267c1262e9098071f2bf9e8ee5bd3ebe76 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <67171972+IoTManagerProject@users.noreply.github.com> Date: Sat, 26 Nov 2022 16:19:54 +0100 Subject: [PATCH 10/14] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=B2=D0=B5=D0=B1=204.4.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data_svelte/build/bundle.css.gz | Bin 5499 -> 5499 bytes data_svelte/build/bundle.js.gz | Bin 48408 -> 49497 bytes data_svelte/index.html | 6 +++--- include/Const.h | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data_svelte/build/bundle.css.gz b/data_svelte/build/bundle.css.gz index 7c46e54ff4eed49c8f64c3c1bf1c2bd469faca2a..9cfe2204b837f7977ddd133bd035ad47c6efe3b6 100644 GIT binary patch delta 16 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