diff --git a/data_svelte/items.json b/data_svelte/items.json index 969ff47f..3a180753 100644 --- a/data_svelte/items.json +++ b/data_svelte/items.json @@ -712,7 +712,7 @@ "type": "Writing", "subtype": "IoTServo", "id": "servo", - "widget": "range", + "widget": "rangeServo", "page": "servo", "descr": "угол", "int": 1, diff --git a/data_svelte/myProfile.json b/data_svelte/myProfile.json index 6fc49ac8..c85d76bb 100644 --- a/data_svelte/myProfile.json +++ b/data_svelte/myProfile.json @@ -3,8 +3,14 @@ "name": "IoTmanagerVer4", "apssid": "IoTmanager", "appass": "", - "routerssid": ["iot","wifi"], - "routerpass": ["hostel3333","pswd"], + "routerssid": [ + "iot", + "wifi" + ], + "routerpass": [ + "hostel3333", + "pswd" + ], "timezone": 2, "ntp": "pool.ntp.org", "weblogin": "admin", @@ -406,6 +412,10 @@ "path": "src/modules/display/NextionUpload", "active": false }, + { + "path": "src/modules/display/Oled128", + "active": false + }, { "path": "src/modules/display/Smi2_m", "active": true diff --git a/data_svelte/widgets.json b/data_svelte/widgets.json index ba91a71f..8a758af7 100644 --- a/data_svelte/widgets.json +++ b/data_svelte/widgets.json @@ -246,11 +246,11 @@ "debounce": 500 }, { - "name": "range", + "name": "rangeServo", "label": "Ползунок (Servo)", "widget": "range", "descrColor": "red", - "after": "%", + "after": "°", "k": 1, "min": 0, "max": 180, diff --git a/data_svelte_lite/widgets copy.json b/data_svelte_lite/widgets copy.json deleted file mode 100644 index ba91a71f..00000000 --- a/data_svelte_lite/widgets copy.json +++ /dev/null @@ -1,308 +0,0 @@ -[ - { - "name": "anydataRed", - "label": "Сообщение1", - "widget": "anydata", - "icon": "body", - "color": "red", - "descrColor": "red" - }, - { - "name": "anydataDgr", - "label": "Сообщение2", - "widget": "anydata", - "after": "", - "color": "red", - "icon": "walk" - }, - { - "name": "anydataDef", - "label": "Текст", - "widget": "anydata", - "after": "", - "icon": "" - }, - { - "name": "anydataVlt", - "label": "Вольты", - "widget": "anydata", - "after": "V", - "icon": "speedometer" - }, - { - "name": "anydataAmp", - "label": "Амперы", - "widget": "anydata", - "after": "A", - "icon": "speedometer" - }, - { - "name": "anydataWt", - "label": "Ватты", - "widget": "anydata", - "after": "Wt", - "icon": "speedometer", - "color": [ - { - "level": 0, - "value": "" - }, - { - "level": 200, - "value": "#009933" - }, - { - "level": 2000, - "value": "#FF9900" - }, - { - "level": 4000, - "value": "red" - } - ] - }, - { - "name": "anydataWth", - "label": "Энергия", - "widget": "anydata", - "after": "kWh", - "icon": "speedometer" - }, - { - "name": "anydataHtz", - "label": "Герцы", - "widget": "anydata", - "after": "Hz", - "icon": "speedometer" - }, - { - "name": "anydataTmp", - "label": "Температура", - "widget": "anydata", - "after": "°С", - "icon": "thermometer", - "font": "OCR A Std", - "color": [ - { - "level": -20, - "value": "#0000CC" - }, - { - "level": -10, - "value": "#0000CC" - }, - { - "level": 0, - "value": "#0000CC" - }, - { - "level": 12, - "value": "#3366FF" - }, - { - "level": 16, - "value": "#33CCFF" - }, - { - "level": 18, - "value": "#009933" - }, - { - "level": 30, - "value": "#FF9900" - }, - { - "level": 40, - "value": "red" - } - ] - }, - { - "name": "anydataMm", - "label": "Давление", - "widget": "anydata", - "after": "mm", - "icon": "speedometer" - }, - { - "name": "anydataHum", - "label": "Влажность", - "widget": "anydata", - "after": "%", - "icon": "water", - "color": "#88AADF" - }, - { - "name": "anydataTm", - "label": "Время", - "widget": "anydata", - "after": "", - "icon": "speedometer" - }, - { - "name": "button", - "label": "Кнопка", - "widget": "btn", - "size": "large", - "color": "green", - "send": "test" - }, - { - "name": "toggle", - "label": "Переключатель", - "widget": "toggle", - "icon": "", - "iconOff": "" - }, - { - "name": "chart1", - "label": "График без точек", - "widget": "chart", - "dateFormat": "HH:mm", - "maxCount": 86400, - "pointRadius": 0 - }, - { - "name": "chart2", - "label": "График с точками", - "widget": "chart", - "maxCount": 86400, - "dateFormat": "HH:mm" - }, - { - "name": "chart3", - "label": "График Дневной", - "widget": "chart", - "dateFormat": "DD.MM.YYYY", - "maxCount": 86400, - "type": "bar" - }, - { - "name": "fillgauge", - "label": "Бочка", - "widget": "fillgauge", - "circleColor": "#00FFFF", - "textColor": "#FFFFFF", - "waveTextColor": "#000000", - "waveColor": "#00FFFF" - }, - { - "name": "inputDate", - "label": "Ввод даты", - "widget": "input", - "size": "small", - "color": "orange", - "type": "date" - }, - { - "name": "inputDgt", - "label": "Ввод числа", - "widget": "input", - "color": "blue", - "type": "number" - }, - { - "name": "inputTxt", - "label": "Ввод текста", - "widget": "input", - "size": "small", - "color": "orange", - "type": "text" - }, - { - "name": "inputTm", - "label": "Ввод времени", - "widget": "input", - "color": "blue", - "type": "time" - }, - { - "name": "progressLine", - "label": "Статус линия", - "widget": "progress-line", - "icon": "sunny", - "max": "100", - "stroke": "10" - }, - { - "name": "progressRound", - "label": "Статус круг", - "widget": "progress-round", - "max": "100", - "stroke": "20", - "color": "#45ccce", - "background": "#777", - "semicircle": "1" - }, - { - "name": "range", - "label": "Ползунок", - "widget": "range", - "descrColor": "red", - "after": "%", - "k": 0.0977, - "min": 0, - "max": 100, - "debounce": 500 - }, - { - "name": "range", - "label": "Ползунок (Servo)", - "widget": "range", - "descrColor": "red", - "after": "%", - "k": 1, - "min": 0, - "max": 180, - "debounce": 500 - }, - { - "name": "select", - "label": "Выпадающий", - "widget": "select", - "options": [ - "Выключен", - "Включен" - ], - "status": 0 - }, - { - "name": "anydataPpm", - "label": "PPM", - "widget": "anydata", - "after": "ppm", - "icon": "speedometer" - }, - { - "name": "anydatamAmp", - "label": "миллиАмперы", - "widget": "anydata", - "after": "mAmp", - "icon": "speedometer" - }, - { - "name": "anydatamVlt", - "label": "миллиВольты", - "widget": "anydata", - "after": "mVlt", - "icon": "speedometer" - }, - { - "name": "anydatamWt", - "label": "миллиВатты", - "widget": "anydata", - "after": "mWt", - "icon": "speedometer" - }, - { - "name": "anydataCm", - "label": "Сантиметры", - "widget": "anydata", - "after": "cm", - "icon": "speedometer" - }, - { - "name": "nil", - "label": "Без виджета" - } -] \ No newline at end of file diff --git a/data_svelte_lite/widgets.json b/data_svelte_lite/widgets.json index ba91a71f..8a758af7 100644 --- a/data_svelte_lite/widgets.json +++ b/data_svelte_lite/widgets.json @@ -246,11 +246,11 @@ "debounce": 500 }, { - "name": "range", + "name": "rangeServo", "label": "Ползунок (Servo)", "widget": "range", "descrColor": "red", - "after": "%", + "after": "°", "k": 1, "min": 0, "max": 180, diff --git a/include/WsServer.h b/include/WsServer.h index c9de7fe2..eed0ac78 100644 --- a/include/WsServer.h +++ b/include/WsServer.h @@ -20,4 +20,5 @@ void periodicWsSend(); void sendFileToWsByFrames(const String& filename, const String& header, const String& json, int client_id, size_t frameSize); void sendStringToWs(const String& header, String& payload, int client_id); -void sendDeviceList(uint8_t num); \ No newline at end of file +void sendDeviceList(uint8_t num); +int getNumWSClients(); \ No newline at end of file diff --git a/include/classes/IoTItem.h b/include/classes/IoTItem.h index 453b6d58..246a102d 100644 --- a/include/classes/IoTItem.h +++ b/include/classes/IoTItem.h @@ -11,7 +11,7 @@ struct IoTValue { class IoTItem { public: IoTItem(const String& parameters); - virtual ~IoTItem() {} + virtual ~IoTItem() {}; virtual void loop(); virtual void doByInterval(); virtual IoTValue execute(String command, std::vector& param); @@ -35,9 +35,12 @@ class IoTItem { void setInterval(long interval); void setIntFromNet(int interval); - unsigned long currentMillis; - unsigned long prevMillis; - unsigned long difference; + // unsigned long currentMillis; + // unsigned long prevMillis; + // unsigned long difference; + unsigned long nextMillis=0; // достаточно 1 переменной, надо экономить память + // задержка следующего вызова, не изменяет текущий _interval + void suspendNextDoByInt(unsigned long _delay); // 0 - force IoTValue value; // хранение основного значения, которое обновляется из сценария, execute(), loop() или doByInterval() diff --git a/include/utils/WiFiUtils.h b/include/utils/WiFiUtils.h index c4e43850..28524211 100644 --- a/include/utils/WiFiUtils.h +++ b/include/utils/WiFiUtils.h @@ -2,9 +2,8 @@ #include "Global.h" #include "MqttClient.h" -#include -// boolean isNetworkActive(); -inline boolean isNetworkActive() {return WiFi.status() == WL_CONNECTED;}; +boolean isNetworkActive(); +uint8_t getNumAPClients(); void routerConnect(); bool startAPMode(); boolean RouterFind(std::vector jArray); diff --git a/myProfile.json b/myProfile.json index 6fc49ac8..a6e193dc 100644 --- a/myProfile.json +++ b/myProfile.json @@ -3,8 +3,8 @@ "name": "IoTmanagerVer4", "apssid": "IoTmanager", "appass": "", - "routerssid": ["iot","wifi"], - "routerpass": ["hostel3333","pswd"], + "routerssid": "iot", + "routerpass": "hostel3333", "timezone": 2, "ntp": "pool.ntp.org", "weblogin": "admin", @@ -406,6 +406,10 @@ "path": "src/modules/display/NextionUpload", "active": false }, + { + "path": "src/modules/display/Oled128", + "active": false + }, { "path": "src/modules/display/Smi2_m", "active": true diff --git a/myProfileDef.json b/myProfileDef.json index 3ea1cffd..0170a5e1 100644 --- a/myProfileDef.json +++ b/myProfileDef.json @@ -32,6 +32,11 @@ "firmware": "0x00000", "littlefs": "0x300000" }, + { + "name": "esp8266_16mb", + "firmware": "0x00000", + "littlefs": "0x200000" + }, { "name": "esp32_4mb", "boot_app0": "0xe000", @@ -40,6 +45,14 @@ "partitions": "0x8000", "littlefs": "0x290000" }, + { + "name": "esp32_16mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x910000" + }, { "name": "esp8266_1mb", "firmware": "0x00000000", @@ -381,6 +394,10 @@ "path": "src/modules/display/NextionUpload", "active": false }, + { + "path": "src/modules/display/Oled128", + "active": false + }, { "path": "src/modules/display/Smi2_m", "active": true diff --git a/src/Main.cpp b/src/Main.cpp index 7093ba18..0d1e7951 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -10,8 +10,8 @@ IoTScenario iotScen; // объект управления сценарием String volStrForSave = ""; -unsigned long currentMillis; -unsigned long prevMillis; +// unsigned long currentMillis; // это сдесь лишнее +// unsigned long prevMillis; void elementsLoop() { // передаем управление каждому элементу конфигурации для выполнения своих функций diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index cfc5e8c2..a08f3000 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -5,7 +5,7 @@ void mqttInit() { ts.add( WIFI_MQTT_CONNECTION_CHECK, MQTT_RECONNECT_INTERVAL, [&](void*) { - if (WiFi.status() == WL_CONNECTED) { + if (isNetworkActive()) { SerialPrint("i", F("WIFI"), "http://" + jsonReadStr(settingsFlashJson, F("ip"))); wifiUptimeCalc(); if (mqtt.connected()) { @@ -121,13 +121,16 @@ void mqttSubscribe() { } void mqttSubscribeExternal(String topic, bool usePrefix) { - SerialPrint("i", F("MQTT"), ("subscribed external" + topic).c_str()); + // SerialPrint("i", F("MQTT"), mqttRootDevice); + String _sb_topic = topic; if (usePrefix) { - mqtt.subscribe((mqttPrefix + topic).c_str()); + _sb_topic = mqttPrefix + "/" + topic; } - mqtt.subscribe(topic.c_str()); + mqtt.subscribe(_sb_topic.c_str()); + + SerialPrint("i", F("MQTT"), ("subscribed external " + _sb_topic).c_str()); } void mqttCallback(char* topic, uint8_t* payload, size_t length) { diff --git a/src/StandWebServer.cpp b/src/StandWebServer.cpp index 5f899613..6a23378f 100644 --- a/src/StandWebServer.cpp +++ b/src/StandWebServer.cpp @@ -7,24 +7,22 @@ String unsupportedFiles = String(); static const char TEXT_PLAIN[] PROGMEM = "text/plain"; static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR"; static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound"; -//static bool fsOK; -//const char* fsName = "LittleFS"; +// static bool fsOK; +// const char* fsName = "LittleFS"; -void standWebServerInit() -{ +void standWebServerInit() { // Кэшировать файлы для быстрой работы - HTTP.serveStatic("/bundle.js", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.css", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.js.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.css.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/favicon.png", FileFS, "/", "max-age=31536000"); // кеширование на 1 год + // если указана директория то все файлы будут отмечены как Directory Request Handler + // если указан файл то он будет отмечен как File Request Handler + HTTP.serveStatic("/build", FileFS, "/build", "max-age=31536000"); // кеширование на 1 год + HTTP.serveStatic("/favicon.ico", FileFS, "/favicon.ico", "max-age=31536000"); // кеширование на 1 год // HTTP.on("/devicelist.json", HTTP_GET, []() { // HTTP.send(200, "application/json", devListHeapJson); // }); - HTTP.on("/settings.h.json", HTTP_GET, []() { - HTTP.send(200, "application/json", settingsFlashJson); - }); + // HTTP.on("/settings.h.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", settingsFlashJson); + //}); // HTTP.on("/settings.f.json", HTTP_GET, []() { // HTTP.send(200, "application/json", readFile(F("settings.json"), 20000)); // }); @@ -46,21 +44,19 @@ void standWebServerInit() // HTTP.send(200, "text/plain", "ok"); // }); - HTTP.on("/set", HTTP_GET, []() { if (HTTP.hasArg(F("routerssid")) && WiFi.getMode() == WIFI_AP) { - jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid"))); - syncSettingsFlashJson(); - HTTP.send(200, "text/plain", "ok"); - } + jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid"))); + syncSettingsFlashJson(); + HTTP.send(200, "text/plain", "ok"); + } if (HTTP.hasArg(F("routerpass")) && WiFi.getMode() == WIFI_AP) { - jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass"))); - syncSettingsFlashJson(); - HTTP.send(200, "text/plain", "ok"); - } - - }); + jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass"))); + syncSettingsFlashJson(); + HTTP.send(200, "text/plain", "ok"); + } + }); // Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше // httpUpdater.setup(&HTTP); @@ -101,39 +97,33 @@ void standWebServerInit() //////////////////////////////// // Utils to return HTTP codes, and determine content-type -void replyOK() -{ +void replyOK() { HTTP.send(200, FPSTR(TEXT_PLAIN), ""); } -void replyOKWithMsg(String msg) -{ +void replyOKWithMsg(String msg) { HTTP.send(200, FPSTR(TEXT_PLAIN), msg); } -void replyNotFound(String msg) -{ +void replyNotFound(String msg) { HTTP.send(404, FPSTR(TEXT_PLAIN), msg); } -void replyBadRequest(String msg) -{ -// DBG_OUTPUT_PORT.println(msg); +void replyBadRequest(String msg) { + // DBG_OUTPUT_PORT.println(msg); HTTP.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n"); } -void replyServerError(String msg) -{ -// DBG_OUTPUT_PORT.println(msg); +void replyServerError(String msg) { + // DBG_OUTPUT_PORT.println(msg); HTTP.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n"); } /* Return the FS type, status and size info */ -void handleStatus() -{ -// DBG_OUTPUT_PORT.println("handleStatus"); +void handleStatus() { + // DBG_OUTPUT_PORT.println("handleStatus"); String json; json.reserve(128); @@ -141,22 +131,22 @@ void handleStatus() json += FS_NAME; json += "\", \"isOk\":"; - #ifdef ESP8266 +#ifdef ESP8266 FSInfo fs_info; - FileFS.info(fs_info); - json += F("\"true\", \"totalBytes\":\""); - json += fs_info.totalBytes; - json += F("\", \"usedBytes\":\""); - json += fs_info.usedBytes; - json += "\""; -#endif + FileFS.info(fs_info); + json += F("\"true\", \"totalBytes\":\""); + json += fs_info.totalBytes; + json += F("\", \"usedBytes\":\""); + json += fs_info.usedBytes; + json += "\""; +#endif #ifdef ESP32 - json += F("\"true\", \"totalBytes\":\""); - json += String(FileFS.totalBytes()); - json += F("\", \"usedBytes\":\""); - json += String(FileFS.usedBytes()); - json += "\""; + json += F("\"true\", \"totalBytes\":\""); + json += String(FileFS.totalBytes()); + json += F("\", \"usedBytes\":\""); + json += String(FileFS.usedBytes()); + json += "\""; #endif json += F(",\"unsupportedFiles\":\""); @@ -168,74 +158,66 @@ void handleStatus() #ifdef ESP32 String getContentType(String filename) { - if (HTTP.hasArg("download")) { - return "application/octet-stream"; - } else if (filename.endsWith(".htm")) { - return "text/html"; - } else if (filename.endsWith(".html")) { - return "text/html"; - } else if (filename.endsWith(".css")) { - return "text/css"; - } else if (filename.endsWith(".js")) { - return "application/javascript"; - } else if (filename.endsWith(".png")) { - return "image/png"; - } else if (filename.endsWith(".gif")) { - return "image/gif"; - } else if (filename.endsWith(".jpg")) { - return "image/jpeg"; - } else if (filename.endsWith(".ico")) { - return "image/x-icon"; - } else if (filename.endsWith(".xml")) { - return "text/xml"; - } else if (filename.endsWith(".pdf")) { - return "application/x-pdf"; - } else if (filename.endsWith(".zip")) { - return "application/x-zip"; - } else if (filename.endsWith(".gz")) { - return "application/x-gzip"; - } - return "text/plain"; + if (HTTP.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; } #endif /* Read the given file from the filesystem and stream it back to the client */ -bool handleFileRead(String path) -{ -// DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); - if (path.endsWith("/")) - { +bool handleFileRead(String path) { + // DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); + if (path.endsWith("/")) { path += "index.html"; } String contentType; - if (HTTP.hasArg("download")) - { + if (HTTP.hasArg("download")) { contentType = F("application/octet-stream"); - } - else - { -#ifdef ESP32 + } else { +#ifdef ESP32 contentType = getContentType(path); #endif #ifdef ESP8266 contentType = mime::getContentType(path); -#endif +#endif } - if (!FileFS.exists(path)) - { + if (!FileFS.exists(path)) { // File not found, try gzip version path = path + ".gz"; } - if (FileFS.exists(path)) - { + if (FileFS.exists(path)) { File file = FileFS.open(path, "r"); - if (HTTP.streamFile(file, contentType) != file.size()) - { - // DBG_OUTPUT_PORT.println("Sent less data than expected!"); + if (HTTP.streamFile(file, contentType) != file.size()) { + // DBG_OUTPUT_PORT.println("Sent less data than expected!"); } file.close(); return true; @@ -248,90 +230,69 @@ bool handleFileRead(String path) As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, return the path of the closest parent still existing */ -String lastExistingParent(String path) -{ - while (!path.isEmpty() && !FileFS.exists(path)) - { - if (path.lastIndexOf('/') > 0) - { +String lastExistingParent(String path) { + while (!path.isEmpty() && !FileFS.exists(path)) { + if (path.lastIndexOf('/') > 0) { path = path.substring(0, path.lastIndexOf('/')); - } - else - { - path = String(); // No slash => the top folder does not exist + } else { + path = String(); // No slash => the top folder does not exist } } -// DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + // DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); return path; } /* Handle a file upload request */ -void handleFileUpload() -{ - if (HTTP.uri() != "/edit") - { +void handleFileUpload() { + if (HTTP.uri() != "/edit") { return; } HTTPUpload &upload = HTTP.upload(); - if (upload.status == UPLOAD_FILE_START) - { + if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; // Make sure paths always start with "/" - if (!filename.startsWith("/")) - { + if (!filename.startsWith("/")) { filename = "/" + filename; } -// DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); + // DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = FileFS.open(filename, "w"); - if (!uploadFile) - { + if (!uploadFile) { return replyServerError(F("CREATE FAILED")); } -// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); - } - else if (upload.status == UPLOAD_FILE_WRITE) - { - if (uploadFile) - { + // DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); - if (bytesWritten != upload.currentSize) - { + if (bytesWritten != upload.currentSize) { return replyServerError(F("WRITE FAILED")); } } -// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); - } - else if (upload.status == UPLOAD_FILE_END) - { - if (uploadFile) - { + // DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + if (uploadFile) { uploadFile.close(); } -// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); + // DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } - #ifdef ESP8266 -void deleteRecursive(String path) -{ +void deleteRecursive(String path) { File file = FileFS.open(path, "r"); bool isDir = file.isDirectory(); file.close(); // If it's a plain file, delete it - if (!isDir) - { + if (!isDir) { FileFS.remove(path); return; } Dir dir = FileFS.openDir(path); - while (dir.next()) - { + while (dir.next()) { deleteRecursive(path + '/' + dir.fileName()); - } + } // Then delete the folder itself FileFS.rmdir(path); @@ -339,41 +300,37 @@ void deleteRecursive(String path) #endif #ifdef ESP32 -struct treename{ - uint8_t type; - char *name; +struct treename { + uint8_t type; + char *name; }; +void deleteRecursive(String path) { + fs::File dir = FileFS.open(path); -void deleteRecursive( String path ){ - fs::File dir = FileFS.open( path ); - - if(!dir.isDirectory()){ - Serial.printf("%s is a file\n", path); - dir.close(); - Serial.printf( "result of removing file %s: %d\n", path, FileFS.remove( path ) ); - return; + if (!dir.isDirectory()) { + Serial.printf("%s is a file\n", path); + dir.close(); + Serial.printf("result of removing file %s: %d\n", path, FileFS.remove(path)); + return; } - + Serial.printf("%s is a directory\n", path); - + fs::File entry, nextentry; - - while ( entry = dir.openNextFile() ){ - -if ( entry.isDirectory() ){ - deleteRecursive( entry.path() ); - } else{ - String tmpname = path+"/"+strdup( entry.name() ); // buffer file name - entry.close(); - Serial.printf( "result of removing file %s: %d\n", tmpname, FileFS.remove( tmpname ) ); - } - + + while (entry = dir.openNextFile()) { + if (entry.isDirectory()) { + deleteRecursive(entry.path()); + } else { + String tmpname = path + "/" + strdup(entry.name()); // buffer file name + entry.close(); + Serial.printf("result of removing file %s: %d\n", tmpname, FileFS.remove(tmpname)); + } } - dir.close(); - Serial.printf( "result of removing directory %s: %d\n", path, FileFS.rmdir( path ) ); - + dir.close(); + Serial.printf("result of removing directory %s: %d\n", path, FileFS.rmdir(path)); } #endif /* @@ -383,23 +340,18 @@ if ( entry.isDirectory() ){ Delete file | parent of deleted file, or remaining ancestor Delete folder | parent of deleted folder, or remaining ancestor */ -void handleFileDelete() -{ +void handleFileDelete() { String path = HTTP.arg(0); - if (path.isEmpty() || path == "/") - { + if (path.isEmpty() || path == "/") { return replyBadRequest("BAD PATH"); } -// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (!FileFS.exists(path)) - { + // DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); + if (!FileFS.exists(path)) { return replyNotFound(FPSTR(FILE_NOT_FOUND)); } deleteRecursive(path); - - replyOKWithMsg(lastExistingParent(path)); } @@ -414,93 +366,72 @@ void handleFileDelete() Rename folder | parent of source folder Move folder | parent of source folder, or remaining ancestor */ -void handleFileCreate() -{ +void handleFileCreate() { String path = HTTP.arg("path"); - if (path.isEmpty()) - { + if (path.isEmpty()) { return replyBadRequest(F("PATH ARG MISSING")); } #ifdef USE_SPIFFS - if (checkForUnsupportedPath(path).length() > 0) - { + if (checkForUnsupportedPath(path).length() > 0) { return replyServerError(F("INVALID FILENAME")); } #endif - if (path == "/") - { + if (path == "/") { return replyBadRequest("BAD PATH"); } - if (FileFS.exists(path)) - { + if (FileFS.exists(path)) { return replyBadRequest(F("PATH FILE EXISTS")); } String src = HTTP.arg("src"); - if (src.isEmpty()) - { + if (src.isEmpty()) { // No source specified: creation -// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); - if (path.endsWith("/")) - { + // DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { // Create a folder path.remove(path.length() - 1); - if (!FileFS.mkdir(path)) - { + if (!FileFS.mkdir(path)) { return replyServerError(F("MKDIR FAILED")); } - } - else - { + } else { // Create a file File file = FileFS.open(path, "w"); - if (file) - { -#ifdef ESP8266 + if (file) { +#ifdef ESP8266 file.write((const char *)0); #endif #ifdef ESP32 file.write(0); -#endif +#endif file.close(); - } - else - { + } else { return replyServerError(F("CREATE FAILED")); } } - if (path.lastIndexOf('/') > -1) - { + if (path.lastIndexOf('/') > -1) { path = path.substring(0, path.lastIndexOf('/')); } replyOKWithMsg(path); - } - else - { + } else { // Source specified: rename - if (src == "/") - { + if (src == "/") { return replyBadRequest("BAD SRC"); } - if (!FileFS.exists(src)) - { + if (!FileFS.exists(src)) { return replyBadRequest(F("SRC FILE NOT FOUND")); } -// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); + // DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); - if (path.endsWith("/")) - { + if (path.endsWith("/")) { path.remove(path.length() - 1); } - if (src.endsWith("/")) - { + if (src.endsWith("/")) { src.remove(src.length() - 1); } - if (!FileFS.rename(src, path)) - { + if (!FileFS.rename(src, path)) { return replyServerError(F("RENAME FAILED")); } replyOKWithMsg(lastExistingParent(src)); @@ -512,26 +443,22 @@ void handleFileCreate() Also demonstrates the use of chunked responses. */ #ifdef ESP8266 -void handleFileList() -{ - if (!HTTP.hasArg("dir")) - { +void handleFileList() { + if (!HTTP.hasArg("dir")) { return replyBadRequest(F("DIR ARG MISSING")); } String path = HTTP.arg("dir"); - if (path != "/" && !FileFS.exists(path)) - { + if (path != "/" && !FileFS.exists(path)) { return replyBadRequest("BAD PATH"); } -// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); + // DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = FileFS.openDir(path); path.clear(); // use HTTP/1.1 Chunked response to avoid building a huge temporary string - if (!HTTP.chunkedResponseModeStart(200, "text/json")) - { + if (!HTTP.chunkedResponseModeStart(200, "text/json")) { HTTP.send(505, F("text/html"), F("HTTP1.1 required")); return; } @@ -539,47 +466,36 @@ void handleFileList() // use the same string for every line String output; output.reserve(64); - while (dir.next()) - { + while (dir.next()) { #ifdef USE_SPIFFS String error = checkForUnsupportedPath(dir.fileName()); - if (error.length() > 0) - { -// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + if (error.length() > 0) { + // DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); continue; } #endif - if (output.length()) - { + if (output.length()) { // send string from previous iteration // as an HTTP chunk HTTP.sendContent(output); output = ','; - } - else - { + } else { output = '['; } output += "{\"type\":\""; - if (dir.isDirectory()) - { + if (dir.isDirectory()) { output += "dir"; - } - else - { + } else { output += F("file\",\"size\":\""); output += dir.fileSize(); } output += F("\",\"name\":\""); // Always return names without leading "/" - if (dir.fileName()[0] == '/') - { + if (dir.fileName()[0] == '/') { output += &(dir.fileName()[1]); - } - else - { + } else { output += dir.fileName(); } @@ -595,65 +511,57 @@ void handleFileList() #ifdef ESP32 void handleFileList() { - if (!HTTP.hasArg("dir")) { - HTTP.send(500, "text/plain", "BAD ARGS"); - return; - } + if (!HTTP.hasArg("dir")) { + HTTP.send(500, "text/plain", "BAD ARGS"); + return; + } - String path = HTTP.arg("dir"); -// DBG_OUTPUT_PORT.println("handleFileList: " + path); + String path = HTTP.arg("dir"); + // DBG_OUTPUT_PORT.println("handleFileList: " + path); + File root = FileFS.open(path); + path = String(); - File root = FileFS.open(path); - path = String(); + String output = "["; + if (root.isDirectory()) { + File file = root.openNextFile(); + while (file) { + if (output != "[") { + output += ','; + } + output += "{\"type\":\""; + // output += (file.isDirectory()) ? "dir" : "file"; + if (file.isDirectory()) { + output += "dir"; + } else { + output += F("file\",\"size\":\""); + output += file.size(); + } - String output = "["; - if(root.isDirectory()){ - File file = root.openNextFile(); - while(file){ - if (output != "[") { - output += ','; - } - output += "{\"type\":\""; - // output += (file.isDirectory()) ? "dir" : "file"; - if (file.isDirectory()) - { - output += "dir"; - } - else - { - output += F("file\",\"size\":\""); - output += file.size(); - } - - output += "\",\"name\":\""; + output += "\",\"name\":\""; output += String(file.name()); - output += "\"}"; - file = root.openNextFile(); - } - } - output += "]"; - HTTP.send(200, "text/json", output); - + output += "\"}"; + file = root.openNextFile(); + } + } + output += "]"; + HTTP.send(200, "text/json", output); } #endif - /* The "Not Found" handler catches all URI not explicitly declared in code First try to find and return the requested file from the filesystem, and if it fails, return a 404 page with debug information */ -void handleNotFound() -{ +void handleNotFound() { #ifdef ESP8266 - String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks + String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks #endif #ifdef ESP32 - String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks + String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks #endif - if (handleFileRead(uri)) - { + if (handleFileRead(uri)) { return; } @@ -667,8 +575,7 @@ void handleNotFound() message += F("\nArguments: "); message += HTTP.args(); message += '\n'; - for (uint8_t i = 0; i < HTTP.args(); i++) - { + for (uint8_t i = 0; i < HTTP.args(); i++) { message += F(" NAME:"); message += HTTP.argName(i); message += F("\n VALUE:"); @@ -678,7 +585,7 @@ void handleNotFound() message += "path="; message += HTTP.arg("path"); message += '\n'; -// DBG_OUTPUT_PORT.print(message); + // DBG_OUTPUT_PORT.print(message); return replyNotFound(message); } @@ -689,10 +596,8 @@ void handleNotFound() embedded in the program code. Otherwise, fails with a 404 page with debug information */ -void handleGetEdit() -{ - if (handleFileRead(F("/edit.htm"))) - { +void handleGetEdit() { + if (handleFileRead(F("/edit.htm"))) { return; } diff --git a/src/WsServer.cpp b/src/WsServer.cpp index b8e07c13..db7bbd35 100644 --- a/src/WsServer.cpp +++ b/src/WsServer.cpp @@ -450,25 +450,27 @@ void sendFileToWsByFrames(const String& filename, const String& header, } void sendStringToWs(const String& header, String& payload, int client_id) { - if (!(WiFi.softAPgetStationNum() || isNetworkActive())) { - return; - } + if ((!getNumAPClients() && !isNetworkActive()) || !getNumWSClients()) { + // standWebSocket.disconnect(); // это и ниже надо сделать при - + // standWebSocket.close(); // - отключении AP И WiFi(STA), надо менять ядро WiFi. Сейчас не закрывается сессия клиента при пропаже AP И WiFi(STA) + return; + } - if (header.length() != 6) { - SerialPrint("E", "FS", F("wrong header size")); - return; - } + if (header.length() != 6) { + SerialPrint("E", "FS", F("wrong header size")); + return; + } - String msg = header + "|0012|" + payload; - size_t totalSize = msg.length(); + String msg = header + "|0012|" + payload; + size_t totalSize = msg.length(); - char dataArray[totalSize]; - msg.toCharArray(dataArray, totalSize + 1); - if (client_id == -1) { - standWebSocket.broadcastBIN((uint8_t*)dataArray, totalSize); - } else { - standWebSocket.sendBIN(client_id, (uint8_t*)dataArray, totalSize); - } + char dataArray[totalSize]; + msg.toCharArray(dataArray, totalSize + 1); + if (client_id == -1) { + standWebSocket.broadcastBIN((uint8_t*)dataArray, totalSize); + } else { + standWebSocket.sendBIN(client_id, (uint8_t*)dataArray, totalSize); + } } void sendDeviceList(uint8_t num) { @@ -483,3 +485,7 @@ void sendDeviceList(uint8_t num) { SerialPrint("i", "FS", "flash list"); } } + +int getNumWSClients() { + return standWebSocket.connectedClients(false); +} \ No newline at end of file diff --git a/src/classes/IoTItem.cpp b/src/classes/IoTItem.cpp index d371b5c5..ff44b369 100644 --- a/src/classes/IoTItem.cpp +++ b/src/classes/IoTItem.cpp @@ -7,9 +7,7 @@ IoTItem::IoTItem(const String& parameters) { jsonRead(parameters, F("int"), _interval, false); - if (_interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле - if (_interval > 0) _interval = _interval * 1000; // если int положителен, то считаем, что получены секунды - if (_interval < 0) _interval = _interval * -1; // если int отрицательный, то миллисекунды + setInterval(_interval); jsonRead(parameters, F("subtype"), _subtype, false); jsonRead(parameters, F("id"), _id); if (!jsonRead(parameters, F("multiply"), _multiply, false)) _multiply = 1; @@ -38,12 +36,16 @@ IoTItem::IoTItem(const String& parameters) { setValue(valAsStr, false); } +void IoTItem::suspendNextDoByInt(unsigned long _delay) { // 0 - force + nextMillis = millis() + _delay; +} + void IoTItem::loop() { if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) { - prevMillis = millis(); + unsigned long currentMillis = millis(); // _interval должен быть < 2147483647 мс (24 суток) + if (nextMillis - currentMillis > 2147483647UL /*ULONG_MAX/2*/ ) { + nextMillis = currentMillis + _interval; + // SerialPrint(F("i"), _id, "this->doByInterval"); this->doByInterval(); } } @@ -202,7 +204,13 @@ bool IoTItem::isStrInID(const String& str) { } void IoTItem::setInterval(long interval) { - _interval = interval; + if (interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле + else { + enableDoByInt = true; + if (interval > 0) _interval = interval * 1000; // если int положителен, то считаем, что получены секунды + else if (interval < 0) _interval = interval * -1; // если int отрицательный, то миллисекунды + } + // SerialPrint(F("i"), F("IoTItem"), "setInterval: " + _interval.toString); } IoTGpio* IoTItem::getGpioDriver() { diff --git a/src/modules/display/NextionUpload/NextionUpload.cpp b/src/modules/display/NextionUpload/NextionUpload.cpp index 9239d3ee..4d1f0c83 100644 --- a/src/modules/display/NextionUpload/NextionUpload.cpp +++ b/src/modules/display/NextionUpload/NextionUpload.cpp @@ -43,13 +43,16 @@ public: #if defined ESP8266 if (!http.begin(_host, 80, _url)) { -#elif defined ESP32 - if (!http.begin(String("http://") + _host + _url)) - { -#endif // Serial.println("connection failed"); SerialPrint("I", F("NextionUpdate"), "connection failed "); } +#elif defined ESP32 + if (!http.begin(String("http://") + _host + _url)) + { + // Serial.println("connection failed"); + SerialPrint("I", F("NextionUpdate"), "connection failed "); + } +#endif SerialPrint("I", F("NextionUpdate"), "Requesting file: " + (String)_url); int code = http.GET(); diff --git a/src/modules/display/Oled128/Oled128.cpp b/src/modules/display/Oled128/Oled128.cpp new file mode 100644 index 00000000..91ebee55 --- /dev/null +++ b/src/modules/display/Oled128/Oled128.cpp @@ -0,0 +1,174 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include + +GyverOLED oled; + +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; + +class Oled128 : public IoTItem { + private: + unsigned int _x; + unsigned int _y; + + unsigned int _k; + + int _shrift; + + String _id2show; + String _descr; + String _descr1; + + int _prevStrSize; + + bool _isShow = true; // экран показывает + + public: + Oled128(String parameters) : IoTItem(parameters) { + String addr, size, xy, k; + _prevStrSize = 0; + + jsonRead(parameters, "addr", addr); + if (addr == "") { + // scanI2C(); + return; + } + + jsonRead(parameters, "coord", xy); + _x = selectFromMarkerToMarker(xy, ",", 0).toInt(); + _y = selectFromMarkerToMarker(xy, ",", 1).toInt(); + + jsonRead(parameters, "descr", _descr); + jsonRead(parameters, "id2show", _id2show); + jsonRead(parameters, "descr1", _descr1); + // jsonRead(parameters, "scale", _k); + jsonRead(parameters, "shrift", _shrift); + + // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 + + oled.init(); // инициализация экрана + } + + void doByInterval() { + printBlankStr(_prevStrSize); + + String tmpStr = ""; + + // if (_descr != "none") tmpStr = _descr + " " + getItemValue(_id2show); + if (_descr != "none") + tmpStr = _descr + " " + getItemValue(_id2show) + " " + _descr1; + else + tmpStr = getItemValue(_id2show); + + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursorXY(_x, _y); + + oled.print(tmpStr); + + oled.update(); + + _prevStrSize = tmpStr.length(); + } + + IoTValue execute(String command, std::vector ¶m) { // будет возможным использовать, когда сценарии запустятся + + if (command == "scroll") { + String tmpStr = ""; + oled.clear(); + uint32_t tmr = millis(); + oled.autoPrintln(false); + int val = 128; + for (;;) { + // oled.clear(); // ЗАКОММЕНТИРУЙ, ЕСЛИ ВКЛЮЧЕН БУФЕР + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursor(val, _y); + + oled.print(tmpStr); + oled.update(); + val--; + if (millis() - tmr > 5000) + ; // return; + + _isShow = true; + } + + } + + else if (command == "stopscroll") { + _isShow = true; + // display->backlight(); + // else if (command == "noDisplay") { + // display->noDisplay(); + // _isShow = false; + } else if (command == "display") { + // display.display(); + _isShow = true; + } else if (command == "toggle") { + if (_isShow) { + // display->noDisplay(); + _isShow = false; + } else { + // display.display(); + _isShow = true; + } + } else if (command == "x") { + if (param.size()) { + _x = param[0].valD; + } + } else if (command == "y") { + if (param.size()) { + _y = param[0].valD; + } + } else if (command == "descr") { + if (param.size()) { + _descr = param[0].valS; + } + } else if (command == "descr1") { + if (param.size()) { + _descr1 = param[0].valS; + } + } else if (command == "id2show") { + if (param.size()) { + _id2show = param[0].valS; + } + } + + doByInterval(); + return {}; + } + + // печать пустой строки нужной длинны для затирания предыдущего значения на экране + void printBlankStr(int strSize) { + String tmpStr = ""; + for (int i = 0; i < strSize; i++) tmpStr += " "; + + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursorXY(_x, _y); + + oled.print(tmpStr); + } + + ~Oled128(){}; +}; + +void *getAPI_Oled128(String subtype, String param) { + if (subtype == F("Oled128")) { + return new Oled128(param); + } else { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/display/Oled128/modinfo.json b/src/modules/display/Oled128/modinfo.json new file mode 100644 index 00000000..3c3c73ac --- /dev/null +++ b/src/modules/display/Oled128/modinfo.json @@ -0,0 +1,86 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "name": "OLED экран 128*64", + "type": "Reading", + "subtype": "Oled128", + "id": "oled", + "widget": "", + "page": "", + "descr": "T", + "descr1": "C", + "int": 1, + "addr": "0x3C", + "coord": "0,10", + "id2show": "id датчика", + "shrift": "2" + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Ilya Belyakov @Biveraxe", + "moduleName": "Oled128", + "moduleVersion": "1.0", + "moduleDesc": "Позволяет выводить на матричные Oled экраны по указанным позициям значения других элементов конфигурации.", + "usedRam": 15, + "propInfo": { + "int": "Период времени в секундах обновления информации на экране по конкретному элементу.", + "addr": "Адрес устройства на шине, обычно 0x3c.", + "coord": "Координата позиции для вывода данных элемента конфигурации.", + "id2show": "id элемента конфигурации.", + "shrift": "Шрифт текста от 1 до 4 " + }, + "funcInfo": [ + { + "name": "x", + "descr": "Устанавливает первую координату", + "params": [ + "Номер строки первого символа" + ] + }, + { + "name": "y", + "descr": "Устанавливает вторую координату", + "params": [ + "Номер столбца первого символа" + ] + }, + { + "name": "descr", + "descr": "Задает приставку слева от значения, если none значит пусто", + "params": [ + "Строка" + ] + }, + { + "name": "descr1", + "descr": "Задает приставку справа от значения. Если descr none , то не выводится", + "params": [ + "Строка" + ] + }, + { + "name": "id2show", + "descr": "Задает ИД элемента, значение которого хотим отображать на экране", + "params": [ + "Имя элемента конфигурации" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "gyverlibs/GyverOLED @ 1.4" + ], + "esp32_16mb": [ + "gyverlibs/GyverOLED @ 1.4" + ], + "esp8266_4mb": [ + "gyverlibs/GyverOLED @ 1.4" + ] + } +} \ No newline at end of file diff --git a/src/modules/exec/ButtonOut/ButtonOut.cpp b/src/modules/exec/ButtonOut/ButtonOut.cpp index e5f86172..65b8e42f 100644 --- a/src/modules/exec/ButtonOut/ButtonOut.cpp +++ b/src/modules/exec/ButtonOut/ButtonOut.cpp @@ -3,24 +3,28 @@ extern IoTGpio IoTgpio; - class ButtonOut : public IoTItem { private: - int _pin, _inv; + int _pin; + bool _inv; public: ButtonOut(String parameters): IoTItem(parameters) { jsonRead(parameters, "pin", _pin); jsonRead(parameters, "inv", _inv); _round = 0; - IoTgpio.pinMode(_pin, OUTPUT); IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); + enableDoByInt = false; } void doByInterval() { - //value.valD = IoTgpio.analogRead(_pin); - + int val = _inv?1:0; + IoTgpio.digitalWrite(_pin, val); + // SerialPrint("I", "ButtonOut","single pulse end"); + value.valD = 0; + regEvent(0, "ButtonOut"); + enableDoByInt = false; //regEvent(value.valD, "ButtonOut"); //обязательный вызов хотяб один } @@ -30,24 +34,40 @@ class ButtonOut : public IoTItem { // param - вектор ("массив") значений параметров переданных вместе с командой: ID.Команда("пар1", 22, 33) -> param[0].ValS = "пар1", param[1].ValD = 22 if (command == "change") { - value.valD = 1 - IoTgpio.digitalRead(_pin); - IoTgpio.digitalWrite(_pin, value.valD); + value.valD = 1 - (int)value.valD; + IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); regEvent(value.valD, "ButtonOut"); } - + else if (command == "pulse") { + if (param[0].isDecimal && (param[0].valD != 0)) { + value.valD = !_inv?1:0; + enableDoByInt = true; + // SerialPrint("I", "ButtonOut","single pulse start"); + regEvent((String)(int)!_inv?1:0, "ButtonOut"); + suspendNextDoByInt(param[0].valD); + IoTgpio.digitalWrite(_pin, !_inv?1:0); + } + } return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки } void setValue(const IoTValue& Value, bool genEvent = true) { value = Value; - IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); + if ((value.valD == !_inv?1:0) && (_interval != 0)) { + value.valD = !_inv?1:0; + enableDoByInt = true; + // SerialPrint("I", "ButtonOut","single pulse start"); + suspendNextDoByInt(_interval); + } else { + enableDoByInt = false; + } regEvent((String)(int)value.valD, "ButtonOut", false, genEvent); + IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); } String getValue() { return (String)(int)value.valD; } - //======================================================================================================= ~ButtonOut() {}; }; diff --git a/src/modules/exec/ButtonOut/modinfo.json b/src/modules/exec/ButtonOut/modinfo.json index 20c729c8..6a16dd18 100644 --- a/src/modules/exec/ButtonOut/modinfo.json +++ b/src/modules/exec/ButtonOut/modinfo.json @@ -39,6 +39,11 @@ "name": "change", "descr": "Инвертирует значение переключателя", "params": [] + }, + { + "name": "pulse", + "descr": "Генерирует одиночный импульс", + "params": ["Длительность (ms)"] } ] }, diff --git a/src/modules/exec/Buzzer/Buzzer.cpp b/src/modules/exec/Buzzer/Buzzer.cpp index 5668b549..656b8dc9 100644 --- a/src/modules/exec/Buzzer/Buzzer.cpp +++ b/src/modules/exec/Buzzer/Buzzer.cpp @@ -79,16 +79,7 @@ public: case 1: // for doByIntervals - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } + IoTItem::loop(); break; case 2: diff --git a/src/modules/exec/HttpGet/HttpGet.cpp b/src/modules/exec/HttpGet/HttpGet.cpp index 8c53c10c..180d9099 100644 --- a/src/modules/exec/HttpGet/HttpGet.cpp +++ b/src/modules/exec/HttpGet/HttpGet.cpp @@ -10,7 +10,7 @@ public: void sendHttpPOST(String url, String msg) { - if (WiFi.status() == WL_CONNECTED) + if (isNetworkActive()) { WiFiClient client; diff --git a/src/modules/exec/IoTServo/modinfo.json b/src/modules/exec/IoTServo/modinfo.json index b77efd4e..df3be571 100644 --- a/src/modules/exec/IoTServo/modinfo.json +++ b/src/modules/exec/IoTServo/modinfo.json @@ -7,7 +7,7 @@ "type": "Writing", "subtype": "IoTServo", "id": "servo", - "widget": "range", + "widget": "rangeServo", "page": "servo", "descr": "угол", "int": 1, diff --git a/src/modules/exec/MySensors/MySensorsGate.cpp b/src/modules/exec/MySensors/MySensorsGate.cpp index 717c0954..8e4aebde 100644 --- a/src/modules/exec/MySensors/MySensorsGate.cpp +++ b/src/modules/exec/MySensors/MySensorsGate.cpp @@ -3,6 +3,11 @@ #include "Arduino.h" #include "MySensorsGate.h" +// временное решение +unsigned long currentMillis; +unsigned long prevMillis; +unsigned long difference; + #ifdef MYSENSORS // callback библиотеки mysensors void receive(const MyMessage& message) { diff --git a/src/modules/exec/TelegramLT/TelegramLT.cpp b/src/modules/exec/TelegramLT/TelegramLT.cpp index 2fb3c14a..eee738e0 100644 --- a/src/modules/exec/TelegramLT/TelegramLT.cpp +++ b/src/modules/exec/TelegramLT/TelegramLT.cpp @@ -13,7 +13,7 @@ class TelegramLT : public IoTItem { } void sendTelegramMsg(bool often, String msg) { - if (WiFi.status() == WL_CONNECTED && (often || !often && _prevMsg != msg)) { + if (isNetworkActive() && (often || !often && _prevMsg != msg)) { WiFiClient client; HTTPClient http; http.begin(client, "http://live-control.com/iotm/telegram.php"); diff --git a/src/modules/exec/Thermostat/Thermostat.cpp b/src/modules/exec/Thermostat/Thermostat.cpp index bcac8274..68999c20 100644 --- a/src/modules/exec/Thermostat/Thermostat.cpp +++ b/src/modules/exec/Thermostat/Thermostat.cpp @@ -246,6 +246,11 @@ protected: pv_last = pv; } +// временное решение + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + void loop() { if (enableDoByInt) diff --git a/src/modules/sensors/AnalogAdc/AnalogAdc.cpp b/src/modules/sensors/AnalogAdc/AnalogAdc.cpp index 1e90f30d..510d5723 100644 --- a/src/modules/sensors/AnalogAdc/AnalogAdc.cpp +++ b/src/modules/sensors/AnalogAdc/AnalogAdc.cpp @@ -64,13 +64,7 @@ class AnalogAdc : public IoTItem { _avgSumm = _avgSumm + IoTgpio.analogRead(_pin); _avgCount++; } - - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) { - prevMillis = millis(); - this->doByInterval(); - } + IoTItem::loop(); } ~AnalogAdc(){}; diff --git a/src/modules/sensors/ExampleModule/ExampleModule.cpp b/src/modules/sensors/ExampleModule/ExampleModule.cpp index 15fedb1f..58be642b 100644 --- a/src/modules/sensors/ExampleModule/ExampleModule.cpp +++ b/src/modules/sensors/ExampleModule/ExampleModule.cpp @@ -107,13 +107,7 @@ public: adc = IoTgpio.analogRead(_pin); // Блок вызова doByInterval, так как если определили loop, то сам он не вызовится - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) - { - prevMillis = millis(); - this->doByInterval(); - } + IoTItem::loop(); } ~ExampleModule_A() diff --git a/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp b/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp index 91da0b9c..a37add87 100644 --- a/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp +++ b/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp @@ -17,6 +17,7 @@ private: bool _isJson; bool _addPrefix; bool _debug; + bool sendOk = false; public: ExternalMQTT(String parameters) : IoTItem(parameters) @@ -26,10 +27,12 @@ public: jsonRead(parameters, F("red"), red); jsonRead(parameters, F("offline"), offline); _topic = jsonReadStr(parameters, "topic"); - _isJson = jsonReadBool(parameters, "isJson"); - _addPrefix = jsonReadBool(parameters, "addPrefix"); - _debug = jsonReadBool(parameters, "debug"); + jsonRead(parameters, "isJson", _isJson); + jsonRead(parameters, "addPrefix", _addPrefix); + jsonRead(parameters, "debug", _debug); dataFromNode = false; + if (mqttIsConnect()) + sendOk = true; mqttSubscribeExternal(_topic, _addPrefix); } char *TimeToString(unsigned long t) @@ -58,6 +61,7 @@ public: { return; } + if (_isJson) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); @@ -106,6 +110,11 @@ public: { _minutesPassed++; setNewWidgetAttributes(); + if (mqttIsConnect() && !sendOk) + { + sendOk = true; + mqttSubscribeExternal(_topic, _addPrefix); + } } void onMqttWsAppConnectEvent() { diff --git a/src/modules/sensors/Sds011/Sds011.cpp b/src/modules/sensors/Sds011/Sds011.cpp index fb081e38..1a903d86 100644 --- a/src/modules/sensors/Sds011/Sds011.cpp +++ b/src/modules/sensors/Sds011/Sds011.cpp @@ -157,16 +157,17 @@ public: void loop() { ts_sds.update(); - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } + IoTItem::loop(); + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= _interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } } //======================================================================================================= // doByInterval() diff --git a/src/modules/virtual/GoogleSheet/GoogleSheet.cpp b/src/modules/virtual/GoogleSheet/GoogleSheet.cpp index 173cfe13..3b196c4f 100644 --- a/src/modules/virtual/GoogleSheet/GoogleSheet.cpp +++ b/src/modules/virtual/GoogleSheet/GoogleSheet.cpp @@ -10,7 +10,7 @@ private: String scid = ""; String shname = ""; // bool init = false; - int interval = 1; + // int interval = 1; // long interval; String URL = ("http://iotmanager.org/projects/google.php?/macros/s/"); // F("https://script.google.com/macros/s/"); String urlFinal; @@ -24,14 +24,17 @@ public: jsonRead(parameters, F("logid"), logid); jsonRead(parameters, F("scid"), scid); jsonRead(parameters, F("shname"), shname); - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60; // так как у нас в минутах + // jsonRead(parameters, F("int"), interval); + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); + // interval = interval * 1000 * 60; // так как у нас в минутах urlFinal = URL + scid + F("/exec?") + F("sheet=") + shname; } void doByInterval() { - if (WiFi.status() == WL_CONNECTED) + if (isNetworkActive()) { String value = getItemValue(logid); if (value != "") @@ -39,23 +42,23 @@ public: } } - void loop() - { - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } - } + // void loop() + // { + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } IoTValue execute(String command, std::vector ¶m) { - if (WiFi.status() == WL_CONNECTED) + if (isNetworkActive()) { if (command == F("logGoogle")) { // Логирование определенного элемента по его идентификатору в GoogleSheet diff --git a/src/modules/virtual/Loging/Loging.cpp b/src/modules/virtual/Loging/Loging.cpp index 9671632e..fa8d4c48 100644 --- a/src/modules/virtual/Loging/Loging.cpp +++ b/src/modules/virtual/Loging/Loging.cpp @@ -23,7 +23,7 @@ class Loging : public IoTItem { String prevDate = ""; bool firstTimeInit = true; - long interval; + // long interval; public: Loging(String parameters) : IoTItem(parameters) { @@ -34,8 +34,9 @@ class Loging : public IoTItem { points = 300; SerialPrint("E", F("Loging"), "'" + id + "' user set more points than allowed, value reset to 300"); } - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60; // приводим к милисекундам + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); //jsonRead(parameters, F("keepdays"), keepdays, false); // создадим экземпляр класса даты @@ -303,18 +304,18 @@ class Loging : public IoTItem { return ""; } - void loop() { - if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) { - prevMillis = millis(); - if (interval != 0) { - this->doByInterval(); - } - } - } - } + // void loop() { + // if (enableDoByInt) { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) { + // prevMillis = millis(); + // if (interval != 0) { + // this->doByInterval(); + // } + // } + // } + // } void regEvent(const String &value, const String &consoleInfo, bool error = false, bool genEvent = true) { String userDate = getItemValue(id + "-date"); diff --git a/src/modules/virtual/LogingDaily/LogingDaily.cpp b/src/modules/virtual/LogingDaily/LogingDaily.cpp index 2ba65db6..32a0e68d 100644 --- a/src/modules/virtual/LogingDaily/LogingDaily.cpp +++ b/src/modules/virtual/LogingDaily/LogingDaily.cpp @@ -25,7 +25,7 @@ class LogingDaily : public IoTItem { String prevDate = ""; bool firstTimeInit = true; - long interval; + // long interval; public: LogingDaily(String parameters) : IoTItem(parameters) { @@ -40,8 +40,9 @@ class LogingDaily : public IoTItem { points = 365; SerialPrint("E", F("LogingDaily"), "'" + id + "' user set more points than allowed, value reset to 365"); } - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60; // приводим к милисекундам + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); } void doByInterval() { @@ -225,16 +226,16 @@ class LogingDaily : public IoTItem { return ""; } - void loop() { - if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) { - prevMillis = millis(); - this->doByInterval(); - } - } - } + // void loop() { + // if (enableDoByInt) { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } // просто максимальное количество точек int calculateMaxCount() { diff --git a/src/modules/virtual/Weather/Weather.cpp b/src/modules/virtual/Weather/Weather.cpp index 2ff85df4..9cadadeb 100644 --- a/src/modules/virtual/Weather/Weather.cpp +++ b/src/modules/virtual/Weather/Weather.cpp @@ -11,22 +11,23 @@ class Weather : public IoTItem private: String _location; String _param; - long interval; + // long interval; public: Weather(String parameters) : IoTItem(parameters) { _location = jsonReadStr(parameters, "location"); _param = jsonReadStr(parameters, "param"); - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60 * 60; // интервал проверки погоды в часах + long interval; + jsonRead(parameters, F("int"), interval); // интервал проверки погоды в часах + setInterval(interval * 60 * 60); } void getWeather() { String ret; - if (WiFi.status() == WL_CONNECTED) + if (isNetworkActive()) { // char c; String payload; @@ -113,19 +114,21 @@ public: regEvent(value.valS, "Weather"); } } - void loop() - { - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } - } + + // void loop() + // { + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } + IoTValue execute(String command, std::vector ¶m) { if (command == "get") diff --git a/src/utils/SerialPrint.cpp b/src/utils/SerialPrint.cpp index 4b3d1434..879405da 100644 --- a/src/utils/SerialPrint.cpp +++ b/src/utils/SerialPrint.cpp @@ -11,11 +11,11 @@ void SerialPrint(const String& errorLevel, const String& module, const String& m tosend += msg; Serial.println(tosend); - if (isNetworkActive()) { - if (jsonReadInt(settingsFlashJson, F("log")) != 0) { - sendStringToWs(F("corelg"), tosend, -1); - } + // if (isNetworkActive()) { // все проверки происходят в sendStringToWs() + if (jsonReadInt(settingsFlashJson, F("log")) != 0) { + sendStringToWs(F("corelg"), tosend, -1); } + // } if (errorLevel == "E") { cleanString(tosend); diff --git a/src/utils/Statistic.cpp b/src/utils/Statistic.cpp index 1b34bf72..1a992514 100644 --- a/src/utils/Statistic.cpp +++ b/src/utils/Statistic.cpp @@ -20,7 +20,7 @@ void updateDeviceStatus() { // jsonRead(settingsFlashJson, F("serverip"), serverIP); String url = serverIP + F("/projects/esprebootstat.php"); // SerialPrint("i", "Stat", "url " + url); - if ((WiFi.status() == WL_CONNECTED)) { + if ((isNetworkActive())) { WiFiClient client; HTTPClient http; http.begin(client, url); diff --git a/src/utils/WiFiUtils.cpp b/src/utils/WiFiUtils.cpp index cf1dc846..b2e0fb2f 100644 --- a/src/utils/WiFiUtils.cpp +++ b/src/utils/WiFiUtils.cpp @@ -171,40 +171,31 @@ boolean RouterFind(std::vector jArray) return res; } -// boolean isNetworkActive() { -// return WiFi.status() == WL_CONNECTED; -// } - -uint8_t RSSIquality() -{ - uint8_t res = 0; - if (WiFi.status() == WL_CONNECTED) - { - int rssi = WiFi.RSSI(); - if (rssi >= -50) - { - res = 6; //"Excellent"; - } - else if (rssi < -50 && rssi >= -60) - { - res = 5; //"Very good"; - } - else if (rssi < -60 && rssi >= -70) - { - res = 4; //"Good"; - } - else if (rssi < -70 && rssi >= -80) - { - res = 3; //"Low"; - } - else if (rssi < -80 && rssi > -100) - { - res = 2; //"Very low"; - } - else if (rssi <= -100) - { - res = 1; //"No signal"; - } - } - return res; +boolean isNetworkActive() { + return WiFi.status() == WL_CONNECTED; +} + +uint8_t getNumAPClients() { + return WiFi.softAPgetStationNum(); +} + +uint8_t RSSIquality() { + uint8_t res = 0; + if (isNetworkActive()) { + int rssi = WiFi.RSSI(); + if (rssi >= -50) { + res = 6; //"Excellent"; + } else if (rssi < -50 && rssi >= -60) { + res = 5; //"Very good"; + } else if (rssi < -60 && rssi >= -70) { + res = 4; //"Good"; + } else if (rssi < -70 && rssi >= -80) { + res = 3; //"Low"; + } else if (rssi < -80 && rssi > -100) { + res = 2; //"Very low"; + } else if (rssi <= -100) { + res = 1; //"No signal"; + } + } + return res; }