From 9d6ec18bb2ed8040c91c1ece21857f99326f7fab Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 7 Nov 2020 02:51:44 +0300 Subject: [PATCH 01/94] add one more preset --- data/presets/alloff.c.txt | 7 +++++++ data/presets/alloff.s.txt | 16 ++++++++++++++++ data/set.device.json | 3 ++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 data/presets/alloff.c.txt create mode 100644 data/presets/alloff.s.txt diff --git a/data/presets/alloff.c.txt b/data/presets/alloff.c.txt new file mode 100644 index 00000000..b1327a8e --- /dev/null +++ b/data/presets/alloff.c.txt @@ -0,0 +1,7 @@ +0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1;st[0] +0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12];st[0] +0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13];st[0] +0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14];st[0] +0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15];st[500] +0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16];st[500] +0;output-text;output-text-7;anydata;Кнопки;Статус;7;st[выключено] \ No newline at end of file diff --git a/data/presets/alloff.s.txt b/data/presets/alloff.s.txt new file mode 100644 index 00000000..0424728a --- /dev/null +++ b/data/presets/alloff.s.txt @@ -0,0 +1,16 @@ +button-out-1 = 1 +button-out-2 1 +button-out-3 1 +button-out-4 1 +pwm-out-5 200 +pwm-out-6 800 +output-text-7 включено +end +button-out-1 = 0 +button-out-2 0 +button-out-3 0 +button-out-4 0 +pwm-out-5 800 +pwm-out-6 200 +output-text-7 выключено +end \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index 55a874a1..5a97f3f8 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -89,7 +89,8 @@ "#": "Выберите пресет из списка", "/set?addPreset=dal.c": "1.Термостат на основе ds18b20 с оповещением в телеграм", "/set?addPreset=dht.c": "2.Контроль влажности на основе DHT с оповещением в телеграм", - "/set?addPreset=rel.c": "3.Включение выключение реле в заданное время" + "/set?addPreset=rel.c": "3.Включение выключение реле в заданное время", + "/set?addPreset=alloff.c": "4.Выключить все (пример работы сценариев)" } }, { From 6d52a181f0ec0670e8acc06bfea9ac9ecd55e5f2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 8 Nov 2020 18:27:01 +0300 Subject: [PATCH 02/94] udp changed for Vadim --- data/config.json | 4 +-- data/widgets/chart.json | 3 ++- include/Consts.h | 2 +- src/RemoteOrdersUdp.cpp | 54 +++++++++++++++++++++++++++-------------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/data/config.json b/data/config.json index 88fa28c5..b229dd48 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "VOLODYA", - "routerpass": "BELCHENKO", + "routerssid": "rise", + "routerpass": "hostel3333", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "m12.cloudmqtt.com", diff --git a/data/widgets/chart.json b/data/widgets/chart.json index 9ecc61e3..878ce0e4 100644 --- a/data/widgets/chart.json +++ b/data/widgets/chart.json @@ -1,4 +1,5 @@ { "widget": "chart", - "dateFormat": "HH:mm" + "dateFormat": "HH:mm", + "pointRadius": 0 } \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index e0679110..e22011ba 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -31,7 +31,7 @@ //#define MDNS_ENABLED //#define WEBSOCKET_ENABLED //#define LAYOUT_IN_RAM -//#define UDP_ENABLED +#define UDP_ENABLED //#define SSDP_ENABLED //=========Sensors enable/disable================================================================================================================================= diff --git a/src/RemoteOrdersUdp.cpp b/src/RemoteOrdersUdp.cpp index 6187d0bd..b91cd642 100644 --- a/src/RemoteOrdersUdp.cpp +++ b/src/RemoteOrdersUdp.cpp @@ -6,22 +6,21 @@ AsyncUDP asyncUdp; void asyncUdpInit() { - //if (asyncUdp.listen(1234)) { - if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 1234)) { + if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210)) { asyncUdp.onPacket([](AsyncUDPPacket packet) { - //Serial.print("UDP Packet Type: "); - //Serial.print(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); - // - //Serial.print(", From: "); - //Serial.print(packet.remoteIP()); - //Serial.print(":"); - //Serial.print(packet.remotePort()); - // - //Serial.print(", To: "); - //Serial.print(packet.localIP()); - //Serial.print(":"); - //Serial.print(packet.localPort()); - // + Serial.print("UDP Packet Type: "); + Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + + Serial.print("From: "); + Serial.print(packet.remoteIP()); + Serial.print(":"); + Serial.println(packet.remotePort()); + + Serial.print("To: "); + Serial.print(packet.localIP()); + Serial.print(":"); + Serial.println(packet.localPort()); + //Serial.print(", Length: "); //Serial.print(packet.length()); // @@ -34,15 +33,33 @@ void asyncUdpInit() { if (udpPacketValidation(data)) { udpPacketParse(data); //Serial.println("', Packet valid"); - } else { + } + else { //Serial.println("', Packet invalid"); } //reply to the client - packet.printf("Got %u bytes of data", packet.length()); + String ip = WiFi.localIP().toString(); + asyncUdp.broadcastTo(ip.c_str(), packet.remotePort()); + + //packet.printf(ip.c_str(), packet.length()); + }); } + + //ts.add( + // UDP, 10000, [&](void*) { + // + // //Serial.println("sended"); + // + // //asyncUdp.broadcastTo("Anyone here?", 5351); + // //asyncUdp.broadcast("test"); + // //asyncUdp.print("Hello Server!"); + // + // }, + // nullptr, true); + } String uint8tToString(uint8_t* data, size_t len) { @@ -56,7 +73,8 @@ String uint8tToString(uint8_t* data, size_t len) { bool udpPacketValidation(String& data) { if (data.indexOf("iotm;") != -1 && data.indexOf(getChipId()) != -1) { return true; - } else { + } + else { return false; } } From 8bfba7ab4ac172df2b0ffb067a8bd3190edfb2cc Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 8 Nov 2020 18:36:36 +0300 Subject: [PATCH 03/94] some change --- src/RemoteOrdersUdp.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/RemoteOrdersUdp.cpp b/src/RemoteOrdersUdp.cpp index b91cd642..9b3cfa35 100644 --- a/src/RemoteOrdersUdp.cpp +++ b/src/RemoteOrdersUdp.cpp @@ -45,20 +45,19 @@ void asyncUdpInit() { //packet.printf(ip.c_str(), packet.length()); - }); + }); } - //ts.add( - // UDP, 10000, [&](void*) { - // - // //Serial.println("sended"); - // - // //asyncUdp.broadcastTo("Anyone here?", 5351); - // //asyncUdp.broadcast("test"); - // //asyncUdp.print("Hello Server!"); - // - // }, - // nullptr, true); + ts.add( + UDP, 10000, [&](void*) { + + Serial.println("sended"); + asyncUdp.broadcastTo("Anyone here?", 64130); + //asyncUdp.broadcast("test"); + //asyncUdp.print("Hello Server!"); + + }, + nullptr, true); } From 96f160fc8821acc5c0dbe60cfeb1030f22e8a0eb Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 13 Nov 2020 02:58:45 +0300 Subject: [PATCH 04/94] editor changed --- data/dev_conf.txt | 0 data/edit.htm | 658 -------------------------------------- data/edit.htm.gz | Bin 0 -> 5455 bytes data/js/function.js.gz | Bin 19600 -> 19540 bytes include/Consts.h | 5 +- include/Utils/statUtils.h | 2 +- src/Utils/statUtils.cpp | 126 ++++---- src/WebServer.cpp | 1 + 8 files changed, 70 insertions(+), 722 deletions(-) create mode 100644 data/dev_conf.txt delete mode 100644 data/edit.htm create mode 100644 data/edit.htm.gz diff --git a/data/dev_conf.txt b/data/dev_conf.txt new file mode 100644 index 00000000..e69de29b diff --git a/data/edit.htm b/data/edit.htm deleted file mode 100644 index 1ebb989d..00000000 --- a/data/edit.htm +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - FS Editor - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/data/edit.htm.gz b/data/edit.htm.gz new file mode 100644 index 0000000000000000000000000000000000000000..5786121f98a84b5695314c9ad8e5537f57650ba4 GIT binary patch literal 5455 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 z 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 literal 19600 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 LkbhLfBwPUi)kAo- diff --git a/include/Consts.h b/include/Consts.h index e22011ba..b5bc2220 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -31,11 +31,10 @@ //#define MDNS_ENABLED //#define WEBSOCKET_ENABLED //#define LAYOUT_IN_RAM -#define UDP_ENABLED +//#define UDP_ENABLED //#define SSDP_ENABLED //=========Sensors enable/disable================================================================================================================================= -#define TANK_LEVEL_SAMPLES 10 #define LEVEL_ENABLED #define ANALOG_ENABLED #define DALLAS_ENABLED @@ -107,6 +106,6 @@ enum ConfigType_t { CT_SCENARIO }; -//07.11.2020 +//07.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.8% (used 38376 bytes from 81920 bytes) //Flash: [===== ] 54.2% (used 566004 bytes from 1044464 bytes) \ No newline at end of file diff --git a/include/Utils/statUtils.h b/include/Utils/statUtils.h index c97abf8e..c01e0bb8 100644 --- a/include/Utils/statUtils.h +++ b/include/Utils/statUtils.h @@ -3,7 +3,7 @@ #include "Global.h" extern void initSt(); -extern String updateDevicePsn(String lat, String lon, String accur); +extern String updateDevicePsn(String lat, String lon, String accur, String geo); extern String updateDeviceStatus(); extern String addNewDevice(); diff --git a/src/Utils/statUtils.cpp b/src/Utils/statUtils.cpp index 0145e5f2..fad0a29d 100644 --- a/src/Utils/statUtils.cpp +++ b/src/Utils/statUtils.cpp @@ -34,10 +34,12 @@ void decide() { if (cnt <= 3) { Serial.println("(get)"); getPsn(); - } else { + } + else { if (cnt % 5) { Serial.println("(skip)"); - } else { + } + else { Serial.println("(get)"); getPsn(); } @@ -51,10 +53,8 @@ void getPsn() { String line = jsonReadStr(res, "loc"); String lat = selectToMarker(line, ","); String lon = deleteBeforeDelimiter(line, ","); - //String city = jsonReadStr(res, "city"); - //String country = jsonReadStr(res, "country"); - //String region = jsonReadStr(res, "region"); - updateDevicePsn(lat, lon, "0"); + String geo = jsonReadStr(res, "city") + ", " + jsonReadStr(res, "country") + ", " + jsonReadStr(res, "region"); + updateDevicePsn(lat, lon, "0", geo); } } @@ -70,7 +70,7 @@ String addNewDevice() { jsonWriteStr(json, "name", FIRMWARE_NAME); jsonWriteStr(json, "model", getChipId()); //============================================== - http.begin(client, serverIP + F(":8082/api/devices/")); + http.begin(client, serverIP + F(":8082/api/devices/")); http.setAuthorization("admin", "admin"); http.addHeader("Content-Type", "application/json"); int httpCode = http.POST(json); @@ -81,7 +81,8 @@ String addNewDevice() { ret += " " + payload; //saveId("statid.txt", jsonReadInt(payload, "id")); } - } else { + } + else { ret = http.errorToString(httpCode).c_str(); } http.end(); @@ -90,35 +91,39 @@ String addNewDevice() { return ret; } -String updateDevicePsn(String lat, String lon, String accur) { +String updateDevicePsn(String lat, String lon, String accur, String geo) { String ret; if ((WiFi.status() == WL_CONNECTED)) { float latfl = lat.toFloat(); float lonfl = lon.toFloat(); randomSeed(micros()); - float latc = random(1, 9)/100; - randomSeed(micros()); - float lonc = random(1, 9)/100; - latfl = latfl + latc; - lonfl = lonfl + lonc; + float c = random(2, 9); + if (c > 5) { + latfl = latfl + (c / 100); + lonfl = lonfl + (c / 100); + } + else { + latfl = latfl - (c / 100); + lonfl = lonfl - (c / 100); + } WiFiClient client; HTTPClient http; - http.begin(client, serverIP + F(":5055/")); + http.begin(client, serverIP + F(":5055/")); http.setAuthorization("admin", "admin"); http.addHeader("Content-Type", "application/json"); String mac = WiFi.macAddress().c_str(); int httpCode = http.POST("?id=" + mac + - "&resetReason=" + ESP_getResetReason() + - "&lat=" + String(latfl) + - "&lon=" + String(lonfl) + - "&accuracy=" + accur + ""); + "&lat=" + String(latfl) + + "&lon=" + String(lonfl) + + "&accuracy=" + accur + ""); if (httpCode > 0) { ret = httpCode; if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); ret += " " + payload; } - } else { + } + else { ret = http.errorToString(httpCode).c_str(); } http.end(); @@ -132,24 +137,25 @@ String updateDeviceStatus() { if ((WiFi.status() == WL_CONNECTED)) { WiFiClient client; HTTPClient http; - http.begin(client, serverIP + F(":5055/")); + http.begin(client, serverIP + F(":5055/")); http.setAuthorization("admin", "admin"); http.addHeader("Content-Type", "application/json"); String mac = WiFi.macAddress().c_str(); int httpCode = http.POST("?id=" + mac + - "&resetReason=" + ESP_getResetReason() + - "&uptime=" + timeNow->getUptime() + - "&uptimeTotal=" + getUptimeTotal() + - "&version=" + FIRMWARE_VERSION + - "&resetsTotal=" + String(getCurrentNumber("stat.txt")) + - "&heap=" + String(ESP.getFreeHeap()) + ""); + "&resetReason=" + ESP_getResetReason() + + "&uptime=" + timeNow->getUptime() + + "&uptimeTotal=" + getUptimeTotal() + + "&version=" + FIRMWARE_VERSION + + "&resetsTotal=" + String(getCurrentNumber("stat.txt")) + + "&heap=" + String(ESP.getFreeHeap()) + ""); if (httpCode > 0) { ret = httpCode; if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); ret += " " + payload; } - } else { + } + else { ret = http.errorToString(httpCode).c_str(); } http.end(); @@ -186,38 +192,38 @@ String ESP_getResetReason(void) { String ESP32GetResetReason(uint32_t cpu_no) { // tools\sdk\include\esp32\rom\rtc.h switch (rtc_get_reset_reason((RESET_REASON)cpu_no)) { - case POWERON_RESET: - return F("Vbat power on reset"); // 1 - case SW_RESET: - return F("Software reset digital core"); // 3 - case OWDT_RESET: - return F("Legacy watch dog reset digital core"); // 4 - case DEEPSLEEP_RESET: - return F("Deep Sleep reset digital core"); // 5 - case SDIO_RESET: - return F("Reset by SLC module, reset digital core"); // 6 - case TG0WDT_SYS_RESET: - return F("Timer Group0 Watch dog reset digital core"); // 7 - case TG1WDT_SYS_RESET: - return F("Timer Group1 Watch dog reset digital core"); // 8 - case RTCWDT_SYS_RESET: - return F("RTC Watch dog Reset digital core"); // 9 - case INTRUSION_RESET: - return F("Instrusion tested to reset CPU"); // 10 - case TGWDT_CPU_RESET: - return F("Time Group reset CPU"); // 11 - case SW_CPU_RESET: - return F("Software reset CPU"); // 12 - case RTCWDT_CPU_RESET: - return F("RTC Watch dog Reset CPU"); // 13 - case EXT_CPU_RESET: - return F("or APP CPU, reseted by PRO CPU"); // 14 - case RTCWDT_BROWN_OUT_RESET: - return F("Reset when the vdd voltage is not stable"); // 15 - case RTCWDT_RTC_RESET: - return F("RTC Watch dog reset digital core and rtc module"); // 16 - default: - return F("NO_MEAN"); // 0 + case POWERON_RESET: + return F("Vbat power on reset"); // 1 + case SW_RESET: + return F("Software reset digital core"); // 3 + case OWDT_RESET: + return F("Legacy watch dog reset digital core"); // 4 + case DEEPSLEEP_RESET: + return F("Deep Sleep reset digital core"); // 5 + case SDIO_RESET: + return F("Reset by SLC module, reset digital core"); // 6 + case TG0WDT_SYS_RESET: + return F("Timer Group0 Watch dog reset digital core"); // 7 + case TG1WDT_SYS_RESET: + return F("Timer Group1 Watch dog reset digital core"); // 8 + case RTCWDT_SYS_RESET: + return F("RTC Watch dog Reset digital core"); // 9 + case INTRUSION_RESET: + return F("Instrusion tested to reset CPU"); // 10 + case TGWDT_CPU_RESET: + return F("Time Group reset CPU"); // 11 + case SW_CPU_RESET: + return F("Software reset CPU"); // 12 + case RTCWDT_CPU_RESET: + return F("RTC Watch dog Reset CPU"); // 13 + case EXT_CPU_RESET: + return F("or APP CPU, reseted by PRO CPU"); // 14 + case RTCWDT_BROWN_OUT_RESET: + return F("Reset when the vdd voltage is not stable"); // 15 + case RTCWDT_RTC_RESET: + return F("RTC Watch dog reset digital core and rtc module"); // 16 + default: + return F("NO_MEAN"); // 0 } } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 810a1add..0cd23843 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -25,6 +25,7 @@ void init() { server.serveStatic("/js/", LittleFS, "/js/").setCacheControl("max-age=600"); server.serveStatic("/favicon.ico", LittleFS, "/favicon.ico").setCacheControl("max-age=600"); server.serveStatic("/icon.jpeg", LittleFS, "/icon.jpeg").setCacheControl("max-age=600"); + server.serveStatic("/edit", LittleFS, "/edit").setCacheControl("max-age=600"); server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm").setAuthentication(login.c_str(), pass.c_str()); From 50ad50efb238ee17eb46067151ad4ce1b21fe30d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 13 Nov 2020 17:13:50 +0300 Subject: [PATCH 05/94] ButtonOut changed (not working version) --- include/BufferExecute.h | 3 - include/Class/ScenarioClass2.h | 69 ----------------- include/Consts.h | 8 +- include/Global.h | 9 ++- include/MqttClient.h | 2 + include/items/ButtonOut.h | 32 ++++++++ include/items/ButtonOutClass.h | 82 ++++++++++----------- include/items/{LoggingClass.h => Logging.h} | 0 src/BufferExecute.cpp | 3 + src/Class/ScenarioClass3.cpp | 11 +-- src/Global.cpp | 10 ++- src/Init.cpp | 15 +++- src/MqttClient.cpp | 18 +++-- src/Web.cpp | 2 +- src/items/ButtonOut.cpp | 64 ++++++++++++++++ src/items/ButtonOutClass.cpp | 46 ++++++------ src/items/ImpulsOutClass.cpp | 6 +- src/items/{LoggingClass.cpp => Logging.cpp} | 2 +- src/main.cpp | 2 +- 19 files changed, 216 insertions(+), 168 deletions(-) delete mode 100644 include/Class/ScenarioClass2.h create mode 100644 include/items/ButtonOut.h rename include/items/{LoggingClass.h => Logging.h} (100%) create mode 100644 src/items/ButtonOut.cpp rename src/items/{LoggingClass.cpp => Logging.cpp} (99%) diff --git a/include/BufferExecute.h b/include/BufferExecute.h index 238844f5..77996ee6 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -9,9 +9,6 @@ extern void loopCmdExecute(); extern void addKey(String& key, String& keyNumberTable, int number); extern int getKeyNum(String& key, String& keyNumberTable); -extern void buttonOut(); -extern void buttonOutSet(); - extern void pwmOut(); extern void pwmOutSet(); diff --git a/include/Class/ScenarioClass2.h b/include/Class/ScenarioClass2.h deleted file mode 100644 index 6dc84eb6..00000000 --- a/include/Class/ScenarioClass2.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include -#include "Cmd.h" -#include "Global.h" - -class Scenario { - -public: - - void loop() { - - String allBlocks = scenario; - allBlocks.replace("\r\n", "\n"); - allBlocks.replace("\r", "\n"); - allBlocks += "\n"; - - String incommingEvent = selectToMarker(eventBuf, ","); - - while (allBlocks.length() > 1) { - String oneBlock = selectToMarker(allBlocks, "end\n"); - String condition = selectToMarker(oneBlock, "\n"); - - String setEvent = selectFromMarkerToMarker(condition, " ", 0); - String setEventSign = selectFromMarkerToMarker(condition, " ", 1); - String setEventValue = selectFromMarkerToMarker(condition, " ", 2); - if (!isDigitStr(setEventValue)) setEventValue = jsonReadStr(configLiveJson, setEventValue); - - String incommingEventValue = jsonReadStr(configLiveJson, incommingEvent); - - if (incommingEvent == setEvent) { - - boolean flag = false; - - if (setEventSign == "=") { - flag = incommingEventValue == setEventValue; - } - else if (setEventSign == "!=") { - flag = incommingEventValue != setEventValue; - } - else if (setEventSign == "<") { - flag = incommingEventValue.toFloat() < setEventValue.toFloat(); - } - else if (setEventSign == ">") { - flag = incommingEventValue.toFloat() > setEventValue.toFloat(); - } - else if (setEventSign == ">=") { - flag = incommingEventValue.toFloat() >= setEventValue.toFloat(); - } - else if (setEventSign == "<=") { - flag = incommingEventValue.toFloat() <= setEventValue.toFloat(); - } - - if (flag) { - //SerialPrint("I", "Scenario", "incomming Event Value: " + incommingEventValue); - //SerialPrint("I", "Scenario", "set Event Value: " + setEventValue); - - oneBlock = deleteBeforeDelimiter(oneBlock, "\n"); - oneBlock.replace("end", ""); - SerialPrint("I", "Scenario", condition + " set:\n" + oneBlock); - spaceCmdExecute(oneBlock); - } - } - allBlocks = deleteBeforeDelimiter(allBlocks, "end\n"); - } - eventBuf = deleteBeforeDelimiter(eventBuf, ","); - } -}; - -extern Scenario* myScenario; \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index b5bc2220..e8e7f96d 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -105,7 +105,11 @@ enum ConfigType_t { CT_CONFIG, CT_SCENARIO }; - +//history //07.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.8% (used 38376 bytes from 81920 bytes) -//Flash: [===== ] 54.2% (used 566004 bytes from 1044464 bytes) \ No newline at end of file +//Flash: [===== ] 54.2% (used 566004 bytes from 1044464 bytes) + +//13.11.2020 (SSDP OFF, UDP OFF) +//RAM: [===== ] 46.6% (used 38208 bytes from 81920 bytes) +//Flash: [===== ] 54.2% (used 566388 bytes from 1044464 bytes) \ No newline at end of file diff --git a/include/Global.h b/include/Global.h index 59226d64..12adbee7 100644 --- a/include/Global.h +++ b/include/Global.h @@ -67,8 +67,13 @@ extern String itemsFile; extern String itemsLine; //key lists and numbers -extern String impulsKeyList; -extern int impulsEnterCounter; +//========================================= +extern String impuls_KeyList; +extern int impuls_EnterCounter; +//========================================= +extern String buttonOut_KeyList; +extern int buttonOut_EnterCounter; +//========================================= // Sensors extern String sensorReadingMap10sec; diff --git a/include/MqttClient.h b/include/MqttClient.h index 7656ced5..1acc89b3 100644 --- a/include/MqttClient.h +++ b/include/MqttClient.h @@ -2,6 +2,8 @@ #include +extern String mqttPrefix; +extern String mqttRootDevice; void mqttInit(); boolean mqttConnect(); diff --git a/include/items/ButtonOut.h b/include/items/ButtonOut.h new file mode 100644 index 00000000..6d3a1863 --- /dev/null +++ b/include/items/ButtonOut.h @@ -0,0 +1,32 @@ +#pragma once +#include + +#include "Global.h" + +class ButtonOut; + +typedef std::vector MyButtonOutVector; + +class ButtonOut { + public: + + ButtonOut(unsigned int pin, boolean inv, String key); + + ~ButtonOut(); + + void init(); + void execute(String state); + + private: + + unsigned int _pin; + boolean _inv; + String _key; + + void addNewDelOldData(const String filename, size_t maxPoints, String payload); +}; + +extern MyButtonOutVector* myButtonOut; + +extern void buttonOut(); +extern void buttonOutExecute(); diff --git a/include/items/ButtonOutClass.h b/include/items/ButtonOutClass.h index 1afebc63..dca5fadb 100644 --- a/include/items/ButtonOutClass.h +++ b/include/items/ButtonOutClass.h @@ -1,41 +1,41 @@ -#pragma once -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class ButtonOutClass : public LineParsing { -public: - ButtonOutClass() : LineParsing() {}; - - void init() { - if (_pin != "") { - pinMode(_pin.toInt(), OUTPUT); - } - jsonWriteStr(configOptionJson, _key + "_pin", _pin); - jsonWriteStr(configOptionJson, _key + "_inv", _inv); - } - - void pinStateSetDefault() { - pinChange(_key, _state); - } - - - void pinChange(String key, String state) { - String pin = jsonReadStr(configOptionJson, key + "_pin"); - String inv = jsonReadStr(configOptionJson, key + "_inv"); - int pinInt = pin.toInt(); - - if (inv == "") { - digitalWrite(pinInt, state.toInt()); - } - else { - digitalWrite(pinInt, !state.toInt()); - } - eventGen2(key, state); - jsonWriteInt(configLiveJson, key, state.toInt()); - publishStatus(key, state); - } -}; - -extern ButtonOutClass myButtonOut; \ No newline at end of file +//#pragma once +//#include +// +//#include "Class/LineParsing.h" +//#include "Global.h" +// +//class ButtonOutClass : public LineParsing { +//public: +// ButtonOutClass() : LineParsing() {}; +// +// void init() { +// if (_pin != "") { +// pinMode(_pin.toInt(), OUTPUT); +// } +// jsonWriteStr(configOptionJson, _key + "_pin", _pin); +// jsonWriteStr(configOptionJson, _key + "_inv", _inv); +// } +// +// void pinStateSetDefault() { +// pinChange(_key, _state); +// } +// +// +// void pinChange(String key, String state) { +// String pin = jsonReadStr(configOptionJson, key + "_pin"); +// String inv = jsonReadStr(configOptionJson, key + "_inv"); +// int pinInt = pin.toInt(); +// +// if (inv == "") { +// digitalWrite(pinInt, state.toInt()); +// } +// else { +// digitalWrite(pinInt, !state.toInt()); +// } +// eventGen2(key, state); +// jsonWriteInt(configLiveJson, key, state.toInt()); +// publishStatus(key, state); +// } +//}; +// +//extern ButtonOutClass myButtonOut; \ No newline at end of file diff --git a/include/items/LoggingClass.h b/include/items/Logging.h similarity index 100% rename from include/items/LoggingClass.h rename to include/items/Logging.h diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index ecba8a91..16584f2c 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -1,5 +1,8 @@ #include "BufferExecute.h" +// #include "items/SensorDallas.h" +#include "items/ButtonOut.h" +// #include "Global.h" #include "Module/Terminal.h" diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index 1876711f..91776583 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -1,18 +1,13 @@ #include "Class/ScenarioClass3.h" +#include "MqttClient.h" Scenario* myScenario; -//void eventGen(String event_name, String number) { -// if (!jsonReadBool(configSetupJson, "scen")) { -// return; -// } -// SerialPrint("", "", event_name); -// eventBuf += event_name + number + ","; -//} - void eventGen2(String eventName, String eventValue) { if (!jsonReadBool(configSetupJson, "scen")) { return; } //Serial.println(eventName + " " + eventValue); eventBuf += eventName + " " + eventValue + ","; + + //publish(mqttPrefix, eventName + " " + eventValue + ","); } \ No newline at end of file diff --git a/src/Global.cpp b/src/Global.cpp index 75203c97..22731505 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -38,9 +38,13 @@ String itemsFile = ""; String itemsLine = ""; //key lists and numbers -String impulsKeyList = ""; -int impulsEnterCounter = -1; - +//========================================= +String impuls_KeyList = ""; +int impuls_EnterCounter = -1; +//========================================= +String buttonOut_KeyList = ""; +int buttonOut_EnterCounter = -1; +//========================================= // Sensors String sensorReadingMap10sec; diff --git a/src/Init.cpp b/src/Init.cpp index f49654c4..fe97b678 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -2,8 +2,9 @@ #include "BufferExecute.h" #include "Cmd.h" #include "Global.h" -#include "items/LoggingClass.h" +#include "items/Logging.h" #include "items/ImpulsOutClass.h" +#include "items/ButtonOut.h" #include "items/SensorDallas.h" void loadConfig() { @@ -44,9 +45,15 @@ void Device_init() { if (myImpulsOut != nullptr) { myImpulsOut->clear(); } - impulsKeyList = ""; - impulsEnterCounter = -1; - //================================ + impuls_KeyList = ""; + impuls_EnterCounter = -1; + //======clear buttonOut params======= + if (myButtonOut != nullptr) { + myButtonOut->clear(); + } + buttonOut_KeyList = ""; + buttonOut_EnterCounter = -1; + //=================================== #ifdef LAYOUT_IN_RAM diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 2210cbb3..bbe52dc2 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -1,7 +1,7 @@ #include "MqttClient.h" #include -#include "items/LoggingClass.h" +#include "items/Logging.h" #include "Class/NotAsync.h" #include "Global.h" #include "Init.h" @@ -62,10 +62,9 @@ void mqttSubscribe() { SerialPrint("I", "MQTT", "subscribe"); mqtt.subscribe(mqttPrefix.c_str()); mqtt.subscribe((mqttRootDevice + "/+/control").c_str()); - mqtt.subscribe((mqttRootDevice + "/order").c_str()); mqtt.subscribe((mqttRootDevice + "/update").c_str()); - mqtt.subscribe((mqttRootDevice + "/devc").c_str()); - mqtt.subscribe((mqttRootDevice + "/devs").c_str()); + //mqtt.subscribe((mqttRootDevice + "/order").c_str()); + //mqtt.subscribe((mqttPrefix + "/event").c_str()); } boolean mqttConnect() { @@ -131,11 +130,16 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { } else if (topicStr.indexOf("order")) { - payloadStr.replace("_", " "); - orderBuf += payloadStr; - orderBuf += ","; + //payloadStr.replace("_", " "); + //orderBuf += payloadStr; + //orderBuf += ","; } + + //else if (topicStr.indexOf("event")) { + // eventBuf += payloadStr; + //} + else if (topicStr.indexOf("update")) { if (payloadStr == "1") { myNotAsyncActions->make(do_UPGRADE); diff --git a/src/Web.cpp b/src/Web.cpp index d2f53825..cd90a2ca 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -3,7 +3,7 @@ #include "Global.h" #include "Init.h" #include "ItemsList.h" -#include "items/LoggingClass.h" +#include "items/Logging.h" #include "Telegram.h" bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { diff --git a/src/items/ButtonOut.cpp b/src/items/ButtonOut.cpp new file mode 100644 index 00000000..26d574a2 --- /dev/null +++ b/src/items/ButtonOut.cpp @@ -0,0 +1,64 @@ +#include "items/ButtonOut.h" + +#include + +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" + +ButtonOut::ButtonOut(unsigned int pin, boolean inv, String key) { + _pin = pin; + _inv = inv; + _key = key; +} +ButtonOut::~ButtonOut() {} + +void ButtonOut::init() { + pinMode(_pin, OUTPUT); +} + +void ButtonOut::execute(String state) { + //if (_inv) { + // digitalWrite(_pin, !state.toInt()); + //} + //else { + digitalWrite(_pin, state.toInt()); + //} + eventGen2(_key, state); + jsonWriteInt(configLiveJson, _key, state.toInt()); + publishStatus(_key, state); +} + +MyButtonOutVector* myButtonOut = nullptr; + +void buttonOut() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + String pin = myLineParsing.gpin(); + String inv = myLineParsing.ginv(); + myLineParsing.clear(); + + buttonOut_EnterCounter++; + addKey(key, buttonOut_KeyList, buttonOut_EnterCounter); + + static bool firstTime = true; + if (firstTime) myButtonOut = new MyButtonOutVector(); + firstTime = false; + myButtonOut->push_back(ButtonOut(pin.toInt(), inv.toInt(), key)); + + sCmd.addCommand(key.c_str(), buttonOutExecute); +} + +void buttonOutExecute() { + String key = sCmd.order(); + String state = sCmd.next(); + + int number = getKeyNum(key, buttonOut_KeyList); + + if (myButtonOut != nullptr) { + if (number != -1) { + myButtonOut->at(number).execute(state); + } + } +} + diff --git a/src/items/ButtonOutClass.cpp b/src/items/ButtonOutClass.cpp index ba674e0d..5de6ca5e 100644 --- a/src/items/ButtonOutClass.cpp +++ b/src/items/ButtonOutClass.cpp @@ -1,23 +1,23 @@ -#include "items/ButtonOutClass.h" - -#include "BufferExecute.h" -//==========================================Модуль кнопок=================================================== -//button-out light toggle Кнопки Свет 1 pin[12] inv[1] st[1] -//========================================================================================================== -ButtonOutClass myButtonOut; -void buttonOut() { - myButtonOut.update(); - String key = myButtonOut.gkey(); - String pin = myButtonOut.gpin(); - String inv = myButtonOut.ginv(); - sCmd.addCommand(key.c_str(), buttonOutSet); - myButtonOut.init(); - myButtonOut.pinStateSetDefault(); - myButtonOut.clear(); -} - -void buttonOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myButtonOut.pinChange(key, state); -} +//#include "items/ButtonOutClass.h" +// +//#include "BufferExecute.h" +////==========================================Модуль кнопок=================================================== +////button-out light toggle Кнопки Свет 1 pin[12] inv[1] st[1] +////========================================================================================================== +//ButtonOutClass myButtonOut; +//void buttonOut() { +// myButtonOut.update(); +// String key = myButtonOut.gkey(); +// String pin = myButtonOut.gpin(); +// String inv = myButtonOut.ginv(); +// sCmd.addCommand(key.c_str(), buttonOutSet); +// myButtonOut.init(); +// myButtonOut.pinStateSetDefault(); +// myButtonOut.clear(); +//} +// +//void buttonOutSet() { +// String key = sCmd.order(); +// String state = sCmd.next(); +// myButtonOut.pinChange(key, state); +//} diff --git a/src/items/ImpulsOutClass.cpp b/src/items/ImpulsOutClass.cpp index 4cab6494..172e89e4 100644 --- a/src/items/ImpulsOutClass.cpp +++ b/src/items/ImpulsOutClass.cpp @@ -44,8 +44,8 @@ void impuls() { String pin = myLineParsing.gpin(); myLineParsing.clear(); - impulsEnterCounter++; - addKey(key, impulsKeyList, impulsEnterCounter); + impuls_EnterCounter++; + addKey(key, impuls_KeyList, impuls_EnterCounter); static bool firstTime = true; if (firstTime) myImpulsOut = new MyImpulsOutVector(); @@ -60,7 +60,7 @@ void impulsExecute() { String impulsPeriod = sCmd.next(); String impulsCount = sCmd.next(); - int number = getKeyNum(key, impulsKeyList); + int number = getKeyNum(key, impuls_KeyList); if (myImpulsOut != nullptr) { if (number != -1) { diff --git a/src/items/LoggingClass.cpp b/src/items/Logging.cpp similarity index 99% rename from src/items/LoggingClass.cpp rename to src/items/Logging.cpp index e01e01f5..2f178d23 100644 --- a/src/items/LoggingClass.cpp +++ b/src/items/Logging.cpp @@ -1,4 +1,4 @@ -#include "items/LoggingClass.h" +#include "items/Logging.h" #include diff --git a/src/main.cpp b/src/main.cpp index 08c03929..4a809cbd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,7 @@ #include "Utils/Timings.h" #include "Utils/WebUtils.h" #include "items/ButtonInClass.h" -#include "items/LoggingClass.h" +#include "items/Logging.h" #include "items/ImpulsOutClass.h" #include "items/SensorDallas.h" #include "Telegram.h" From 6135f3322e496c579635df48f0a23d97163b10c7 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 14 Nov 2020 23:00:09 +0300 Subject: [PATCH 06/94] some --- data/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/config.json b/data/config.json index b229dd48..88fa28c5 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", - "routerpass": "hostel3333", + "routerssid": "VOLODYA", + "routerpass": "BELCHENKO", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "m12.cloudmqtt.com", From d6aab0cf949d0ea196cb376acdd9956e0f9595c4 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 15 Nov 2020 01:44:25 +0300 Subject: [PATCH 07/94] Big changes. Compiling version --- data/items/button-out.inv.txt | 2 +- data/items/button-out.npin.txt | 2 +- data/items/button-out.pin.txt | 2 +- data/items/input-digit.txt | 2 +- data/items/input-time.txt | 2 +- data/items/output-text.txt | 2 +- data/items/pwm-out.txt | 2 +- data/presets/alloff.c.txt | 14 ++--- data/presets/dal.c.txt | 6 +-- data/presets/dht.c.txt | 6 +-- data/presets/rel.c.txt | 8 +-- data/set.device.json | 5 +- include/BufferExecute.h | 2 +- include/Consts.h | 1 + include/Global.h | 3 ++ include/Utils/JsonUtils.h | 4 +- include/items/ButtonOutClass.h | 41 -------------- include/items/InputClass.h | 52 +++++++++--------- include/items/{ButtonOut.h => vButtonOut.h} | 1 - .../items/{ImpulsOutClass.h => vImpulsOut.h} | 0 include/items/vInput.h | 29 ++++++++++ include/items/{Logging.h => vLogging.h} | 0 .../items/{SensorDallas.h => vSensorDallas.h} | 0 src/BufferExecute.cpp | 8 +-- src/Global.cpp | 3 ++ src/Init.cpp | 21 ++++++-- src/ItemsCmd.cpp | 4 +- src/MqttClient.cpp | 2 +- src/Utils/JsonUtils.cpp | 6 +++ src/Web.cpp | 2 +- src/items/ButtonOutClass.cpp | 23 -------- src/items/InputDigitClass.cpp | 38 ++++++------- src/items/InputTimeClass.cpp | 28 +++++----- src/items/{ButtonOut.cpp => vButtonOut.cpp} | 34 +++++++----- .../{ImpulsOutClass.cpp => vImpulsOut.cpp} | 2 +- src/items/vInput.cpp | 53 +++++++++++++++++++ src/items/{Logging.cpp => vLogging.cpp} | 3 +- .../{SensorDallas.cpp => vSensorDallas.cpp} | 2 +- src/main.cpp | 6 +-- 39 files changed, 238 insertions(+), 183 deletions(-) delete mode 100644 include/items/ButtonOutClass.h rename include/items/{ButtonOut.h => vButtonOut.h} (96%) rename include/items/{ImpulsOutClass.h => vImpulsOut.h} (100%) create mode 100644 include/items/vInput.h rename include/items/{Logging.h => vLogging.h} (100%) rename include/items/{SensorDallas.h => vSensorDallas.h} (100%) delete mode 100644 src/items/ButtonOutClass.cpp rename src/items/{ButtonOut.cpp => vButtonOut.cpp} (68%) rename src/items/{ImpulsOutClass.cpp => vImpulsOut.cpp} (98%) create mode 100644 src/items/vInput.cpp rename src/items/{Logging.cpp => vLogging.cpp} (98%) rename src/items/{SensorDallas.cpp => vSensorDallas.cpp} (97%) diff --git a/data/items/button-out.inv.txt b/data/items/button-out.inv.txt index fd3bdead..5499a108 100644 --- a/data/items/button-out.inv.txt +++ b/data/items/button-out.inv.txt @@ -1 +1 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1];st[1] \ No newline at end of file +0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] \ No newline at end of file diff --git a/data/items/button-out.npin.txt b/data/items/button-out.npin.txt index 33bb17da..ecffd881 100644 --- a/data/items/button-out.npin.txt +++ b/data/items/button-out.npin.txt @@ -1 +1 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;st[0] \ No newline at end of file +0;button-out;id;toggle;Кнопки;Освещение;order \ No newline at end of file diff --git a/data/items/button-out.pin.txt b/data/items/button-out.pin.txt index 81310f4e..b0645025 100644 --- a/data/items/button-out.pin.txt +++ b/data/items/button-out.pin.txt @@ -1 +1 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin;st[0] \ No newline at end of file +0;button-out;id;toggle;Кнопки;Освещение;order;pin \ No newline at end of file diff --git a/data/items/input-digit.txt b/data/items/input-digit.txt index bfb99882..65c39647 100644 --- a/data/items/input-digit.txt +++ b/data/items/input-digit.txt @@ -1 +1 @@ -0;input-digit;id;inputDigit;Ввод;Введите#цифру;order;st[60] \ No newline at end of file +0;input;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file diff --git a/data/items/input-time.txt b/data/items/input-time.txt index 14c29d89..f5e69327 100644 --- a/data/items/input-time.txt +++ b/data/items/input-time.txt @@ -1 +1 @@ -0;input-time;id;inputTime;Ввод;Введите#время;order;st[10:00] \ No newline at end of file +0;input;id;inputTime;Ввод;Введите#время;order \ No newline at end of file diff --git a/data/items/output-text.txt b/data/items/output-text.txt index f96c2e26..900658f7 100644 --- a/data/items/output-text.txt +++ b/data/items/output-text.txt @@ -1 +1 @@ -0;output-text;id;anydata;Вывод;Сигнализация;order;st[Обнаружено#движение] \ No newline at end of file +0;output-text;id;anydata;Вывод;Сигнализация;order \ No newline at end of file diff --git a/data/items/pwm-out.txt b/data/items/pwm-out.txt index 39a42037..2acbb459 100644 --- a/data/items/pwm-out.txt +++ b/data/items/pwm-out.txt @@ -1 +1 @@ -0;pwm-out;id;range;Ползунки;Яркость;order;pin;st[500] \ No newline at end of file +0;pwm-out;id;range;Ползунки;Яркость;order;pin \ No newline at end of file diff --git a/data/presets/alloff.c.txt b/data/presets/alloff.c.txt index b1327a8e..fe6b26ff 100644 --- a/data/presets/alloff.c.txt +++ b/data/presets/alloff.c.txt @@ -1,7 +1,7 @@ -0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1;st[0] -0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12];st[0] -0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13];st[0] -0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14];st[0] -0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15];st[500] -0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16];st[500] -0;output-text;output-text-7;anydata;Кнопки;Статус;7;st[выключено] \ No newline at end of file +0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1 +0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12] +0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13] +0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] +0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] +0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] +0;output-text;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/data/presets/dal.c.txt b/data/presets/dal.c.txt index 7cd73ce5..9433d86e 100644 --- a/data/presets/dal.c.txt +++ b/data/presets/dal.c.txt @@ -1,5 +1,5 @@ 0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] 0;logging;log;chart;Термостат;История;2;val[temp];int[60];cnt[100] -0;input-digit;inputU;inputDigit;Термостат;Верхний#порог;3;st[30] -0;input-digit;inputL;inputDigit;Термостат;Нижний#порог;4;st[20] -0;button-out;button;toggle;Термостат;Нагрев;5;pin[12];st[0] \ No newline at end of file +0;input;inputU;inputDigit;Термостат;Верхний#порог;3 +0;input;inputL;inputDigit;Термостат;Нижний#порог;4 +0;button-out;button;toggle;Термостат;Нагрев;5;pin[12] \ No newline at end of file diff --git a/data/presets/dht.c.txt b/data/presets/dht.c.txt index 35f8dd19..29195c1c 100644 --- a/data/presets/dht.c.txt +++ b/data/presets/dht.c.txt @@ -1,5 +1,5 @@ 0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] 0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] -0;input-digit;inputU;inputDigit;Теплица;Верхний#порог;3;st[45] -0;input-digit;inputL;inputDigit;Теплица;Нижний#порог;4;st[35] -0;button-out;button;toggle;Теплица;Полив;5;pin[12];st[0] \ No newline at end of file +0;input;inputU;inputDigit;Теплица;Верхний#порог;3 +0;input;inputL;inputDigit;Теплица;Нижний#порог;4 +0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/data/presets/rel.c.txt b/data/presets/rel.c.txt index af8550f0..faf94758 100644 --- a/data/presets/rel.c.txt +++ b/data/presets/rel.c.txt @@ -1,4 +1,4 @@ -0;button-out;button1;toggle;Реле;Освещение;1;pin[12];st[0] -0;button-out;button2;toggle;Реле;Освещение;2;pin[13];st[0] -0;input-time;T1;inputTime;Реле;Введите#время#включения;3;st[10:00] -0;input-time;T2;inputTime;Реле;Введите#время#выключения;4;st[11:00] \ No newline at end of file +0;button-out;button1;toggle;Реле;Освещение;1;pin[12] +0;button-out;button2;toggle;Реле;Освещение;2;pin[13] +0;input;T1;inputTime;Реле;Введите#время#включения;3 +0;input;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index 5a97f3f8..b9874d8c 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -56,7 +56,8 @@ "title": { "#": "Выберите элемент из списка", "/set?addItem=button-out.pin": "1.Кнопка управляющая пином", - "/set?addItem=button-out.npin": "2.Кнопка виртуальная", + "/set?addItem=button-out.inv": "2.Кнопка управляющая пином (с инверсией)", + "/set?addItem=button-out.npin": "3.Кнопка виртуальная", "/set?addItem=button-in": "4.Кнопка физическая", "/set?addItem=pwm-out": "3.Широтно импульсная модуляция pwm", "/set?addItem=input-digit": "5.Окно ввода цифровых значений", @@ -167,7 +168,7 @@ }, { "type": "button", - "title": "Очистить логи сенсоров", + "title": "Очистить графики и сбросить введенные данные", "action": "/set?cleanlog", "class": "btn btn-block btn-default" }, diff --git a/include/BufferExecute.h b/include/BufferExecute.h index 77996ee6..e77d36db 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -15,7 +15,7 @@ extern void pwmOutSet(); extern void buttonIn(); extern void buttonInSet(); -extern void inputDigit(); +extern void input(); extern void inputDigitSet(); extern void inputTime(); diff --git a/include/Consts.h b/include/Consts.h index e8e7f96d..31f52549 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -33,6 +33,7 @@ //#define LAYOUT_IN_RAM //#define UDP_ENABLED //#define SSDP_ENABLED +#define SAVE_SETTINGS_TO_FLASH //=========Sensors enable/disable================================================================================================================================= #define LEVEL_ENABLED diff --git a/include/Global.h b/include/Global.h index 12adbee7..fd0205f2 100644 --- a/include/Global.h +++ b/include/Global.h @@ -74,6 +74,9 @@ extern int impuls_EnterCounter; extern String buttonOut_KeyList; extern int buttonOut_EnterCounter; //========================================= +extern String input_KeyList; +extern int input_EnterCounter; +//========================================= // Sensors extern String sensorReadingMap10sec; diff --git a/include/Utils/JsonUtils.h b/include/Utils/JsonUtils.h index c244a14e..4f60503a 100644 --- a/include/Utils/JsonUtils.h +++ b/include/Utils/JsonUtils.h @@ -16,4 +16,6 @@ String jsonWriteFloat(String& json, String name, float value); String jsonWriteBool(String& json, String name, boolean value); -void saveConfig(); \ No newline at end of file +void saveConfig(); + +void saveLive(); \ No newline at end of file diff --git a/include/items/ButtonOutClass.h b/include/items/ButtonOutClass.h deleted file mode 100644 index dca5fadb..00000000 --- a/include/items/ButtonOutClass.h +++ /dev/null @@ -1,41 +0,0 @@ -//#pragma once -//#include -// -//#include "Class/LineParsing.h" -//#include "Global.h" -// -//class ButtonOutClass : public LineParsing { -//public: -// ButtonOutClass() : LineParsing() {}; -// -// void init() { -// if (_pin != "") { -// pinMode(_pin.toInt(), OUTPUT); -// } -// jsonWriteStr(configOptionJson, _key + "_pin", _pin); -// jsonWriteStr(configOptionJson, _key + "_inv", _inv); -// } -// -// void pinStateSetDefault() { -// pinChange(_key, _state); -// } -// -// -// void pinChange(String key, String state) { -// String pin = jsonReadStr(configOptionJson, key + "_pin"); -// String inv = jsonReadStr(configOptionJson, key + "_inv"); -// int pinInt = pin.toInt(); -// -// if (inv == "") { -// digitalWrite(pinInt, state.toInt()); -// } -// else { -// digitalWrite(pinInt, !state.toInt()); -// } -// eventGen2(key, state); -// jsonWriteInt(configLiveJson, key, state.toInt()); -// publishStatus(key, state); -// } -//}; -// -//extern ButtonOutClass myButtonOut; \ No newline at end of file diff --git a/include/items/InputClass.h b/include/items/InputClass.h index 447fccfe..ec7bce19 100644 --- a/include/items/InputClass.h +++ b/include/items/InputClass.h @@ -3,29 +3,29 @@ #include "Class/LineParsing.h" #include "Global.h" -class InputClass : public LineParsing { - public: - InputClass() : LineParsing(){}; - - void inputSetDefaultFloat() { - inputSetFloat(_key, _state); - } - - void inputSetDefaultStr() { - inputSetStr(_key, _state); - } - - void inputSetFloat(String key, String state) { - eventGen2(key, state); - jsonWriteFloat(configLiveJson, key, state.toFloat()); - publishStatus(key, state); - } - - void inputSetStr(String key, String state) { - eventGen2(key, state); - jsonWriteStr(configLiveJson, key, state); - publishStatus(key, state); - } -}; - -extern InputClass myInput; \ No newline at end of file +//class InputClass : public LineParsing { +// public: +// InputClass() : LineParsing(){}; +// +// void inputSetDefaultFloat() { +// inputSetFloat(_key, _state); +// } +// +// void inputSetDefaultStr() { +// inputSetStr(_key, _state); +// } +// +// void inputSetFloat(String key, String state) { +// eventGen2(key, state); +// jsonWriteFloat(configLiveJson, key, state.toFloat()); +// publishStatus(key, state); +// } +// +// void inputSetStr(String key, String state) { +// eventGen2(key, state); +// jsonWriteStr(configLiveJson, key, state); +// publishStatus(key, state); +// } +//}; +// +//extern InputClass myInput; \ No newline at end of file diff --git a/include/items/ButtonOut.h b/include/items/vButtonOut.h similarity index 96% rename from include/items/ButtonOut.h rename to include/items/vButtonOut.h index 6d3a1863..bf918f8c 100644 --- a/include/items/ButtonOut.h +++ b/include/items/vButtonOut.h @@ -14,7 +14,6 @@ class ButtonOut { ~ButtonOut(); - void init(); void execute(String state); private: diff --git a/include/items/ImpulsOutClass.h b/include/items/vImpulsOut.h similarity index 100% rename from include/items/ImpulsOutClass.h rename to include/items/vImpulsOut.h diff --git a/include/items/vInput.h b/include/items/vInput.h new file mode 100644 index 00000000..9e465b52 --- /dev/null +++ b/include/items/vInput.h @@ -0,0 +1,29 @@ +#pragma once +#include + +#include "Global.h" + +class Input; + +typedef std::vector MyInputVector; + +class Input { + public: + + Input(String key); + + ~Input(); + + void execute(String state); + + private: + + String _key; + + void addNewDelOldData(const String filename, size_t maxPoints, String payload); +}; + +extern MyInputVector* myInput; + +extern void input(); +extern void inputExecute(); diff --git a/include/items/Logging.h b/include/items/vLogging.h similarity index 100% rename from include/items/Logging.h rename to include/items/vLogging.h diff --git a/include/items/SensorDallas.h b/include/items/vSensorDallas.h similarity index 100% rename from include/items/SensorDallas.h rename to include/items/vSensorDallas.h diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 16584f2c..901d9150 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -1,7 +1,7 @@ #include "BufferExecute.h" // -#include "items/SensorDallas.h" -#include "items/ButtonOut.h" +#include "items/vSensorDallas.h" +#include "items/vButtonOut.h" // #include "Global.h" #include "Module/Terminal.h" @@ -46,10 +46,10 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), buttonIn); } else if (order == F("input-digit")) { - sCmd.addCommand(order.c_str(), inputDigit); + sCmd.addCommand(order.c_str(), input); } else if (order == F("input-time")) { - sCmd.addCommand(order.c_str(), inputTime); + //sCmd.addCommand(order.c_str(), inputTime); } else if (order == F("output-text")) { sCmd.addCommand(order.c_str(), textOut); diff --git a/src/Global.cpp b/src/Global.cpp index 22731505..d8facc43 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -45,6 +45,9 @@ int impuls_EnterCounter = -1; String buttonOut_KeyList = ""; int buttonOut_EnterCounter = -1; //========================================= +String input_KeyList = ""; +int input_EnterCounter = -1; +//========================================= // Sensors String sensorReadingMap10sec; diff --git a/src/Init.cpp b/src/Init.cpp index fe97b678..36d98c21 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -2,16 +2,21 @@ #include "BufferExecute.h" #include "Cmd.h" #include "Global.h" -#include "items/Logging.h" -#include "items/ImpulsOutClass.h" -#include "items/ButtonOut.h" -#include "items/SensorDallas.h" +#include "items/vLogging.h" +#include "items/vImpulsOut.h" +#include "items/vButtonOut.h" +#include "items/vSensorDallas.h" +#include "items/vInput.h" void loadConfig() { configSetupJson = readFile("config.json", 4096); - //configSetupJson.replace(" ", ""); configSetupJson.replace("\r\n", ""); +#ifdef SAVE_SETTINGS_TO_FLASH + configLiveJson = readFile("live.json", 4096); + configLiveJson.replace("\r\n", ""); +#endif + jsonWriteStr(configSetupJson, "chipID", chipId); jsonWriteInt(configSetupJson, "firmware_version", FIRMWARE_VERSION); @@ -53,6 +58,12 @@ void Device_init() { } buttonOut_KeyList = ""; buttonOut_EnterCounter = -1; + //======clear input params======= + if (myInput != nullptr) { + myInput->clear(); + } + input_KeyList = ""; + input_EnterCounter = -1; //=================================== diff --git a/src/ItemsCmd.cpp b/src/ItemsCmd.cpp index be6330ac..cbac328a 100644 --- a/src/ItemsCmd.cpp +++ b/src/ItemsCmd.cpp @@ -7,7 +7,7 @@ //#include "Module/Terminal.h" //#include "Servo/Servos.h" // -//#include "items/SensorDallas.h" +//#include "items/vSensorDallas.h" // //Terminal *term = nullptr; // @@ -27,7 +27,7 @@ //sCmd.addCommand("pwm-out", pwmOut); //sCmd.addCommand("button-in", buttonIn); - //sCmd.addCommand("input-digit", inputDigit); + //sCmd.addCommand("input-digit", input); //sCmd.addCommand("input-time", inputTime); //sCmd.addCommand("output-text", textOut); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index bbe52dc2..cdd18448 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -1,7 +1,7 @@ #include "MqttClient.h" #include -#include "items/Logging.h" +#include "items/vLogging.h" #include "Class/NotAsync.h" #include "Global.h" #include "Init.h" diff --git a/src/Utils/JsonUtils.cpp b/src/Utils/JsonUtils.cpp index 2389773c..fad6c937 100644 --- a/src/Utils/JsonUtils.cpp +++ b/src/Utils/JsonUtils.cpp @@ -53,4 +53,10 @@ String jsonWriteFloat(String& json, String name, float value) { void saveConfig() { writeFile(String("config.json"), configSetupJson); +} + +void saveLive() { +#ifdef SAVE_SETTINGS_TO_FLASH + writeFile(String("live.json"), configLiveJson); +#endif } \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index cd90a2ca..d3926cba 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -3,7 +3,7 @@ #include "Global.h" #include "Init.h" #include "ItemsList.h" -#include "items/Logging.h" +#include "items/vLogging.h" #include "Telegram.h" bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { diff --git a/src/items/ButtonOutClass.cpp b/src/items/ButtonOutClass.cpp deleted file mode 100644 index 5de6ca5e..00000000 --- a/src/items/ButtonOutClass.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//#include "items/ButtonOutClass.h" -// -//#include "BufferExecute.h" -////==========================================Модуль кнопок=================================================== -////button-out light toggle Кнопки Свет 1 pin[12] inv[1] st[1] -////========================================================================================================== -//ButtonOutClass myButtonOut; -//void buttonOut() { -// myButtonOut.update(); -// String key = myButtonOut.gkey(); -// String pin = myButtonOut.gpin(); -// String inv = myButtonOut.ginv(); -// sCmd.addCommand(key.c_str(), buttonOutSet); -// myButtonOut.init(); -// myButtonOut.pinStateSetDefault(); -// myButtonOut.clear(); -//} -// -//void buttonOutSet() { -// String key = sCmd.order(); -// String state = sCmd.next(); -// myButtonOut.pinChange(key, state); -//} diff --git a/src/items/InputDigitClass.cpp b/src/items/InputDigitClass.cpp index 1a2e3b38..d9f1383a 100644 --- a/src/items/InputDigitClass.cpp +++ b/src/items/InputDigitClass.cpp @@ -1,19 +1,19 @@ -#include "BufferExecute.h" -#include "items/InputClass.h" -//==========================================Модуль ввода цифровых значений================================== -//input-digit digit1 inputDigit Ввод Введите.цифру 4 st[60] -//========================================================================================================== -InputClass myInputDigit; -void inputDigit() { - myInputDigit.update(); - String key = myInputDigit.gkey(); - sCmd.addCommand(key.c_str(), inputDigitSet); - myInputDigit.inputSetDefaultFloat(); - myInputDigit.clear(); -} - -void inputDigitSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myInputDigit.inputSetFloat(key, state); -} \ No newline at end of file +//#include "BufferExecute.h" +//#include "items/InputClass.h" +////==========================================Модуль ввода цифровых значений================================== +////input-digit digit1 inputDigit Ввод Введите.цифру 4 st[60] +////========================================================================================================== +//InputClass myInputDigit; +//void inputDigit() { +// myInputDigit.update(); +// String key = myInputDigit.gkey(); +// sCmd.addCommand(key.c_str(), inputDigitSet); +// myInputDigit.inputSetDefaultFloat(); +// myInputDigit.clear(); +//} +// +//void inputDigitSet() { +// String key = sCmd.order(); +// String state = sCmd.next(); +// myInputDigit.inputSetFloat(key, state); +//} \ No newline at end of file diff --git a/src/items/InputTimeClass.cpp b/src/items/InputTimeClass.cpp index 9dc16dd4..ae5ce8aa 100644 --- a/src/items/InputTimeClass.cpp +++ b/src/items/InputTimeClass.cpp @@ -2,20 +2,20 @@ #include "items/InputClass.h" //==========================================Модуль ввода времени============================================ //========================================================================================================== -InputClass myInputTime; -void inputTime() { - myInputTime.update(); - String key = myInputTime.gkey(); - sCmd.addCommand(key.c_str(), inputTimeSet); - myInputTime.inputSetDefaultStr(); - myInputTime.clear(); -} - -void inputTimeSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myInputTime.inputSetStr(key, state); -} +//InputClass myInputTime; +//void inputTime() { +// myInputTime.update(); +// String key = myInputTime.gkey(); +// sCmd.addCommand(key.c_str(), inputTimeSet); +// myInputTime.inputSetDefaultStr(); +// myInputTime.clear(); +//} +// +//void inputTimeSet() { +// String key = sCmd.order(); +// String state = sCmd.next(); +// myInputTime.inputSetStr(key, state); +//} void handle_time_init() { ts.add( diff --git a/src/items/ButtonOut.cpp b/src/items/vButtonOut.cpp similarity index 68% rename from src/items/ButtonOut.cpp rename to src/items/vButtonOut.cpp index 26d574a2..bb4759ec 100644 --- a/src/items/ButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -1,4 +1,4 @@ -#include "items/ButtonOut.h" +#include "items/vButtonOut.h" #include @@ -10,22 +10,28 @@ ButtonOut::ButtonOut(unsigned int pin, boolean inv, String key) { _pin = pin; _inv = inv; _key = key; + pinMode(_pin, OUTPUT); + int state = jsonReadInt(configLiveJson, key); + this->execute(String(state)); } ButtonOut::~ButtonOut() {} -void ButtonOut::init() { - pinMode(_pin, OUTPUT); -} - void ButtonOut::execute(String state) { - //if (_inv) { - // digitalWrite(_pin, !state.toInt()); - //} - //else { - digitalWrite(_pin, state.toInt()); - //} + if (state == "change") { + state = String(!digitalRead(_pin)); + digitalWrite(_pin, state.toInt()); + } + else { + if (_inv) { + digitalWrite(_pin, !state.toInt()); + } + else { + digitalWrite(_pin, state.toInt()); + } + } eventGen2(_key, state); jsonWriteInt(configLiveJson, _key, state.toInt()); + saveLive(); publishStatus(_key, state); } @@ -36,6 +42,10 @@ void buttonOut() { String key = myLineParsing.gkey(); String pin = myLineParsing.gpin(); String inv = myLineParsing.ginv(); + + bool invb = false; + if (inv.toInt() == 1) invb = true; + myLineParsing.clear(); buttonOut_EnterCounter++; @@ -44,7 +54,7 @@ void buttonOut() { static bool firstTime = true; if (firstTime) myButtonOut = new MyButtonOutVector(); firstTime = false; - myButtonOut->push_back(ButtonOut(pin.toInt(), inv.toInt(), key)); + myButtonOut->push_back(ButtonOut(pin.toInt(), invb, key)); sCmd.addCommand(key.c_str(), buttonOutExecute); } diff --git a/src/items/ImpulsOutClass.cpp b/src/items/vImpulsOut.cpp similarity index 98% rename from src/items/ImpulsOutClass.cpp rename to src/items/vImpulsOut.cpp index 172e89e4..911fdd22 100644 --- a/src/items/ImpulsOutClass.cpp +++ b/src/items/vImpulsOut.cpp @@ -1,4 +1,4 @@ -#include "items/ImpulsOutClass.h" +#include "items/vImpulsOut.h" #include #include "BufferExecute.h" diff --git a/src/items/vInput.cpp b/src/items/vInput.cpp new file mode 100644 index 00000000..7689ff2a --- /dev/null +++ b/src/items/vInput.cpp @@ -0,0 +1,53 @@ +#include "items/vInput.h" + +#include + +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" + +Input::Input(String key) { + _key = key; + String value = jsonReadStr(configLiveJson, key); + this->execute(value); +} +Input::~Input() {} + +void Input::execute(String state) { + eventGen2(_key, state); + jsonWriteInt(configLiveJson, _key, state.toInt()); + saveLive(); + publishStatus(_key, state); +} + +MyInputVector* myInput = nullptr; + +void input() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + myLineParsing.clear(); + + input_EnterCounter++; + addKey(key, input_KeyList, input_EnterCounter); + + static bool firstTime = true; + if (firstTime) myInput = new MyInputVector(); + firstTime = false; + myInput->push_back(Input(key)); + + sCmd.addCommand(key.c_str(), inputExecute); +} + +void inputExecute() { + String key = sCmd.order(); + String state = sCmd.next(); + + int number = getKeyNum(key, input_KeyList); + + if (myInput != nullptr) { + if (number != -1) { + myInput->at(number).execute(state); + } + } +} + diff --git a/src/items/Logging.cpp b/src/items/vLogging.cpp similarity index 98% rename from src/items/Logging.cpp rename to src/items/vLogging.cpp index 2f178d23..7777ef79 100644 --- a/src/items/Logging.cpp +++ b/src/items/vLogging.cpp @@ -1,4 +1,4 @@ -#include "items/Logging.h" +#include "items/vLogging.h" #include @@ -120,5 +120,6 @@ void clean_log_date() { SerialPrint("I", "System", fname); removeFile("logs/" + fname); } + removeFile("live.json"); #endif } diff --git a/src/items/SensorDallas.cpp b/src/items/vSensorDallas.cpp similarity index 97% rename from src/items/SensorDallas.cpp rename to src/items/vSensorDallas.cpp index 1abadc25..323bca12 100644 --- a/src/items/SensorDallas.cpp +++ b/src/items/vSensorDallas.cpp @@ -1,4 +1,4 @@ -#include "items/SensorDallas.h" +#include "items/vSensorDallas.h" #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" diff --git a/src/main.cpp b/src/main.cpp index 4a809cbd..4751e813 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,9 +15,9 @@ #include "Utils/Timings.h" #include "Utils/WebUtils.h" #include "items/ButtonInClass.h" -#include "items/Logging.h" -#include "items/ImpulsOutClass.h" -#include "items/SensorDallas.h" +#include "items/vLogging.h" +#include "items/vImpulsOut.h" +#include "items/vSensorDallas.h" #include "Telegram.h" void not_async_actions(); From 94d93d3be61972bb6c44793466cf2d0276439c45 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 15 Nov 2020 02:46:27 +0300 Subject: [PATCH 08/94] Working version --- data/set.device.json | 12 ++++++------ include/Consts.h | 6 +++++- include/Utils/TimeUtils.h | 4 +++- include/items/InputClass.h | 31 ------------------------------- include/items/vInput.h | 8 +++----- include/items/vLogging.h | 2 +- src/BufferExecute.cpp | 5 +---- src/ItemsList.cpp | 2 ++ src/Utils/TimeUtils.cpp | 16 +++++++++++++++- src/Web.cpp | 2 +- src/items/InputDigitClass.cpp | 19 ------------------- src/items/InputTimeClass.cpp | 32 -------------------------------- src/items/vInput.cpp | 27 +++++++++++++++++++-------- src/items/vLogging.cpp | 3 ++- 14 files changed, 58 insertions(+), 111 deletions(-) delete mode 100644 include/items/InputClass.h delete mode 100644 src/items/InputDigitClass.cpp delete mode 100644 src/items/InputTimeClass.cpp diff --git a/data/set.device.json b/data/set.device.json index b9874d8c..16c964c0 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -154,6 +154,12 @@ { "type": "hr" }, + { + "type": "button", + "title": "Очистить графики и сбросить введенные данные", + "action": "/set?cleanlog", + "class": "btn btn-block btn-default" + }, { "type": "link", "title": "Ручная настройка", @@ -166,12 +172,6 @@ "action": "https://github.com/IoTManagerProject/IoTManager/wiki", "class": "btn btn-block btn-default" }, - { - "type": "button", - "title": "Очистить графики и сбросить введенные данные", - "action": "/set?cleanlog", - "class": "btn btn-block btn-default" - }, { "type": "hr" }, diff --git a/include/Consts.h b/include/Consts.h index 31f52549..4e65e5e3 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -113,4 +113,8 @@ enum ConfigType_t { //13.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.6% (used 38208 bytes from 81920 bytes) -//Flash: [===== ] 54.2% (used 566388 bytes from 1044464 bytes) \ No newline at end of file +//Flash: [===== ] 54.2% (used 566388 bytes from 1044464 bytes) + +//15.11.2020 (SSDP OFF, UDP OFF) +//RAM: [===== ] 46.1% (used 37780 bytes from 81920 bytes) +//Flash: [===== ] 54.3% (used 566656 bytes from 1044464 bytes) \ No newline at end of file diff --git a/include/Utils/TimeUtils.h b/include/Utils/TimeUtils.h index d20286cf..024d1bc8 100644 --- a/include/Utils/TimeUtils.h +++ b/include/Utils/TimeUtils.h @@ -48,4 +48,6 @@ int getOffsetInMinutes(int timezone); /* * Разбивает время на составляющие */ -void breakEpochToTime(unsigned long epoch, Time_t& tm); \ No newline at end of file +void breakEpochToTime(unsigned long epoch, Time_t& tm); + +void handle_time_init(); \ No newline at end of file diff --git a/include/items/InputClass.h b/include/items/InputClass.h deleted file mode 100644 index ec7bce19..00000000 --- a/include/items/InputClass.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "Class/LineParsing.h" -#include "Global.h" - -//class InputClass : public LineParsing { -// public: -// InputClass() : LineParsing(){}; -// -// void inputSetDefaultFloat() { -// inputSetFloat(_key, _state); -// } -// -// void inputSetDefaultStr() { -// inputSetStr(_key, _state); -// } -// -// void inputSetFloat(String key, String state) { -// eventGen2(key, state); -// jsonWriteFloat(configLiveJson, key, state.toFloat()); -// publishStatus(key, state); -// } -// -// void inputSetStr(String key, String state) { -// eventGen2(key, state); -// jsonWriteStr(configLiveJson, key, state); -// publishStatus(key, state); -// } -//}; -// -//extern InputClass myInput; \ No newline at end of file diff --git a/include/items/vInput.h b/include/items/vInput.h index 9e465b52..52a6c80b 100644 --- a/include/items/vInput.h +++ b/include/items/vInput.h @@ -10,17 +10,15 @@ typedef std::vector MyInputVector; class Input { public: - Input(String key); - + Input(String key, String widget); ~Input(); - - void execute(String state); + + void execute(String value); private: String _key; - void addNewDelOldData(const String filename, size_t maxPoints, String payload); }; extern MyInputVector* myInput; diff --git a/include/items/vLogging.h b/include/items/vLogging.h index 9f8094c7..1fb3f09c 100644 --- a/include/items/vLogging.h +++ b/include/items/vLogging.h @@ -31,4 +31,4 @@ extern MyLoggingVector* myLogging; extern void choose_log_date_and_send(); extern void sendLogData(String file, String topic); -extern void clean_log_date(); +extern void cleanLogAndData(); diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 901d9150..dcab1cd6 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -45,12 +45,9 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("button-in")) { sCmd.addCommand(order.c_str(), buttonIn); } - else if (order == F("input-digit")) { + else if (order == F("input")) { sCmd.addCommand(order.c_str(), input); } - else if (order == F("input-time")) { - //sCmd.addCommand(order.c_str(), inputTime); - } else if (order == F("output-text")) { sCmd.addCommand(order.c_str(), textOut); } diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index d4d1cf8a..926eb313 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -68,6 +68,8 @@ void addPreset(String name) { void delAllItems() { removeFile(DEVICE_CONFIG_FILE); addFile(DEVICE_CONFIG_FILE, String(firstLine)); + removeFile(DEVICE_SCENARIO_FILE); + addFile(DEVICE_SCENARIO_FILE, "//"); removeFile("id.txt"); removeFile("order.txt"); removeFile("pins.txt"); diff --git a/src/Utils/TimeUtils.cpp b/src/Utils/TimeUtils.cpp index 2b313e69..5420b4a1 100644 --- a/src/Utils/TimeUtils.cpp +++ b/src/Utils/TimeUtils.cpp @@ -1,5 +1,5 @@ #include "Utils/TimeUtils.h" - +#include "Global.h" #include "Utils/StringUtils.h" static const uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -211,3 +211,17 @@ void breakEpochToTime(unsigned long epoch, Time_t& tm) { tm.day_of_month = time + 1; // day of month tm.valid = (epoch > MIN_DATETIME); } + +void handle_time_init() { + ts.add( + TIME, 1000, [&](void*) { + String timenow = timeNow->getTimeWOsec(); + static String prevTime; + if (prevTime != timenow) { + prevTime = timenow; + jsonWriteStr(configLiveJson, "timenow", timenow); + eventGen2("timenow", timenow); + } + }, + nullptr, true); +} diff --git a/src/Web.cpp b/src/Web.cpp index d3926cba..9b2a84e0 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -59,7 +59,7 @@ void web_init() { #ifdef LOGGING_ENABLED if (request->hasArg("cleanlog")) { - clean_log_date(); + cleanLogAndData(); request->send(200); } #endif diff --git a/src/items/InputDigitClass.cpp b/src/items/InputDigitClass.cpp deleted file mode 100644 index d9f1383a..00000000 --- a/src/items/InputDigitClass.cpp +++ /dev/null @@ -1,19 +0,0 @@ -//#include "BufferExecute.h" -//#include "items/InputClass.h" -////==========================================Модуль ввода цифровых значений================================== -////input-digit digit1 inputDigit Ввод Введите.цифру 4 st[60] -////========================================================================================================== -//InputClass myInputDigit; -//void inputDigit() { -// myInputDigit.update(); -// String key = myInputDigit.gkey(); -// sCmd.addCommand(key.c_str(), inputDigitSet); -// myInputDigit.inputSetDefaultFloat(); -// myInputDigit.clear(); -//} -// -//void inputDigitSet() { -// String key = sCmd.order(); -// String state = sCmd.next(); -// myInputDigit.inputSetFloat(key, state); -//} \ No newline at end of file diff --git a/src/items/InputTimeClass.cpp b/src/items/InputTimeClass.cpp deleted file mode 100644 index ae5ce8aa..00000000 --- a/src/items/InputTimeClass.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "BufferExecute.h" -#include "items/InputClass.h" -//==========================================Модуль ввода времени============================================ -//========================================================================================================== -//InputClass myInputTime; -//void inputTime() { -// myInputTime.update(); -// String key = myInputTime.gkey(); -// sCmd.addCommand(key.c_str(), inputTimeSet); -// myInputTime.inputSetDefaultStr(); -// myInputTime.clear(); -//} -// -//void inputTimeSet() { -// String key = sCmd.order(); -// String state = sCmd.next(); -// myInputTime.inputSetStr(key, state); -//} - -void handle_time_init() { - ts.add( - TIME, 1000, [&](void*) { - String timenow = timeNow->getTimeWOsec(); - static String prevTime; - if (prevTime != timenow) { - prevTime = timenow; - jsonWriteStr(configLiveJson, "timenow", timenow); - eventGen2("timenow", timenow); - } - }, - nullptr, true); -} \ No newline at end of file diff --git a/src/items/vInput.cpp b/src/items/vInput.cpp index 7689ff2a..7a71b472 100644 --- a/src/items/vInput.cpp +++ b/src/items/vInput.cpp @@ -6,24 +6,35 @@ #include "Global.h" #include "BufferExecute.h" -Input::Input(String key) { +Input::Input(String key, String widget) { _key = key; String value = jsonReadStr(configLiveJson, key); + + if (value == "") { + if (widget.indexOf("Digit") != -1) { + value = "52"; + } + if (widget.indexOf("Time") != -1) { + value = "12:00"; + } + } + this->execute(value); } Input::~Input() {} -void Input::execute(String state) { - eventGen2(_key, state); - jsonWriteInt(configLiveJson, _key, state.toInt()); +void Input::execute(String value) { + eventGen2(_key, value); + jsonWriteStr(configLiveJson, _key, value); saveLive(); - publishStatus(_key, state); + publishStatus(_key, value); } MyInputVector* myInput = nullptr; void input() { myLineParsing.update(); + String widget = myLineParsing.gfile(); String key = myLineParsing.gkey(); myLineParsing.clear(); @@ -33,20 +44,20 @@ void input() { static bool firstTime = true; if (firstTime) myInput = new MyInputVector(); firstTime = false; - myInput->push_back(Input(key)); + myInput->push_back(Input(key, widget)); sCmd.addCommand(key.c_str(), inputExecute); } void inputExecute() { String key = sCmd.order(); - String state = sCmd.next(); + String value = sCmd.next(); int number = getKeyNum(key, input_KeyList); if (myInput != nullptr) { if (number != -1) { - myInput->at(number).execute(state); + myInput->at(number).execute(value); } } } diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 7777ef79..473fe5b0 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -112,7 +112,7 @@ void sendLogData(String file, String topic) { } } -void clean_log_date() { +void cleanLogAndData() { #ifdef ESP8266 auto dir = LittleFS.openDir("logs"); while (dir.next()) { @@ -121,5 +121,6 @@ void clean_log_date() { removeFile("logs/" + fname); } removeFile("live.json"); + configLiveJson = ""; #endif } From 3766a744f49ae796c46b7eb8e3f001172075145c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 15 Nov 2020 02:52:24 +0300 Subject: [PATCH 09/94] bug fixed with scenario init --- src/ItemsList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 926eb313..290380f2 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -63,6 +63,7 @@ void addPreset(String name) { String scenario = readFile("presets/" + name + ".txt", 4048); removeFile(DEVICE_SCENARIO_FILE); addFile(DEVICE_SCENARIO_FILE, scenario); + loadScenario(); } void delAllItems() { From 6f45148d753be536c107acbc4de1a51786214b56 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 15 Nov 2020 03:09:21 +0300 Subject: [PATCH 10/94] cleaning --- include/Module/CharBuffer.h | 72 ------- include/Module/CircularBuffer.h | 86 --------- include/Module/CommandShell.h | 45 ----- include/Module/EditLine.h | 68 ------- include/Module/Module.h | 84 --------- include/Module/Runner.h | 17 -- include/Module/Telnet.h | 51 ----- include/Module/Terminal.h | 186 ------------------- src/BufferExecute.cpp | 6 +- src/Module/Telnet.cpp | 86 --------- src/Module/Terminal.cpp | 320 -------------------------------- 11 files changed, 1 insertion(+), 1020 deletions(-) delete mode 100644 include/Module/CharBuffer.h delete mode 100644 include/Module/CircularBuffer.h delete mode 100644 include/Module/CommandShell.h delete mode 100644 include/Module/EditLine.h delete mode 100644 include/Module/Module.h delete mode 100644 include/Module/Runner.h delete mode 100644 include/Module/Telnet.h delete mode 100644 include/Module/Terminal.h delete mode 100644 src/Module/Telnet.cpp delete mode 100644 src/Module/Terminal.cpp diff --git a/include/Module/CharBuffer.h b/include/Module/CharBuffer.h deleted file mode 100644 index 74fd922d..00000000 --- a/include/Module/CharBuffer.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include - -class CharBuffer : Print { - public: - CharBuffer(size_t size) : _capacity(size < 2 ? 2 : size), _write(0), _read(0) { - _pool = new char[_capacity + 1]; - memset(_pool, 0, _capacity + 1); - } - - CharBuffer(const CharBuffer &src) { - _capacity = src._capacity; - _write = src._write; - memcpy(_pool, src._pool, src._write); - } - - CharBuffer(const char *str) : CharBuffer(strlen(str) + 1) { - write((const uint8_t *)str, strlen(str)); - } - - ~CharBuffer() { - delete _pool; - } - - void clear() { - memset(_pool, 0, _capacity); - _write = 0; - _read = 0; - } - - size_t size() const { return _capacity; } - - size_t free() const { return _capacity - _write - 2; } - - size_t available() const { return _write; } - - const char *c_str() { - if (_pool[_write] != '\x00') - _pool[_write] = '\x00'; - return _pool; - } - - size_t write(char ch) { - return write((uint8_t)ch); - }; - - size_t write(const uint8_t ch) { - size_t n = 0; - if (_write < (_capacity - 2)) { - _pool[_write++] = ch; - n = 1; - } - return n; - } - - size_t write(const uint8_t *ptr, const size_t size) { - size_t n = 0; - while (n < size) { - uint8_t ch = ptr[n++]; - if (!write(ch)) - break; - } - return n; - } - - protected: - char *_pool; - size_t _capacity; - size_t _write; - size_t _read; -}; diff --git a/include/Module/CircularBuffer.h b/include/Module/CircularBuffer.h deleted file mode 100644 index 5c197652..00000000 --- a/include/Module/CircularBuffer.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include - -template -class CircularBuffer { - public: - CircularBuffer() : _head{0}, _tail{0}, _full{false} {} - - ~CircularBuffer() {} - - void reset() { - _head = _tail = _full = 0; - } - - bool empty() const { - return _head == _tail && !_full; - } - - bool full() const { - return _full; - } - - size_t size() const { - size_t res = 0; - if (!_full) { - if (_head < _tail) - res = BUFFER_SIZE + _head - _tail; - else - res = _head - _tail; - } else { - res = BUFFER_SIZE; - } - return res; - } - - void push(const T &item) { - if (_full) { - _tail++; - if (_tail == BUFFER_SIZE) - _tail = 0; - } - _pool[_head++] = item; - if (_head == BUFFER_SIZE) - _head = 0; - if (_head == _tail) - _full = true; - } - - bool pop(T &item) { - bool res = false; - if (!empty()) { - item = _pool[_tail++]; - if (_tail == BUFFER_SIZE) _tail = 0; - _full = false; - res = true; - } - return res; - } - - bool pop_back(T &item) { - bool res = false; - if (!empty()) { - item = _pool[--_head]; - _full = false; - res = true; - } - return res; - } - - bool peek(T &item) const { - bool res = false; - if (!empty()) { - item = _pool[_tail]; - - res = true; - } - return res; - } - - private: - T _pool[BUFFER_SIZE]; - size_t _head; - size_t _tail; - bool _full; -}; \ No newline at end of file diff --git a/include/Module/CommandShell.h b/include/Module/CommandShell.h deleted file mode 100644 index 6a986639..00000000 --- a/include/Module/CommandShell.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -#include "Module/Terminal.h" -#include "Module/CircularBuffer.h" -#include "Module/Runner.h" - -class CommandShell { - public: - CommandShell(Runner *runner); - - void setTerm(Terminal *term); - Terminal *term(); - - void showGreetings(bool = true); - void showFarewell(bool = true); - - void clearHistory(); - void addHistory(const char *); - bool getHistoryInput(String &); - void setEditLine(const String &); - bool active(); - void loop(); - - private: - size_t printGreetings(Print *); - size_t printFarewell(Print *); - size_t printPrompt(Print *); - - void onOpen(Print *out); - void onClose(Print *out); - void onData(const char *); - void onHistory(Print *out); - bool getLastInput(String &); - - private: - CircularBuffer _history; - Terminal *_term; - Runner *_runner; - String _path; - bool _active; - bool _greetings; - bool _farewell; -}; diff --git a/include/Module/EditLine.h b/include/Module/EditLine.h deleted file mode 100644 index 4368fb7f..00000000 --- a/include/Module/EditLine.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "Module/CharBuffer.h" - -class EditLine : public CharBuffer { - public: - EditLine(size_t size) : CharBuffer(size){}; - - char &operator[](size_t i) { return _pool[i]; } - - char operator[](size_t i) const { return _pool[i]; } - - EditLine &operator=(const EditLine &src) { - delete[] _pool; - _pool = new char[src._capacity]; - memcpy(_pool, src._pool, src._capacity); - _read = src._read; - _write = src._write; - return *this; - } - - void del() { - size_t i; - for (i = _write; i < _capacity; ++i) - _pool[i] = _pool[i + 1]; - _pool[i] = '\x00'; - } - - bool backspace() { - bool res = false; - if (prev()) { - del(); - res = true; - } - return res; - } - - bool next() { - bool res = false; - if (_write < _capacity - 1) { - _write++; - res = true; - } - return res; - } - - bool prev() { - bool res = false; - if (_write > 0) { - _write--; - res = true; - } - return res; - } - - size_t home() { - size_t res = _write; - _write = 0; - return res; - } - - size_t end() { - size_t n; - for (n = 0; n < _capacity - 1; ++n) - if (_pool[n] == '\x00') break; - return n; - } -}; diff --git a/include/Module/Module.h b/include/Module/Module.h deleted file mode 100644 index 800d5919..00000000 --- a/include/Module/Module.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include - -enum ModuleState_t { - MOD_INIT, - MOD_INIT_FAILED, - MOD_INIT_COMPLETE, - MOD_START_FAILED, - MOD_ACTIVE -}; - -class Module { - protected: - virtual bool onInit() { return true; }; - virtual void onEnd(){}; - virtual bool onStart() { return true; } - virtual void onStop(){}; - virtual void onLoop() = 0; - - protected: - Print *_out; - - public: - Module() : _state{MOD_INIT} {} - - bool init(bool force = false) { - if (_state > MOD_INIT_COMPLETE) { - return true; - } - if (_state == MOD_INIT_FAILED && !force) { - return false; - } - - _state = onInit() ? MOD_INIT_COMPLETE : MOD_INIT_FAILED; - - return _state == MOD_INIT_COMPLETE; - } - - bool start(bool force = false) { - if (_state == MOD_ACTIVE) { - return true; - } - if (_state == MOD_START_FAILED && !force) { - return false; - } - if (_state < MOD_INIT_COMPLETE) { - if (!init(force)) { - return false; - } - } - _state = onStart() ? MOD_ACTIVE : MOD_START_FAILED; - return _state == MOD_ACTIVE; - } - - void stop() { - if (_state < MOD_ACTIVE) { - return; - } - onStop(); - _state = MOD_INIT_COMPLETE; - }; - - void end() { - if (_state < MOD_INIT_FAILED) { - return; - } - onEnd(); - _state = MOD_INIT; - }; - - void loop() { - if (_state == MOD_ACTIVE || start()) onLoop(); - }; - - void setOutput(Print *p) { _out = p; } - - ModuleState_t getState() { - return _state; - } - - private: - ModuleState_t _state; -}; diff --git a/include/Module/Runner.h b/include/Module/Runner.h deleted file mode 100644 index 45ca7403..00000000 --- a/include/Module/Runner.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include "Cmd.h" - -class Runner { - public: - virtual void run(const char*, Print*); -}; - -class CmdRunner : public Runner { - public: - void run(const char* cmd, Print* out) override { - String cmdStr{cmd}; - csvCmdExecute(cmdStr); - } -}; \ No newline at end of file diff --git a/include/Module/Telnet.h b/include/Module/Telnet.h deleted file mode 100644 index ade6e4f2..00000000 --- a/include/Module/Telnet.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Global.h" - -#include "Module/Module.h" -#include "Module/Terminal.h" -#include "Module/CommandShell.h" - -#include - -enum TelnetEvent_t { - TE_CONNECTED, - TE_DISCONNECTED -}; - -typedef std::function TelnetEventHandler; - -class Telnet : public Module { - public: - Telnet(uint16_t port) : _port{port}, _lastConnected{false} {}; - - public: - void setEventHandler(TelnetEventHandler); - void sendData(const String&); - bool hasClient(); - bool isShellActive(); - void setCommandShell(CommandShell*); - - protected: - bool onInit() override; - void onEnd() override; - bool onStart() override; - void onStop() override; - void onLoop() override; - - private: - void onConnect(); - void onDisconnect(); - void onData(); - void onOpen(); - void onClose(); - - private: - TelnetEventHandler _eventHandler; - uint16_t _port; - bool _lastConnected; - WiFiClient _client; - WiFiServer* _server; - Terminal* _term; - CommandShell* _shell; -}; diff --git a/include/Module/Terminal.h b/include/Module/Terminal.h deleted file mode 100644 index cbb8a6f8..00000000 --- a/include/Module/Terminal.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include - -#include "Module/EditLine.h" -#include - -#define A_NORMAL 0x0000 // normal -#define A_UNDERLINE 0x0001 // underline -#define A_REVERSE 0x0002 // reverse -#define A_BLINK 0x0004 // blink -#define A_BOLD 0x0008 // bold -#define A_DIM 0x0010 // dim -#define A_STANDOUT A_BOLD // standout (same as bold) - -#define F_BLACK 0x0100 // foreground black -#define F_RED 0x0200 // foreground red -#define F_GREEN 0x0300 // foreground green -#define F_BROWN 0x0400 // foreground brown -#define F_BLUE 0x0500 // foreground blue -#define F_MAGENTA 0x0600 // foreground magenta -#define F_CYAN 0x0700 // foreground cyan -#define F_WHITE 0x0800 // foreground white -#define F_YELLOW F_BROWN // some terminals show brown as yellow (with A_BOLD) -#define F_COLOR 0x0F00 // foreground mask - -#define B_BLACK 0x1000 // background black -#define B_RED 0x2000 // background red -#define B_GREEN 0x3000 // background green -#define B_BROWN 0x4000 // background brown -#define B_BLUE 0x5000 // background blue -#define B_MAGENTA 0x6000 // background magenta -#define B_CYAN 0x7000 // background cyan -#define B_WHITE 0x8000 // background white -#define B_YELLOW B_BROWN // some terminals show brown as yellow (with A_BOLD) -#define B_COLOR 0xF000 // background mask - -#define CHAR_NULL 0x00 -#define CHAR_BEL 0x07 -#define CHAR_BS 0x08 -#define CHAR_SPACE 0x20 -#define CHAR_TAB 0x09 -#define CHAR_LF 0x0a -#define CHAR_CR 0x0d -#define CHR_ZERO 0x30 - -#define KEY_DEL 0x7f -#define KEY_DOWN 0x80 -#define KEY_UP 0x81 -#define KEY_LEFT 0x82 -#define KEY_RIGHT 0x83 -#define KEY_HOME 0x84 -#define KEY_INS 0x86 -#define KEY_PAGE_DOWN 0x87 -#define KEY_PAGE_UP 0x88 -#define KEY_END 0x89 -#define CHAR_LT 0x8b -#define CHAR_CSI 0x9b -#define CHAR_ESC 0x1b -#define CHAR_BIN 0xFF - -#define ESC_CURSOR_HOME "\x1b[H" -#define ESC_SAVE_CURSOR "\x1b[s" -#define ESC_UNSAVE_CURSOR "\x1b[u" -#define ESC_SAVE_CURSOR_AND_ATTRS "\x1b[7" -#define ESC_RESTORE_CURSOR_AND_ATTRS "\x1b[8" - -#define ESC_CLEAR "\x1b[2J" -#define ESC_CLEAR_BOTTOM "\x1b[J" -#define ESC_CLEAR_EOL "\x1b[0K" - -#define ESC_CURSOR_UP "\x1b[1A" -#define ESC_CURSOR_DOWN "\x1b[1B" -#define ESC_CURSOR_FORWARD "\x1b[1C" -#define ESC_CURSOR_BACKWARD "\x1b[1D" - -#define SEQ_CSI PSTR("\033[") // code introducer -#define SEQ_LOAD_G1 PSTR("\033)0") // load G1 character set -#define SEQ_CLEAR PSTR("\033[2J") // clear screen -#define SEQ_ATTRSET PSTR("\033[0") // set attributes, e.g. "\033[0;7;1m" - -#define SEQ_ATTRSET_BOLD PSTR(";1") // bold -#define SEQ_ATTRSET_DIM PSTR(";2") // dim -#define SEQ_ATTRSET_FCOLOR PSTR(";3") // forground color -#define SEQ_ATTRSET_UNDERLINE PSTR(";4") // underline -#define SEQ_ATTRSET_BCOLOR PSTR(";4") // background color -#define SEQ_ATTRSET_BLINK PSTR(";5") // blink -#define SEQ_ATTRSET_REVERSE PSTR(";7") // reverse - -enum TerminalEventEnum { - EVENT_OPEN, - EVENT_CLOSE, - EVENT_TAB -}; - -enum SpecialKeyEnum { SPEC_KEY_UP, - SPEC_KEY_TAB, - SPEC_KEY_ENTER, - SPEC_KEY_ESC }; - -typedef std::function SpecialKeyPressedEvent; - -typedef std::function TerminalEventHandler; - -typedef std::function TerminalInputEventHandler; - -enum EOLType_t { CRLF, - LFCR, - LF, - CR }; - -enum State { ST_INACTIVE, - ST_NORMAL, - ST_ESC_SEQ, - ST_CTRL_SEQ }; - -class Terminal : public Print { - public: - Terminal(Stream *stream = nullptr); - - void setStream(Stream *stream); - void setEOL(EOLType_t code); - void enableControlCodes(bool enabled = true); - void enableEcho(bool enabled = true); - void enableColors(bool enabled = true); - void setOnEvent(TerminalEventHandler); - void setOnSpecKeyPress(SpecialKeyPressedEvent); - void setOnReadLine(TerminalInputEventHandler); - - bool setLine(const uint8_t *bytes, size_t size); - CharBuffer &getLine(); - - void backsp(); - void clear(); - void clear_line(); - size_t println(const char *str); - size_t println(void); - size_t write_P(PGM_P str); - size_t write(uint8_t c); - size_t write(const uint8_t *buf, size_t size); - void writeByDigit(uint8_t i); - bool available(); - void loop(); - void start(); - void quit(); - void initscr(); - void attrset(uint16_t attr); - - private: - void move(uint8_t y, uint8_t x); - TerminalEventHandler eventHandler_; - TerminalInputEventHandler inputHandler_; - - uint8_t attr = 0xff; - uint8_t curY = 0xff; - uint8_t curX = 0xff; - - unsigned long _lastReceived = 0; - State state; - Stream *_stream; - EditLine _line; - char _cc_buf[32] = {0}; - size_t _cc_pos = 0; - bool _color = false; - bool _controlCodes = false; - bool _echo = false; - EOLType_t _eol = CRLF; - - struct ControlCode { - const char *cc; - const char ch; - }; - - ControlCode keyMap[10] = { - {"G", KEY_HOME}, // 71 Home key - {"H", KEY_UP}, // 72 Up arrow - {"I", KEY_PAGE_UP}, // 73 PageUp - {"K", KEY_LEFT}, // 75 Left arrow - {"M", KEY_RIGHT}, // 77 Right arrow - {"O", KEY_END}, // 79 End key - {"P", KEY_DOWN}, // 80 Down arrow - {"Q", KEY_PAGE_DOWN}, // 81 PageDown - {"R", KEY_INS}, // 82 Insert - {"S", KEY_DEL}, // 83 Delete - }; -}; diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index dcab1cd6..3c28d319 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -4,7 +4,7 @@ #include "items/vButtonOut.h" // #include "Global.h" -#include "Module/Terminal.h" + void loopCmdAdd(const String& cmdStr) { orderBuf += cmdStr; @@ -27,8 +27,6 @@ void csvCmdExecute(String& cmdStr) { while (cmdStr.length()) { String buf = selectToMarker(cmdStr, "\n"); - - buf = deleteBeforeDelimiter(buf, " "); //отсечка чекбокса count++; @@ -94,8 +92,6 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), impuls); } - - sCmd.readStr(buf); } cmdStr = deleteBeforeDelimiter(cmdStr, "\n"); diff --git a/src/Module/Telnet.cpp b/src/Module/Telnet.cpp deleted file mode 100644 index d8bec643..00000000 --- a/src/Module/Telnet.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "Module/Telnet.h" - -bool Telnet::onInit() { - _server = new WiFiServer(_port); - _term = new Terminal(); - _term->enableControlCodes(); - _term->enableEcho(false); - _term->setStream(&_client); - return true; -} - -void Telnet::onEnd() { - delete _server; -} - -bool Telnet::onStart() { - _server->begin(); - _server->setNoDelay(true); - return true; -} - -void Telnet::onStop() { - if (hasClient()) { - _client.stop(); - } - _server->stop(); -} - -bool Telnet::hasClient() { return _client.connected(); } - -void Telnet::sendData(const String& data) { - if (hasClient()) { - _client.write(data.c_str()); - } -} - -void Telnet::setCommandShell(CommandShell* shell) { - _shell = shell; - _shell->setTerm(_term); -} - -void Telnet::setEventHandler(TelnetEventHandler h) { _eventHandler = h; } - -void Telnet::onLoop() { - if (_server->hasClient()) { - if (!_client) { - _client = _server->available(); - } else { - if (!_client.connected()) { - _server->stop(); - _client = _server->available(); - } else { - WiFiClient rejected; - rejected = _server->available(); - rejected.stop(); - } - } - } - - if (_lastConnected != hasClient()) { - _lastConnected = hasClient(); - if (_lastConnected) { - onConnect(); - } else { - onDisconnect(); - } - } - - if (hasClient() && _shell != nullptr) _shell->loop(); -} - -bool Telnet::isShellActive() { - return _shell->active(); -} - -void Telnet::onConnect() { - if (_eventHandler) { - _eventHandler(TE_CONNECTED, &_client); - } -} - -void Telnet::onDisconnect() { - if (_eventHandler) { - _eventHandler(TE_DISCONNECTED, nullptr); - } -} diff --git a/src/Module/Terminal.cpp b/src/Module/Terminal.cpp deleted file mode 100644 index 38c03b97..00000000 --- a/src/Module/Terminal.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "Module/Terminal.h" - -#include "Utils/TimeUtils.h" - -#define INPUT_MAX_LENGHT 255 - -Terminal::Terminal(Stream *stream) : _stream{stream}, - _line(INPUT_MAX_LENGHT), - _cc_pos(0), - _color(false), - _controlCodes(false), - _echo(false), - _eol(CRLF) { state = ST_NORMAL; }; - -void Terminal::setStream(Stream *stream) { - _stream = stream; -} - -void Terminal::setOnReadLine(TerminalInputEventHandler h) { inputHandler_ = h; } - -void Terminal::setOnEvent(TerminalEventHandler h) { eventHandler_ = h; } - -bool Terminal::available() { - return _stream != nullptr ? _stream->available() : false; -} - -void Terminal::setEOL(EOLType_t eol) { - _eol = eol; -} - -void Terminal::enableEcho(bool enabled) { - _echo = enabled; -} - -void Terminal::enableColors(bool enabled) { - _color = enabled; -} - -void Terminal::enableControlCodes(bool enabled) { - _controlCodes = enabled; -} - -void Terminal::quit() {} - -void Terminal::loop() { - if (_stream == nullptr || !_stream->available()) return; - - byte moveX = 0; - byte moveY = 0; - - char c = _stream->read(); - - _lastReceived = millis(); - - if (state == ST_INACTIVE) { - // wait for CR - if (c == CHAR_CR) { - if (eventHandler_) { - eventHandler_(EVENT_OPEN, _stream); - state = ST_NORMAL; - } - } - // or ignore all other - return; - } - - if (c == CHAR_LF || c == CHAR_NULL || c == CHAR_BIN) - return; - - // Esc - if (c == CHAR_ESC || c == 195) { - state = ST_ESC_SEQ; - _cc_pos = 0; - for (size_t i = 0; i < 2; ++i) { - bool timeout = false; - while (!_stream->available() && - !(timeout = millis_since(_lastReceived) > 10)) { - delay(0); - } - if (timeout) { - state = ST_NORMAL; - break; - } - _lastReceived = millis(); - c = _stream->read(); - _cc_buf[_cc_pos] = c; - if ((c == '[') || ((c >= 'A' && c <= 'Z') || c == '~')) { - _cc_pos++; - _cc_buf[++_cc_pos] = '\x00'; - } - } - uint8_t i; - for (i = 0; i < 10; ++i) { - if (strcmp(_cc_buf, keyMap[i].cc) == 0) { - c = keyMap[i].ch; - state = ST_NORMAL; - } - } - } - - if (state == ST_ESC_SEQ) { - state = ST_NORMAL; - return; - } - - // WHEN NORMAL - if (state == ST_NORMAL) { - if (c == CHAR_ESC) { - if (!_line.available()) { - // QUIT - state = ST_INACTIVE; - if (eventHandler_) - eventHandler_(EVENT_CLOSE, _stream); - } else { - // CLEAR - _line.clear(); - if (_controlCodes) { - clear_line(); - } else { - println(); - } - } - return; - } - - switch (c) { - case CHAR_CR: - println(); - if (inputHandler_) - inputHandler_(_line.c_str()); - _line.clear(); - moveY++; - break; - case CHAR_TAB: - if (eventHandler_) - eventHandler_(EVENT_TAB, _stream); - return; - case KEY_LEFT: - if (_line.prev()) - moveX--; - break; - case KEY_RIGHT: - if (_line.next()) - moveX++; - break; - case KEY_HOME: - moveX = -1 * _line.home(); - break; - case KEY_END: - moveX = _line.end(); - break; - case CHAR_BS: - case KEY_DEL: - if (_line.backspace()) { - backsp(); - moveX--; - } - break; - default: - // printable ascii 7bit or printable 8bit ISO8859 - if ((c & '\x7F') >= 32 && (c & '\x7F') < 127) - if (_line.write(c)) { - if (_echo) write(c); - moveX++; - } - break; - } - - // if (controlCodesEnabled) - // move(startY + moveY, startX + moveX); - } -} - -bool Terminal::setLine(const uint8_t *ptr, size_t size) { - _line.clear(); - if (_line.write(ptr, size)) - print(_line.c_str()); - return true; -} - -CharBuffer &Terminal::getLine() { return _line; } - -void Terminal::start() { - if (_controlCodes) initscr(); - println(); -} - -void Terminal::initscr() { - write_P(SEQ_LOAD_G1); - attrset(A_NORMAL); - move(0, 0); - clear(); -} - -void Terminal::attrset(const uint16_t attr) { - uint8_t i; - - if (attr != this->attr) { - this->write_P(SEQ_ATTRSET); - - i = (attr & F_COLOR) >> 8; - - if (i >= 1 && i <= 8) { - this->write_P(SEQ_ATTRSET_FCOLOR); - this->write(i - 1 + '0'); - } - - i = (attr & B_COLOR) >> 12; - - if (i >= 1 && i <= 8) { - this->write_P(SEQ_ATTRSET_BCOLOR); - this->write(i - 1 + '0'); - } - - if (attr & A_REVERSE) - this->write_P(SEQ_ATTRSET_REVERSE); - if (attr & A_UNDERLINE) - this->write_P(SEQ_ATTRSET_UNDERLINE); - if (attr & A_BLINK) - this->write_P(SEQ_ATTRSET_BLINK); - if (attr & A_BOLD) - this->write_P(SEQ_ATTRSET_BOLD); - if (attr & A_DIM) - this->write_P(SEQ_ATTRSET_DIM); - this->write('m'); - this->attr = attr; - } -} - -void Terminal::clear() { write_P(SEQ_CLEAR); } - -void Terminal::clear_line() { - write(CHAR_CR); - write_P(ESC_CLEAR_EOL); -} - -void Terminal::move(uint8_t y, uint8_t x) { - write_P(SEQ_CSI); - writeByDigit(y + 1); - write(';'); - writeByDigit(x + 1); - write('H'); - curY = y; - curX = x; -} - -void Terminal::writeByDigit(uint8_t i) { - uint8_t ii; - if (i >= 10) { - if (i >= 100) { - ii = i / 100; - write(ii + '0'); - i -= 100 * ii; - } - ii = i / 10; - write(ii + '0'); - i -= 10 * ii; - } - write(i + '0'); -} - -void Terminal::backsp() { - write(CHAR_BS); - write(CHAR_SPACE); - write(CHAR_BS); -} - -size_t Terminal::println(const char *str) { - size_t n = print(str); - return n += println(); -} - -size_t Terminal::println(void) { - size_t n = 0; - switch (_eol) { - case CRLF: - n += write(CHAR_CR); - n += write(CHAR_LF); - break; - case LF: - n += write(CHAR_LF); - break; - case LFCR: - n += write(CHAR_LF); - n += write(CHAR_CR); - break; - case CR: - n += write(CHAR_CR); - break; - } - return n; -} - -size_t Terminal::write(uint8_t ch) { - size_t n = 0; - if (_stream) - n = _stream->write(ch); - return n; -} - -size_t Terminal::write_P(PGM_P str) { - uint8_t ch; - size_t n = 0; - while ((ch = pgm_read_byte(str + n)) != '\x0') { - _stream->write(ch); - n++; - } - return n; -} - -size_t Terminal::write(const uint8_t *buf, size_t size) { - size_t n = 0; - while (size--) { - if (_stream->write(*buf++)) - n++; - else - break; - } - return n; -} \ No newline at end of file From 2e73eac2f162627c5017e39685b2c22e056fa348 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 15 Nov 2020 22:39:38 +0300 Subject: [PATCH 11/94] 13 --- src/BufferExecute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 3c28d319..db55a09c 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -1,9 +1,9 @@ #include "BufferExecute.h" +#include "Global.h" // #include "items/vSensorDallas.h" #include "items/vButtonOut.h" -// -#include "Global.h" + void loopCmdAdd(const String& cmdStr) { From 3c3a4a33cac3dd66e1954b234b630be825b0e4ae Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Mon, 16 Nov 2020 13:58:26 +0300 Subject: [PATCH 12/94] RSSI to web interface added --- data/config.json | 8 +++---- data/set.device.json | 4 ++++ include/Utils/WiFiUtils.h | 2 ++ src/Utils/WiFiUtils.cpp | 47 ++++++++++++++++++++++++++++++++------- src/main.cpp | 5 ++--- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/data/config.json b/data/config.json index 88fa28c5..2e45f37b 100644 --- a/data/config.json +++ b/data/config.json @@ -7,11 +7,11 @@ "routerpass": "BELCHENKO", "timezone": 1, "ntp": "pool.ntp.org", - "mqttServer": "m12.cloudmqtt.com", - "mqttPort": 14053, + "mqttServer": "wqtt.ru", + "mqttPort": 8021, "mqttPrefix": "/iotTest", - "mqttUser": "lbscvzuj", - "mqttPass": "bLxlveOgaF8F", + "mqttUser": "u_K1CK9Q", + "mqttPass": "YjyCloWS", "scen": "1", "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", diff --git a/data/set.device.json b/data/set.device.json index 16c964c0..b45d6c71 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -37,6 +37,10 @@ "type": "h4", "title": "Uptime: {{uptime}}" }, + { + "type": "h4", + "title": "WiFi Signal: {{signal}}" + }, { "type": "h4", "title": "Build version: {{firmware_version}}" diff --git a/include/Utils/WiFiUtils.h b/include/Utils/WiFiUtils.h index ef2c9379..60ed3e7b 100644 --- a/include/Utils/WiFiUtils.h +++ b/include/Utils/WiFiUtils.h @@ -10,3 +10,5 @@ bool startAPMode(); boolean RouterFind(String ssid); +String RSSIquality(); + diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index f0c66eae..cac571c4 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -11,8 +11,10 @@ void routerConnect() { if (_ssid == "" && _password == "") { WiFi.begin(); - } else { + } + else { WiFi.begin(_ssid.c_str(), _password.c_str()); + WiFi.setOutputPower(20.5); SerialPrint("I", "WIFI", "ssid: " + _ssid); } @@ -29,7 +31,8 @@ void routerConnect() { if (WiFi.status() != WL_CONNECTED) { Serial.println(""); startAPMode(); - } else { + } + else { Serial.println(""); SerialPrint("I", "WIFI", "http://" + WiFi.localIP().toString()); jsonWriteStr(configSetupJson, "ip", WiFi.localIP().toString()); @@ -81,17 +84,17 @@ boolean RouterFind(String ssid) { if (n == -2) { //Сканирование не было запущено, запускаем SerialPrint("I", "WIFI", "start scanning"); WiFi.scanNetworks(true, false); //async, show_hidden - } - + } + else if (n == -1) { //Сканирование все еще выполняется SerialPrint("I", "WIFI", "scanning in progress"); - } - + } + else if (n == 0) { //ни одна сеть не найдена SerialPrint("I", "WIFI", "no networks found"); WiFi.scanNetworks(true, false); - } - + } + else if (n > 0) { for (int8_t i = 0; i < n; i++) { if (WiFi.SSID(i) == ssid) { @@ -107,3 +110,31 @@ boolean RouterFind(String ssid) { boolean isNetworkActive() { return WiFi.status() == WL_CONNECTED; } + +String RSSIquality() { + String res = "not connected"; + if (WiFi.status() == WL_CONNECTED) { + int rssi = WiFi.RSSI(); + if (rssi >= -50) { + res = "Excellent"; + } + else if (rssi < -50 && rssi >= -60) { + res = "Very good"; + } + else if (rssi < -60 && rssi >= -70) { + res = "Good"; + } + else if (rssi < -70 && rssi >= -80) { + res = "Low"; + } + else if (rssi < -80 && rssi > -100) { + res = "Very low"; + } + else if (rssi <= -100) { + res = "No signal"; + } + } + return res; +} + + diff --git a/src/main.cpp b/src/main.cpp index 4751e813..64165406 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -94,18 +94,17 @@ void setup() { SsdpInit(); #endif - - //esp_log_level_set("esp_littlefs", ESP_LOG_NONE); ts.add( TEST, 1000 * 60, [&](void*) { SerialPrint("I", "System", printMemoryStatus()); + jsonWriteStr(configSetupJson, "signal", RSSIquality()); }, nullptr, true); just_load = false; - initialized = true; + initialized = true; } void loop() { From a20c2b856490de4d43ce13302b4b1accc41fc584 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Mon, 16 Nov 2020 18:47:09 +0300 Subject: [PATCH 13/94] Save date to flash added --- data/set.device.json | 2 +- include/Global.h | 1 + include/Utils/JsonUtils.h | 2 +- src/Global.cpp | 1 + src/Init.cpp | 6 ++---- src/MqttClient.cpp | 2 +- src/Utils/JsonUtils.cpp | 6 ++---- src/Web.cpp | 1 + src/WebServer.cpp | 4 ++++ src/items/vButtonOut.cpp | 8 ++++---- src/items/vInput.cpp | 8 ++++---- src/items/vLogging.cpp | 4 ++-- 12 files changed, 24 insertions(+), 21 deletions(-) diff --git a/data/set.device.json b/data/set.device.json index b45d6c71..8eb0b1e3 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -160,7 +160,7 @@ }, { "type": "button", - "title": "Очистить графики и сбросить введенные данные", + "title": "Очистить графики и введенные данные", "action": "/set?cleanlog", "class": "btn btn-block btn-default" }, diff --git a/include/Global.h b/include/Global.h index fd0205f2..d00d8266 100644 --- a/include/Global.h +++ b/include/Global.h @@ -52,6 +52,7 @@ extern boolean telegramInitBeen; // Json extern String configSetupJson; //все настройки extern String configLiveJson; //все данные с датчиков (связан с mqtt) +extern String configStoreJson; //все данные которые должны сохраняться extern String configOptionJson; //для трансфера // Mqtt diff --git a/include/Utils/JsonUtils.h b/include/Utils/JsonUtils.h index 4f60503a..2b19dcc5 100644 --- a/include/Utils/JsonUtils.h +++ b/include/Utils/JsonUtils.h @@ -18,4 +18,4 @@ String jsonWriteBool(String& json, String name, boolean value); void saveConfig(); -void saveLive(); \ No newline at end of file +void saveStore(); \ No newline at end of file diff --git a/src/Global.cpp b/src/Global.cpp index d8facc43..9b41fb67 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -23,6 +23,7 @@ boolean telegramInitBeen = false; // Json String configSetupJson = "{}"; String configLiveJson = "{}"; +String configStoreJson = "{}"; String configOptionJson = "{}"; // Mqtt diff --git a/src/Init.cpp b/src/Init.cpp index 36d98c21..08969c88 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -12,10 +12,8 @@ void loadConfig() { configSetupJson = readFile("config.json", 4096); configSetupJson.replace("\r\n", ""); -#ifdef SAVE_SETTINGS_TO_FLASH - configLiveJson = readFile("live.json", 4096); - configLiveJson.replace("\r\n", ""); -#endif + configStoreJson = readFile("store.json", 4096); + configStoreJson.replace("\r\n", ""); jsonWriteStr(configSetupJson, "chipID", chipId); jsonWriteInt(configSetupJson, "firmware_version", FIRMWARE_VERSION); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index cdd18448..38844fb0 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -232,7 +232,7 @@ void publishWidgets() { void publishState() { // берет строку json и ключи превращает в топики а значения колючей в них посылает - String str = configLiveJson; + String str = configLiveJson + "," + configStoreJson; str.replace("{", ""); str.replace("}", ""); str += ","; diff --git a/src/Utils/JsonUtils.cpp b/src/Utils/JsonUtils.cpp index fad6c937..ca70c082 100644 --- a/src/Utils/JsonUtils.cpp +++ b/src/Utils/JsonUtils.cpp @@ -55,8 +55,6 @@ void saveConfig() { writeFile(String("config.json"), configSetupJson); } -void saveLive() { -#ifdef SAVE_SETTINGS_TO_FLASH - writeFile(String("live.json"), configLiveJson); -#endif +void saveStore() { + writeFile(String("store.json"), configStoreJson); } \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index 9b2a84e0..3c30b59d 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -36,6 +36,7 @@ void web_init() { if (request->hasArg("delAllItems")) { delAllItems(); + cleanLogAndData(); request->redirect("/?set.device"); } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 0cd23843..9a4611f4 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -49,6 +49,10 @@ void init() { request->send(200, "application/json", configLiveJson); }); + server.on("/config.store.json", HTTP_GET, [](AsyncWebServerRequest *request) { + request->send(200, "application/json", configStoreJson); + }); + // данные не являющиеся событиями server.on("/config.option.json", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "application/json", configOptionJson); diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index bb4759ec..a45b7c9f 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -5,13 +5,13 @@ #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" - +//this class save date to flash ButtonOut::ButtonOut(unsigned int pin, boolean inv, String key) { _pin = pin; _inv = inv; _key = key; pinMode(_pin, OUTPUT); - int state = jsonReadInt(configLiveJson, key); + int state = jsonReadInt(configStoreJson, key); this->execute(String(state)); } ButtonOut::~ButtonOut() {} @@ -30,8 +30,8 @@ void ButtonOut::execute(String state) { } } eventGen2(_key, state); - jsonWriteInt(configLiveJson, _key, state.toInt()); - saveLive(); + jsonWriteInt(configStoreJson, _key, state.toInt()); + saveStore(); publishStatus(_key, state); } diff --git a/src/items/vInput.cpp b/src/items/vInput.cpp index 7a71b472..76589d94 100644 --- a/src/items/vInput.cpp +++ b/src/items/vInput.cpp @@ -5,10 +5,10 @@ #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" - +//this class save date to flash Input::Input(String key, String widget) { _key = key; - String value = jsonReadStr(configLiveJson, key); + String value = jsonReadStr(configStoreJson, key); if (value == "") { if (widget.indexOf("Digit") != -1) { @@ -25,8 +25,8 @@ Input::~Input() {} void Input::execute(String value) { eventGen2(_key, value); - jsonWriteStr(configLiveJson, _key, value); - saveLive(); + jsonWriteStr(configStoreJson, _key, value); + saveStore(); publishStatus(_key, value); } diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 473fe5b0..2958cf1c 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -120,7 +120,7 @@ void cleanLogAndData() { SerialPrint("I", "System", fname); removeFile("logs/" + fname); } - removeFile("live.json"); - configLiveJson = ""; #endif + removeFile("store.json"); + configStoreJson = ""; } From 06df1f17b691356476dac9a79db46cb53a78359e Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Tue, 17 Nov 2020 01:01:42 +0300 Subject: [PATCH 14/94] Progress of renew of class --- data/items/input-digit.txt | 2 +- data/items/input-time.txt | 2 +- data/items/output-text.txt | 2 +- data/presets/alloff.c.txt | 2 +- data/presets/dal.c.txt | 4 +- data/presets/dht.c.txt | 4 +- data/presets/rel.c.txt | 4 +- data/set.device.json | 23 +++++++--- include/BufferExecute.h | 14 ------ include/Class/ScenarioClass3.h | 4 +- include/Consts.h | 6 ++- include/Global.h | 7 ++- include/Utils/WiFiUtils.h | 2 +- include/items/OutputTextClass.h | 23 ---------- include/items/PwmOutClass.h | 31 ------------- include/items/vButtonOut.h | 1 - include/items/vInOutput.h | 27 +++++++++++ include/items/vInput.h | 27 ----------- include/items/vLogging.h | 1 + include/items/vPwmOut.h | 29 ++++++++++++ src/BufferExecute.cpp | 12 +++-- src/Global.cpp | 7 ++- src/Init.cpp | 45 +++++++------------ src/Telegram.cpp | 2 +- src/UpgradeFirm.cpp | 31 +++++++------ src/Utils/WiFiUtils.cpp | 16 +++---- src/Web.cpp | 2 + src/items/OutputTextClass.cpp | 19 -------- src/items/PwmOutClass.cpp | 24 ---------- src/items/vButtonOut.cpp | 7 ++- src/items/{vInput.cpp => vInOutput.cpp} | 30 ++++++------- src/items/vPwmOut.cpp | 55 +++++++++++++++++++++++ src/main.cpp | 60 +++++++++++++++++-------- 33 files changed, 266 insertions(+), 259 deletions(-) delete mode 100644 include/items/OutputTextClass.h delete mode 100644 include/items/PwmOutClass.h create mode 100644 include/items/vInOutput.h delete mode 100644 include/items/vInput.h create mode 100644 include/items/vPwmOut.h delete mode 100644 src/items/OutputTextClass.cpp delete mode 100644 src/items/PwmOutClass.cpp rename src/items/{vInput.cpp => vInOutput.cpp} (59%) create mode 100644 src/items/vPwmOut.cpp diff --git a/data/items/input-digit.txt b/data/items/input-digit.txt index 65c39647..7df7260e 100644 --- a/data/items/input-digit.txt +++ b/data/items/input-digit.txt @@ -1 +1 @@ -0;input;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file +0;inoutput;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file diff --git a/data/items/input-time.txt b/data/items/input-time.txt index f5e69327..3fc078b0 100644 --- a/data/items/input-time.txt +++ b/data/items/input-time.txt @@ -1 +1 @@ -0;input;id;inputTime;Ввод;Введите#время;order \ No newline at end of file +0;inoutput;id;inputTime;Ввод;Введите#время;order \ No newline at end of file diff --git a/data/items/output-text.txt b/data/items/output-text.txt index 900658f7..65d45284 100644 --- a/data/items/output-text.txt +++ b/data/items/output-text.txt @@ -1 +1 @@ -0;output-text;id;anydata;Вывод;Сигнализация;order \ No newline at end of file +0;inoutput;id;anydata;Вывод;Сигнализация;order \ No newline at end of file diff --git a/data/presets/alloff.c.txt b/data/presets/alloff.c.txt index fe6b26ff..359a7a52 100644 --- a/data/presets/alloff.c.txt +++ b/data/presets/alloff.c.txt @@ -4,4 +4,4 @@ 0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] 0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] 0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] -0;output-text;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file +0;inoutput;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/data/presets/dal.c.txt b/data/presets/dal.c.txt index 9433d86e..abcd06dc 100644 --- a/data/presets/dal.c.txt +++ b/data/presets/dal.c.txt @@ -1,5 +1,5 @@ 0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] 0;logging;log;chart;Термостат;История;2;val[temp];int[60];cnt[100] -0;input;inputU;inputDigit;Термостат;Верхний#порог;3 -0;input;inputL;inputDigit;Термостат;Нижний#порог;4 +0;inoutput;inputU;inputDigit;Термостат;Верхний#порог;3 +0;inoutput;inputL;inputDigit;Термостат;Нижний#порог;4 0;button-out;button;toggle;Термостат;Нагрев;5;pin[12] \ No newline at end of file diff --git a/data/presets/dht.c.txt b/data/presets/dht.c.txt index 29195c1c..fb2c602b 100644 --- a/data/presets/dht.c.txt +++ b/data/presets/dht.c.txt @@ -1,5 +1,5 @@ 0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] 0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] -0;input;inputU;inputDigit;Теплица;Верхний#порог;3 -0;input;inputL;inputDigit;Теплица;Нижний#порог;4 +0;inoutput;inputU;inputDigit;Теплица;Верхний#порог;3 +0;inoutput;inputL;inputDigit;Теплица;Нижний#порог;4 0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/data/presets/rel.c.txt b/data/presets/rel.c.txt index faf94758..3f1ccaa1 100644 --- a/data/presets/rel.c.txt +++ b/data/presets/rel.c.txt @@ -1,4 +1,4 @@ 0;button-out;button1;toggle;Реле;Освещение;1;pin[12] 0;button-out;button2;toggle;Реле;Освещение;2;pin[13] -0;input;T1;inputTime;Реле;Введите#время#включения;3 -0;input;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file +0;inoutput;T1;inputTime;Реле;Введите#время#включения;3 +0;inoutput;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index 8eb0b1e3..7c02e45e 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -23,15 +23,15 @@ }, { "type": "h4", - "title": "Device ID: {{chipID}}" + "title": "ID устройства: {{chipID}}" }, { "type": "h4", - "title": "IP address: {{ip}}" + "title": "IP адрес: {{ip}}" }, { "type": "h4", - "title": "Time: {{timenow}}" + "title": "Время: {{timenow}}" }, { "type": "h4", @@ -39,15 +39,26 @@ }, { "type": "h4", - "title": "WiFi Signal: {{signal}}" + "title": "Версия прошивки: {{firmware_version}}" }, { "type": "h4", - "title": "Build version: {{firmware_version}}" + "title": "Версия файловой системы: 267" }, { "type": "h4", - "title": "LittleFS version: 267" + "title": "{{signal}}" + }, + { + "type": "hr" + }, + { + "type": "text", + "title": "{{warning1}}" + }, + { + "type": "text", + "title": "{{warning2}}" }, { "type": "hr" diff --git a/include/BufferExecute.h b/include/BufferExecute.h index e77d36db..99a1ab26 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -9,21 +9,9 @@ extern void loopCmdExecute(); extern void addKey(String& key, String& keyNumberTable, int number); extern int getKeyNum(String& key, String& keyNumberTable); -extern void pwmOut(); -extern void pwmOutSet(); - extern void buttonIn(); extern void buttonInSet(); -extern void input(); -extern void inputDigitSet(); - -extern void inputTime(); -extern void inputTimeSet(); - -extern void textOut(); -extern void textOutSet(); - extern void analogAdc(); extern void analogReading(); @@ -56,6 +44,4 @@ extern void bmp280ReadingPress(); extern void sysUptime(); extern void uptimeReading(); -extern void logging(); - extern void impuls(); diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index 3bedf2bc..f49dcea4 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -8,7 +8,9 @@ class Scenario { public: void loop() { - + if (!jsonReadBool(configSetupJson, "scen")) { + return; + } String allBlocks = scenario; allBlocks.replace("\r\n", "\n"); allBlocks.replace("\r", "\n"); diff --git a/include/Consts.h b/include/Consts.h index 4e65e5e3..6fed021f 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -117,4 +117,8 @@ enum ConfigType_t { //15.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.1% (used 37780 bytes from 81920 bytes) -//Flash: [===== ] 54.3% (used 566656 bytes from 1044464 bytes) \ No newline at end of file +//Flash: [===== ] 54.3% (used 566656 bytes from 1044464 bytes) + +//17.11.2020 (SSDP OFF, UDP OFF) +//RAM: [===== ] 45.7% (used 37476 bytes from 81920 bytes) +//Flash: [===== ] 54.5% (used 569296 bytes from 1044464 bytes) \ No newline at end of file diff --git a/include/Global.h b/include/Global.h index d00d8266..b44cb8d7 100644 --- a/include/Global.h +++ b/include/Global.h @@ -75,8 +75,11 @@ extern int impuls_EnterCounter; extern String buttonOut_KeyList; extern int buttonOut_EnterCounter; //========================================= -extern String input_KeyList; -extern int input_EnterCounter; +extern String inOutput_KeyList; +extern int inOutput_EnterCounter; +//========================================= +extern String pwmOut_KeyList; +extern int pwmOut_EnterCounter; //========================================= // Sensors diff --git a/include/Utils/WiFiUtils.h b/include/Utils/WiFiUtils.h index 60ed3e7b..a6dbcc34 100644 --- a/include/Utils/WiFiUtils.h +++ b/include/Utils/WiFiUtils.h @@ -10,5 +10,5 @@ bool startAPMode(); boolean RouterFind(String ssid); -String RSSIquality(); +uint8_t RSSIquality(); diff --git a/include/items/OutputTextClass.h b/include/items/OutputTextClass.h deleted file mode 100644 index 8e3867ba..00000000 --- a/include/items/OutputTextClass.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include -#include "Class/LineParsing.h" -#include "Global.h" - -class OutputTextClass : public LineParsing { - public: - OutputTextClass() : LineParsing(){}; - - void OutputModuleStateSetDefault() { - if (_state != "") { - OutputModuleChange(_key, _state); - } - } - - void OutputModuleChange(String key, String state) { - state.replace("#", " "); - eventGen2(key, state); - jsonWriteStr(configLiveJson, key, state); - publishStatus(key, state); - } -}; -extern OutputTextClass myOutputText; \ No newline at end of file diff --git a/include/items/PwmOutClass.h b/include/items/PwmOutClass.h deleted file mode 100644 index bd6570ba..00000000 --- a/include/items/PwmOutClass.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "Class/LineParsing.h" -#include "Global.h" - -class PwmOutClass : public LineParsing { - public: - PwmOutClass() : LineParsing(){}; - - void pwmModeSet() { - if (_pin != "") { - pinMode(_pin.toInt(), INPUT); - } - } - - void pwmStateSetDefault() { - if (_state != "") { - pwmChange(_key, _pin, _state); - } - } - - void pwmChange(String key, String pin, String state) { - int pinInt = pin.toInt(); - analogWrite(pinInt, state.toInt()); - eventGen2(key, state); - jsonWriteInt(configLiveJson, key, state.toInt()); - publishStatus(key, state); - } -}; - -extern PwmOutClass myPwmOut; \ No newline at end of file diff --git a/include/items/vButtonOut.h b/include/items/vButtonOut.h index bf918f8c..3568b516 100644 --- a/include/items/vButtonOut.h +++ b/include/items/vButtonOut.h @@ -22,7 +22,6 @@ class ButtonOut { boolean _inv; String _key; - void addNewDelOldData(const String filename, size_t maxPoints, String payload); }; extern MyButtonOutVector* myButtonOut; diff --git a/include/items/vInOutput.h b/include/items/vInOutput.h new file mode 100644 index 00000000..99fe4636 --- /dev/null +++ b/include/items/vInOutput.h @@ -0,0 +1,27 @@ +#pragma once +#include + +#include "Global.h" + +class InOutput; + +typedef std::vector MyInOutputVector; + +class InOutput { + public: + + InOutput(String key, String widget); + ~InOutput(); + + void execute(String value); + + private: + + String _key; + +}; + +extern MyInOutputVector* myInOutput; + +extern void inOutput(); +extern void inOutputExecute(); diff --git a/include/items/vInput.h b/include/items/vInput.h deleted file mode 100644 index 52a6c80b..00000000 --- a/include/items/vInput.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include - -#include "Global.h" - -class Input; - -typedef std::vector MyInputVector; - -class Input { - public: - - Input(String key, String widget); - ~Input(); - - void execute(String value); - - private: - - String _key; - -}; - -extern MyInputVector* myInput; - -extern void input(); -extern void inputExecute(); diff --git a/include/items/vLogging.h b/include/items/vLogging.h index 1fb3f09c..d1060334 100644 --- a/include/items/vLogging.h +++ b/include/items/vLogging.h @@ -29,6 +29,7 @@ class LoggingClass { extern MyLoggingVector* myLogging; +extern void logging(); extern void choose_log_date_and_send(); extern void sendLogData(String file, String topic); extern void cleanLogAndData(); diff --git a/include/items/vPwmOut.h b/include/items/vPwmOut.h new file mode 100644 index 00000000..2b9ba669 --- /dev/null +++ b/include/items/vPwmOut.h @@ -0,0 +1,29 @@ +#pragma once +#include + +#include "Global.h" + +class PwmOut; + +typedef std::vector MyPwmOutVector; + +class PwmOut { + public: + + PwmOut(unsigned int pin, String key); + + ~PwmOut(); + + void execute(String state); + + private: + + unsigned int _pin; + String _key; + +}; + +extern MyPwmOutVector* myPwmOut; + +extern void pwmOut(); +extern void pwmOutExecute(); diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index db55a09c..893119b2 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -3,8 +3,9 @@ // #include "items/vSensorDallas.h" #include "items/vButtonOut.h" - - +#include "items/vPwmOut.h" +#include "items/vInOutput.h" +#include "items/vLogging.h" void loopCmdAdd(const String& cmdStr) { orderBuf += cmdStr; @@ -43,11 +44,8 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("button-in")) { sCmd.addCommand(order.c_str(), buttonIn); } - else if (order == F("input")) { - sCmd.addCommand(order.c_str(), input); - } - else if (order == F("output-text")) { - sCmd.addCommand(order.c_str(), textOut); + else if (order == F("inoutput")) { + sCmd.addCommand(order.c_str(), inOutput); } else if (order == F("analog-adc")) { sCmd.addCommand(order.c_str(), analogAdc); diff --git a/src/Global.cpp b/src/Global.cpp index 9b41fb67..96f9c7de 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -46,8 +46,11 @@ int impuls_EnterCounter = -1; String buttonOut_KeyList = ""; int buttonOut_EnterCounter = -1; //========================================= -String input_KeyList = ""; -int input_EnterCounter = -1; +String inOutput_KeyList = ""; +int inOutput_EnterCounter = -1; +//========================================= +String pwmOut_KeyList = ""; +int pwmOut_EnterCounter = -1; //========================================= // Sensors diff --git a/src/Init.cpp b/src/Init.cpp index 08969c88..7972db73 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -6,7 +6,8 @@ #include "items/vImpulsOut.h" #include "items/vButtonOut.h" #include "items/vSensorDallas.h" -#include "items/vInput.h" +#include "items/vInOutput.h" +#include "items/vPwmOut.h" void loadConfig() { configSetupJson = readFile("config.json", 4096); @@ -15,6 +16,9 @@ void loadConfig() { configStoreJson = readFile("store.json", 4096); configStoreJson.replace("\r\n", ""); + jsonWriteStr(configSetupJson, "warning1", ""); + jsonWriteStr(configSetupJson, "warning2", ""); + jsonWriteStr(configSetupJson, "chipID", chipId); jsonWriteInt(configSetupJson, "firmware_version", FIRMWARE_VERSION); @@ -57,11 +61,17 @@ void Device_init() { buttonOut_KeyList = ""; buttonOut_EnterCounter = -1; //======clear input params======= - if (myInput != nullptr) { - myInput->clear(); + if (myInOutput != nullptr) { + myInOutput->clear(); } - input_KeyList = ""; - input_EnterCounter = -1; + inOutput_KeyList = ""; + inOutput_EnterCounter = -1; + //======clear pwm params======= + if (myPwmOut != nullptr) { + myPwmOut->clear(); + } + pwmOut_KeyList = ""; + pwmOut_EnterCounter = -1; //=================================== @@ -94,28 +104,3 @@ void handle_uptime() { jsonWriteStr(configSetupJson, "uptime", timeNow->getUptime()); } -//void handle_statistics() { -// if (isNetworkActive()) { -// String urls = "http://backup.privet.lv/visitors/?"; -// //----------------------------------------------------------------- -// urls += WiFi.macAddress().c_str(); -// urls += "&"; -// //----------------------------------------------------------------- -//#ifdef ESP8266 -// urls += "iot-manager_esp8266"; -//#endif -//#ifdef ESP32 -// urls += "iot-manager_esp32"; -//#endif -// urls += "&"; -//#ifdef ESP8266 -// urls += ESP.getResetReason(); -//#endif -//#ifdef ESP32 -// urls += "Power on"; -//#endif -// urls += "&"; -// urls += String(FIRMWARE_VERSION); -// String stat = getURL(urls); -// } -//} \ No newline at end of file diff --git a/src/Telegram.cpp b/src/Telegram.cpp index 89425e5a..a26ca23e 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -61,7 +61,7 @@ void telegramMsgParse(String msg) { SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + "\n" + list); } else { - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), "Wrong order, use /all to get all values, /get_id to get value, or /set_id_value to set value"); + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), F("Wrong order, use /all to get all values, /get_id to get value, or /set_id_value to set value")); } } diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index f5670d23..4a8889ad 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -26,23 +26,28 @@ void upgradeInit() { getLastVersion(); if (lastVersion > 0) { SerialPrint("I", "Update", "available version: " + String(lastVersion)); + if (lastVersion > FIRMWARE_VERSION) { + jsonWriteStr(configSetupJson, "warning2", F("

Вышла новая версия прошивки, нажмите обновить прошивку

")); + } } }; } void getLastVersion() { if ((WiFi.status() == WL_CONNECTED)) { - #ifdef ESP8266 - String tmp = getURL( serverIP + F("/projects/iotmanager/esp8266/esp8266ver/esp8266ver.txt")); - #else - String tmp = getURL( serverIP + F("/projects/iotmanager/esp32/esp32ver/esp32ver.txt")); - #endif +#ifdef ESP8266 + String tmp = getURL(serverIP + F("/projects/iotmanager/esp8266/esp8266ver/esp8266ver.txt")); +#else + String tmp = getURL(serverIP + F("/projects/iotmanager/esp32/esp32ver/esp32ver.txt")); +#endif if (tmp == "error") { lastVersion = -1; - } else { + } + else { lastVersion = tmp.toInt(); } - } else { +} + else { lastVersion = -2; } jsonWriteInt(configSetupJson, "last_version", lastVersion); @@ -89,15 +94,15 @@ bool upgradeFS() { Serial.println("Start upgrade LittleFS, please wait..."); #ifdef ESP8266 ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return retFS = ESPhttpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp8266/littlefs/littlefs.bin")); + t_httpUpdate_return retFS = ESPhttpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp8266/littlefs/littlefs.bin")); #else httpUpdate.rebootOnUpdate(false); - HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp32/littlefs/spiffs.bin")); + HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp32/littlefs/spiffs.bin")); #endif if (retFS == HTTP_UPDATE_OK) { //если FS обновилась успешно SerialPrint("I", "Update", "LittleFS upgrade done!"); ret = true; - } +} return ret; } @@ -108,16 +113,16 @@ bool upgradeBuild() { #ifdef ESP8266 ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, serverIP + F("/projects/iotmanager/esp8266/firmware/firmware.bin")); + t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, serverIP + F("/projects/iotmanager/esp8266/firmware/firmware.bin")); #else httpUpdate.rebootOnUpdate(false); - HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, serverIP + F("/projects/iotmanager/esp32/firmware/firmware.bin")); + HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, serverIP + F("/projects/iotmanager/esp32/firmware/firmware.bin")); #endif if (retBuild == HTTP_UPDATE_OK) { //если BUILD обновился успешно SerialPrint("I", "Update", "BUILD upgrade done!"); ret = true; - } +} return ret; } diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index cac571c4..5b163c93 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -111,27 +111,27 @@ boolean isNetworkActive() { return WiFi.status() == WL_CONNECTED; } -String RSSIquality() { - String res = "not connected"; +uint8_t RSSIquality() { + uint8_t res = 0; if (WiFi.status() == WL_CONNECTED) { int rssi = WiFi.RSSI(); if (rssi >= -50) { - res = "Excellent"; + res = 6; //"Excellent"; } else if (rssi < -50 && rssi >= -60) { - res = "Very good"; + res = 5; //"Very good"; } else if (rssi < -60 && rssi >= -70) { - res = "Good"; + res = 4; //"Good"; } else if (rssi < -70 && rssi >= -80) { - res = "Low"; + res = 3; //"Low"; } else if (rssi < -80 && rssi > -100) { - res = "Very low"; + res = 2; //"Very low"; } else if (rssi <= -100) { - res = "No signal"; + res = 1; //"No signal"; } } return res; diff --git a/src/Web.cpp b/src/Web.cpp index 3c30b59d..c77e1266 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -30,6 +30,7 @@ void web_init() { } if (request->hasArg("delChoosingItems")) { + jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); myNotAsyncActions->make(do_delChoosingItems); request->send(200); } @@ -37,6 +38,7 @@ void web_init() { if (request->hasArg("delAllItems")) { delAllItems(); cleanLogAndData(); + jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); request->redirect("/?set.device"); } diff --git a/src/items/OutputTextClass.cpp b/src/items/OutputTextClass.cpp deleted file mode 100644 index f86de77d..00000000 --- a/src/items/OutputTextClass.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "BufferExecute.h" -#include "items/OutputTextClass.h" -//===============================================Модуль вывода текста============================================ -//output-text;id;anydata;Вывод;Сигнализация;order;st[Обнаружено.движение] -//=============================================================================================================== -OutputTextClass myOutputText; -void textOut() { - myOutputText.update(); - String key = myOutputText.gkey(); - sCmd.addCommand(key.c_str(), textOutSet); - myOutputText.OutputModuleStateSetDefault(); - myOutputText.clear(); -} - -void textOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myOutputText.OutputModuleChange(key, state); -} \ No newline at end of file diff --git a/src/items/PwmOutClass.cpp b/src/items/PwmOutClass.cpp deleted file mode 100644 index 44ece25c..00000000 --- a/src/items/PwmOutClass.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "BufferExecute.h" -#include "items/PwmOutClass.h" -//==========================================Модуль управления ШИМ=================================================== -//pwm-out volume range Кнопки Свет 1 pin[12] st[500] -//================================================================================================================== -PwmOutClass myPwmOut; -void pwmOut() { - myPwmOut.update(); - String key = myPwmOut.gkey(); - String pin = myPwmOut.gpin(); - String inv = myPwmOut.ginv(); - sCmd.addCommand(key.c_str(), pwmOutSet); - jsonWriteStr(configOptionJson, key + "_pin", pin); - myPwmOut.pwmModeSet(); - myPwmOut.pwmStateSetDefault(); - myPwmOut.clear(); -} - -void pwmOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - String pin = jsonReadStr(configOptionJson, key + "_pin"); - myPwmOut.pwmChange(key, pin, state); -} \ No newline at end of file diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index a45b7c9f..15790687 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -1,11 +1,10 @@ #include "items/vButtonOut.h" - -#include - #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" -//this class save date to flash + +#include +//this class save data to flash ButtonOut::ButtonOut(unsigned int pin, boolean inv, String key) { _pin = pin; _inv = inv; diff --git a/src/items/vInput.cpp b/src/items/vInOutput.cpp similarity index 59% rename from src/items/vInput.cpp rename to src/items/vInOutput.cpp index 76589d94..c842ac62 100644 --- a/src/items/vInput.cpp +++ b/src/items/vInOutput.cpp @@ -1,4 +1,4 @@ -#include "items/vInput.h" +#include "items/vInOutput.h" #include @@ -6,7 +6,7 @@ #include "Global.h" #include "BufferExecute.h" //this class save date to flash -Input::Input(String key, String widget) { +InOutput::InOutput(String key, String widget) { _key = key; String value = jsonReadStr(configStoreJson, key); @@ -21,43 +21,43 @@ Input::Input(String key, String widget) { this->execute(value); } -Input::~Input() {} +InOutput::~InOutput() {} -void Input::execute(String value) { +void InOutput::execute(String value) { eventGen2(_key, value); jsonWriteStr(configStoreJson, _key, value); saveStore(); publishStatus(_key, value); } -MyInputVector* myInput = nullptr; +MyInOutputVector* myInOutput = nullptr; -void input() { +void inOutput() { myLineParsing.update(); String widget = myLineParsing.gfile(); String key = myLineParsing.gkey(); myLineParsing.clear(); - input_EnterCounter++; - addKey(key, input_KeyList, input_EnterCounter); + inOutput_EnterCounter++; + addKey(key, inOutput_KeyList, inOutput_EnterCounter); static bool firstTime = true; - if (firstTime) myInput = new MyInputVector(); + if (firstTime) myInOutput = new MyInOutputVector(); firstTime = false; - myInput->push_back(Input(key, widget)); + myInOutput->push_back(InOutput(key, widget)); - sCmd.addCommand(key.c_str(), inputExecute); + sCmd.addCommand(key.c_str(), inOutputExecute); } -void inputExecute() { +void inOutputExecute() { String key = sCmd.order(); String value = sCmd.next(); - int number = getKeyNum(key, input_KeyList); + int number = getKeyNum(key, inOutput_KeyList); - if (myInput != nullptr) { + if (myInOutput != nullptr) { if (number != -1) { - myInput->at(number).execute(value); + myInOutput->at(number).execute(value); } } } diff --git a/src/items/vPwmOut.cpp b/src/items/vPwmOut.cpp new file mode 100644 index 00000000..f2346c27 --- /dev/null +++ b/src/items/vPwmOut.cpp @@ -0,0 +1,55 @@ +#include "items/vPwmOut.h" +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" + +#include +//this class save data to flash +PwmOut::PwmOut(unsigned int pin, String key) { + _pin = pin; + _key = key; + pinMode(_pin, OUTPUT); + int state = jsonReadInt(configStoreJson, key); + this->execute(String(state)); +} +PwmOut::~PwmOut() {} + +void PwmOut::execute(String state) { + analogWrite(_pin, state.toInt()); + eventGen2(_key, state); + jsonWriteInt(configStoreJson, _key, state.toInt()); + saveStore(); + publishStatus(_key, state); +} + +MyPwmOutVector* myPwmOut = nullptr; + +void pwmOut() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + String pin = myLineParsing.gpin(); + myLineParsing.clear(); + + pwmOut_EnterCounter++; + addKey(key, pwmOut_KeyList, pwmOut_EnterCounter); + + static bool firstTime = true; + if (firstTime) myPwmOut = new MyPwmOutVector(); + firstTime = false; + myPwmOut->push_back(PwmOut(pin.toInt(), key)); + + sCmd.addCommand(key.c_str(), pwmOutExecute); +} + +void pwmOutExecute() { + String key = sCmd.order(); + String state = sCmd.next(); + + int number = getKeyNum(key, pwmOut_KeyList); + + if (myPwmOut != nullptr) { + if (number != -1) { + myPwmOut->at(number).execute(state); + } + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 64165406..7b4e9b07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,7 @@ void setup() { Serial.begin(115200); Serial.flush(); Serial.println(); - Serial.println("--------------started----------------"); + Serial.println(F("--------------started----------------")); setChipId(); @@ -40,57 +40,57 @@ void setup() { myScenario = new Scenario(); fileSystemInit(); - SerialPrint("I", "FS", "FS Init"); + SerialPrint("I", F("FS"), F("FS Init")); loadConfig(); - SerialPrint("I", "Conf", "Config Init"); + SerialPrint("I", F("Conf"), F("Config Init")); clock_init(); - SerialPrint("I", "Time", "Clock Init"); + SerialPrint("I", F("Time"), F("Clock Init")); handle_time_init(); - SerialPrint("I", "Time", "Handle time init("); + SerialPrint("I", F("Time"), F("Handle time init")); sensorsInit(); - SerialPrint("I", "Sensors", "Sensors Init"); + SerialPrint("I", F("Sensors"), F("Sensors Init")); itemsListInit(); - SerialPrint("I", "Items", "Items Init"); + SerialPrint("I", F("Items"), F("Items Init")); all_init(); - SerialPrint("I", "Init", "Init Init"); + SerialPrint("I", F("Init"), F("Init Init")); routerConnect(); - SerialPrint("I", "WIFI", "Network Init"); + SerialPrint("I", F("WIFI"), F("Network Init")); telegramInit(); - SerialPrint("I", "Telegram", "Telegram Init"); + SerialPrint("I", F("Telegram"), F("Telegram Init")); uptime_init(); - SerialPrint("I", "Uptime", "Uptime Init"); + SerialPrint("I", F("Uptime"), F("Uptime Init")); upgradeInit(); - SerialPrint("I", "Update", "Updater Init"); + SerialPrint("I", F("Update"), F("Updater Init")); HttpServer::init(); - SerialPrint("I", "HTTP", "HttpServer Init"); + SerialPrint("I", F("HTTP"), F("HttpServer Init")); web_init(); - SerialPrint("I", "Web", "WebAdmin Init"); + SerialPrint("I", F("Web"), F("WebAdmin Init")); initSt(); - SerialPrint("I", "Stat", "Stat Init"); + SerialPrint("I", F("Stat"), F("Stat Init")); #ifdef UDP_ENABLED - SerialPrint("I", "UDP", "Udp Init"); + SerialPrint("I", F("UDP"), "Udp Init"); asyncUdpInit(); #endif - SerialPrint("I", "Bus", "Bus Init"); + SerialPrint("I", F("Bus"), F("Bus Init")); busInit(); #ifdef SSDP_ENABLED - SerialPrint("I", "SSDP", "Ssdp Init"); + SerialPrint("I", F("SSDP"), F("Ssdp Init")); SsdpInit(); #endif @@ -99,7 +99,29 @@ void setup() { ts.add( TEST, 1000 * 60, [&](void*) { SerialPrint("I", "System", printMemoryStatus()); - jsonWriteStr(configSetupJson, "signal", RSSIquality()); + switch (RSSIquality()) { + case 0: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); + break; + case 1: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); + break; + case 2: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); + break; + case 3: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); + break; + case 4: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); + break; + case 5: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); + break; + case 6: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); + break; + } }, nullptr, true); From ceb516ddbe5ddacab695d7364d6d6264fc62aad1 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 18 Nov 2020 03:25:05 +0300 Subject: [PATCH 15/94] add count down timer --- data/config.json | 4 +-- data/items/count-down.txt | 1 + data/set.device.json | 3 +- include/BufferExecute.h | 2 +- include/Global.h | 3 ++ include/items/vCountDown.h | 27 +++++++++++++++ include/items/vImpulsOut.h | 2 +- src/BufferExecute.cpp | 5 +++ src/Global.cpp | 3 ++ src/Init.cpp | 7 ++++ src/items/vCountDown.cpp | 71 ++++++++++++++++++++++++++++++++++++++ src/items/vImpulsOut.cpp | 3 +- src/main.cpp | 8 +++-- 13 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 data/items/count-down.txt create mode 100644 include/items/vCountDown.h create mode 100644 src/items/vCountDown.cpp diff --git a/data/config.json b/data/config.json index 2e45f37b..d060d7cc 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "VOLODYA", - "routerpass": "BELCHENKO", + "routerssid": "rise", + "routerpass": "hostel3333", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "wqtt.ru", diff --git a/data/items/count-down.txt b/data/items/count-down.txt new file mode 100644 index 00000000..fa38b94b --- /dev/null +++ b/data/items/count-down.txt @@ -0,0 +1 @@ +0;count-down;id;na;na;na;order \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index 7c02e45e..a5aefdac 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -91,7 +91,8 @@ "/set?addItem=bmp280-temp": "18.Датчик температуры bmp280", "/set?addItem=bmp280-press": "19.Датчик давления bmp280", "/set?addItem=impuls-out": "20.Создать импульсы через заданный промежуток времени (управление шд)", - "/set?addItem=modbus": "21.Прочитать регистр modbus устройства", + "/set?addItem=count-down": "21.Таймер обратного отчета", + "/set?addItem=modbus": "22.Прочитать регистр modbus устройства", "/set?addItem=logging": "a.Логгирование и вывод в график любой величины", "/set?addItem=uptime": "b.Отобразить время работы устройства" } diff --git a/include/BufferExecute.h b/include/BufferExecute.h index 99a1ab26..a23ba5c5 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -44,4 +44,4 @@ extern void bmp280ReadingPress(); extern void sysUptime(); extern void uptimeReading(); -extern void impuls(); + diff --git a/include/Global.h b/include/Global.h index b44cb8d7..86bc8f3a 100644 --- a/include/Global.h +++ b/include/Global.h @@ -81,6 +81,9 @@ extern int inOutput_EnterCounter; extern String pwmOut_KeyList; extern int pwmOut_EnterCounter; //========================================= +extern String countDown_KeyList; +extern int countDown_EnterCounter; +//========================================= // Sensors extern String sensorReadingMap10sec; diff --git a/include/items/vCountDown.h b/include/items/vCountDown.h new file mode 100644 index 00000000..1c5e9f14 --- /dev/null +++ b/include/items/vCountDown.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include "Global.h" + +class CountDownClass; + +typedef std::vector MyCountDownVector; + +class CountDownClass { +public: + CountDownClass(String key); + ~CountDownClass(); + + void loop(); + void execute(unsigned int countDownPeriod); + +private: + unsigned long _countDownPeriod = 0; + String _key; + bool _start = false; + +}; + +extern MyCountDownVector* myCountDown; + +extern void countDown(); +extern void countDownExecute(); \ No newline at end of file diff --git a/include/items/vImpulsOut.h b/include/items/vImpulsOut.h index 1d9f9fa6..79316073 100644 --- a/include/items/vImpulsOut.h +++ b/include/items/vImpulsOut.h @@ -1,6 +1,5 @@ #pragma once #include - #include "Global.h" class ImpulsOutClass; @@ -28,4 +27,5 @@ class ImpulsOutClass { extern MyImpulsOutVector* myImpulsOut; +extern void impuls(); extern void impulsExecute(); diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 893119b2..7b3d20b0 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -6,6 +6,8 @@ #include "items/vPwmOut.h" #include "items/vInOutput.h" #include "items/vLogging.h" +#include "items/vImpulsOut.h" +#include "items/vCountDown.h" void loopCmdAdd(const String& cmdStr) { orderBuf += cmdStr; @@ -89,6 +91,9 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("impuls-out")) { sCmd.addCommand(order.c_str(), impuls); } + else if (order == F("count-down")) { + sCmd.addCommand(order.c_str(), countDown); + } sCmd.readStr(buf); } diff --git a/src/Global.cpp b/src/Global.cpp index 96f9c7de..b7df47ec 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -52,6 +52,9 @@ int inOutput_EnterCounter = -1; String pwmOut_KeyList = ""; int pwmOut_EnterCounter = -1; //========================================= +String countDown_KeyList = ""; +int countDown_EnterCounter = -1; +//========================================= // Sensors String sensorReadingMap10sec; diff --git a/src/Init.cpp b/src/Init.cpp index 7972db73..9e57f92a 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -8,6 +8,7 @@ #include "items/vSensorDallas.h" #include "items/vInOutput.h" #include "items/vPwmOut.h" +#include "items/vCountDown.h" void loadConfig() { configSetupJson = readFile("config.json", 4096); @@ -73,6 +74,12 @@ void Device_init() { pwmOut_KeyList = ""; pwmOut_EnterCounter = -1; //=================================== + if (myCountDown != nullptr) { + myCountDown->clear(); + } + countDown_KeyList = ""; + countDown_EnterCounter = -1; + //=================================== #ifdef LAYOUT_IN_RAM diff --git a/src/items/vCountDown.cpp b/src/items/vCountDown.cpp new file mode 100644 index 00000000..2215ca9f --- /dev/null +++ b/src/items/vCountDown.cpp @@ -0,0 +1,71 @@ +#include "items/vCountDown.h" +#include "BufferExecute.h" +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" +#include + +CountDownClass::CountDownClass(String key) { + _key = key; +} + +CountDownClass::~CountDownClass() {} + +void CountDownClass::execute(unsigned int countDownPeriod) { + _countDownPeriod = countDownPeriod * 1000; + _start = true; +} + +void CountDownClass::loop() { + static unsigned long prevMillis1; + static unsigned long prevMillis2; + static int sec; + if (_countDownPeriod > 0 && _start) { + prevMillis1 = millis(); + _start = false; + sec = (_countDownPeriod / 1000) + 1; + } + unsigned long currentMillis = millis(); + unsigned long difference1 = currentMillis - prevMillis1; + unsigned long difference2 = currentMillis - prevMillis2; + if (difference1 > _countDownPeriod && _countDownPeriod > 0) { + _countDownPeriod = 0; + eventGen2(_key, "0"); + } + if (difference2 > 1000 && _countDownPeriod > 0) { + prevMillis2 = millis(); + Serial.println(sec--); + publishStatus(_key, String(sec)); + } +} + +MyCountDownVector* myCountDown = nullptr; + +void countDown() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + myLineParsing.clear(); + + countDown_EnterCounter++; + addKey(key, countDown_KeyList, countDown_EnterCounter); + + static bool firstTime = true; + if (firstTime) myCountDown = new MyCountDownVector(); + firstTime = false; + myCountDown->push_back(CountDownClass(key)); + + sCmd.addCommand(key.c_str(), countDownExecute); +} + +void countDownExecute() { + String key = sCmd.order(); + String countDownPeriod = sCmd.next(); + + int number = getKeyNum(key, countDown_KeyList); + + if (myCountDown != nullptr) { + if (number != -1) { + myCountDown->at(number).execute(countDownPeriod.toInt()); + } + } +} \ No newline at end of file diff --git a/src/items/vImpulsOut.cpp b/src/items/vImpulsOut.cpp index 911fdd22..9654cd69 100644 --- a/src/items/vImpulsOut.cpp +++ b/src/items/vImpulsOut.cpp @@ -1,10 +1,9 @@ #include "items/vImpulsOut.h" - -#include #include "BufferExecute.h" #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" +#include ImpulsOutClass::ImpulsOutClass(unsigned int impulsPin) { _impulsPin = impulsPin; diff --git a/src/main.cpp b/src/main.cpp index 7b4e9b07..4337dc23 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "items/vLogging.h" #include "items/vImpulsOut.h" #include "items/vSensorDallas.h" +#include "items/vCountDown.h" #include "Telegram.h" void not_async_actions(); @@ -156,16 +157,19 @@ void loop() { myLogging->at(i).loop(); } } - if (myImpulsOut != nullptr) { for (unsigned int i = 0; i < myImpulsOut->size(); i++) { myImpulsOut->at(i).loop(); } } - if (mySensorDallas2 != nullptr) { for (unsigned int i = 0; i < mySensorDallas2->size(); i++) { mySensorDallas2->at(i).loop(); } } + if (myCountDown != nullptr) { + for (unsigned int i = 0; i < myCountDown->size(); i++) { + myCountDown->at(i).loop(); + } + } } \ No newline at end of file From b2e9d90686720adb3aeae85c5f1365769b2f6998 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 18 Nov 2020 13:18:08 +0300 Subject: [PATCH 16/94] timer bug fully fixed --- data/items/count-down.txt | 2 +- include/items/vCountDown.h | 9 ++++++++- src/items/vCountDown.cpp | 18 ++++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/data/items/count-down.txt b/data/items/count-down.txt index fa38b94b..3300573c 100644 --- a/data/items/count-down.txt +++ b/data/items/count-down.txt @@ -1 +1 @@ -0;count-down;id;na;na;na;order \ No newline at end of file +0;count-down;id;anydata;Таймер;Обратный#отчет;order \ No newline at end of file diff --git a/include/items/vCountDown.h b/include/items/vCountDown.h index 1c5e9f14..a8d9d46a 100644 --- a/include/items/vCountDown.h +++ b/include/items/vCountDown.h @@ -16,8 +16,15 @@ public: private: unsigned long _countDownPeriod = 0; - String _key; bool _start = false; + String _key; + + unsigned long currentMillis; + unsigned long prevMillis1; + unsigned long prevMillis2; + unsigned long difference1; + unsigned long difference2; + int sec; }; diff --git a/src/items/vCountDown.cpp b/src/items/vCountDown.cpp index 2215ca9f..118f7b3a 100644 --- a/src/items/vCountDown.cpp +++ b/src/items/vCountDown.cpp @@ -17,24 +17,23 @@ void CountDownClass::execute(unsigned int countDownPeriod) { } void CountDownClass::loop() { - static unsigned long prevMillis1; - static unsigned long prevMillis2; - static int sec; if (_countDownPeriod > 0 && _start) { prevMillis1 = millis(); _start = false; - sec = (_countDownPeriod / 1000) + 1; + sec = (_countDownPeriod / 1000); } - unsigned long currentMillis = millis(); - unsigned long difference1 = currentMillis - prevMillis1; - unsigned long difference2 = currentMillis - prevMillis2; + currentMillis = millis(); + difference1 = currentMillis - prevMillis1; + difference2 = currentMillis - prevMillis2; if (difference1 > _countDownPeriod && _countDownPeriod > 0) { _countDownPeriod = 0; eventGen2(_key, "0"); + Serial.println(_key + " completed"); } if (difference2 > 1000 && _countDownPeriod > 0) { prevMillis2 = millis(); - Serial.println(sec--); + sec--; + Serial.println(_key + " " + String(sec)); publishStatus(_key, String(sec)); } } @@ -49,6 +48,9 @@ void countDown() { countDown_EnterCounter++; addKey(key, countDown_KeyList, countDown_EnterCounter); + Serial.println(countDown_EnterCounter); + Serial.println(countDown_KeyList); + static bool firstTime = true; if (firstTime) myCountDown = new MyCountDownVector(); firstTime = false; From 4f9763334b9707f2d444d5e50269c52fc50fb693 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 18 Nov 2020 13:29:53 +0300 Subject: [PATCH 17/94] fix some more bugs --- include/items/vImpulsOut.h | 2 +- include/items/vLogging.h | 1 + include/items/vSensorDallas.h | 1 + src/items/vImpulsOut.cpp | 2 +- src/items/vLogging.cpp | 2 +- src/items/vSensorDallas.cpp | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/items/vImpulsOut.h b/include/items/vImpulsOut.h index 79316073..2cd806ca 100644 --- a/include/items/vImpulsOut.h +++ b/include/items/vImpulsOut.h @@ -17,7 +17,7 @@ class ImpulsOutClass { private: unsigned long currentMillis; unsigned long prevMillis; - + unsigned long difference; unsigned long _impulsPeriod = 0; unsigned int _impulsCount = 0; unsigned int _impulsCountBuf = 0; diff --git a/include/items/vLogging.h b/include/items/vLogging.h index d1060334..53c098af 100644 --- a/include/items/vLogging.h +++ b/include/items/vLogging.h @@ -19,6 +19,7 @@ class LoggingClass { unsigned long currentMillis; unsigned long prevMillis; + unsigned long difference; unsigned long _period; unsigned int _maxPoints; String _loggingValueKey; diff --git a/include/items/vSensorDallas.h b/include/items/vSensorDallas.h index 3feeb572..2dff5f04 100644 --- a/include/items/vSensorDallas.h +++ b/include/items/vSensorDallas.h @@ -22,6 +22,7 @@ private: unsigned long currentMillis; unsigned long prevMillis; + unsigned long difference; unsigned long _interval; String _key; unsigned int _pin; diff --git a/src/items/vImpulsOut.cpp b/src/items/vImpulsOut.cpp index 9654cd69..f09d5756 100644 --- a/src/items/vImpulsOut.cpp +++ b/src/items/vImpulsOut.cpp @@ -20,7 +20,7 @@ void ImpulsOutClass::execute(unsigned long impulsPeriod, unsigned int impulsCoun void ImpulsOutClass::loop() { currentMillis = millis(); - unsigned long difference = currentMillis - prevMillis; + difference = currentMillis - prevMillis; if (_impulsCountBuf > 0) { if (difference > _impulsPeriod) { _impulsCountBuf--; diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 2958cf1c..95d1bef5 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -17,7 +17,7 @@ LoggingClass::~LoggingClass() {} void LoggingClass::loop() { currentMillis = millis(); - unsigned long difference = currentMillis - prevMillis; + difference = currentMillis - prevMillis; if (difference >= _period) { prevMillis = millis(); addNewDelOldData("logs/" + _key + ".txt", _maxPoints, jsonReadStr(configLiveJson, _loggingValueKey)); diff --git a/src/items/vSensorDallas.cpp b/src/items/vSensorDallas.cpp index 323bca12..d52b8b57 100644 --- a/src/items/vSensorDallas.cpp +++ b/src/items/vSensorDallas.cpp @@ -20,7 +20,7 @@ SensorDallas::~SensorDallas() {} void SensorDallas::loop() { currentMillis = millis(); - unsigned long difference = currentMillis - prevMillis; + difference = currentMillis - prevMillis; if (difference >= _interval) { prevMillis = millis(); readDallas(); From 70c34d3d8d2008527fcde1cb464389c0d02af0b3 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 19 Nov 2020 04:14:52 +0300 Subject: [PATCH 18/94] Cleaning and optimization --- include/Class/ScenarioClass3.h | 2 +- include/Consts.h | 5 +- include/Global.h | 1 + src/BufferExecute.cpp | 19 +- src/Global.cpp | 2 +- src/Init.cpp | 1 - src/ItemsCmd.cpp | 334 --------------------------- src/MqttDiscovery.cpp | 75 ------ src/PushingBox.cpp | 60 ----- src/Sensors.cpp | 405 --------------------------------- src/Telegram.cpp | 4 +- src/Timers.cpp | 91 -------- src/items/vCountDown.cpp | 2 +- src/items/vLogging.cpp | 2 +- src/main.cpp | 4 +- 15 files changed, 29 insertions(+), 978 deletions(-) delete mode 100644 src/ItemsCmd.cpp delete mode 100644 src/MqttDiscovery.cpp delete mode 100644 src/PushingBox.cpp delete mode 100644 src/Sensors.cpp delete mode 100644 src/Timers.cpp diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index f49dcea4..70782092 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -30,7 +30,7 @@ public: String setEventSign = selectFromMarkerToMarker(condition, " ", 1); String setEventValue = selectFromMarkerToMarker(condition, " ", 2); - if (!isDigitStr(setEventValue)) setEventValue = jsonReadStr(configLiveJson, setEventValue); + if (!isDigitStr(setEventValue)) setEventValue = getValue(setEventValue); //jsonReadStr(configLiveJson , setEventValue); boolean flag = false; diff --git a/include/Consts.h b/include/Consts.h index 6fed021f..5a99eae0 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -72,15 +72,12 @@ enum TimerTask_t { WIFI_SCAN, WIFI_MQTT_CONNECTION_CHECK, SENSORS10SEC, SENSORS30SEC, - TIMER_COUNTDOWN, TIME, TIME_SYNC, STATISTICS, - STATISTICS_WORK, UPTIME, UDP, - UDP_DB, - TEST }; + SYGNAL}; enum NotAsyncActions { do_ZERO, diff --git a/include/Global.h b/include/Global.h index 86bc8f3a..6fd81b67 100644 --- a/include/Global.h +++ b/include/Global.h @@ -54,6 +54,7 @@ extern String configSetupJson; //все настройки extern String configLiveJson; //все данные с датчиков (связан с mqtt) extern String configStoreJson; //все данные которые должны сохраняться extern String configOptionJson; //для трансфера +extern String getValue(String& key); // Mqtt extern String chipId; diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 7b3d20b0..5a36c915 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -94,7 +94,7 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("count-down")) { sCmd.addCommand(order.c_str(), countDown); } - + sCmd.readStr(buf); } cmdStr = deleteBeforeDelimiter(cmdStr, "\n"); @@ -163,3 +163,20 @@ int getKeyNum(String& key, String& keyNumberTable) { } return number; } + +String getValue(String& key) { + String live = jsonReadStr(configLiveJson, key); + String store = jsonReadStr(configStoreJson, key); + if (live != nullptr) { + return live; + } + else if (store != nullptr) { + return store; + } + else if (store == nullptr && live == nullptr) { + return "no value"; + } + else { + return "data error"; + } +} diff --git a/src/Global.cpp b/src/Global.cpp index b7df47ec..5b5318f3 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -5,7 +5,7 @@ AsyncWebSocket ws; //AsyncEventSource events; #endif -TickerScheduler ts(TEST + 1); +TickerScheduler ts(SYGNAL + 1); WiFiClient espClient; PubSubClient mqtt(espClient); StringCommand sCmd; diff --git a/src/Init.cpp b/src/Init.cpp index 9e57f92a..afd5af33 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -33,7 +33,6 @@ void loadConfig() { void all_init() { Device_init(); loadScenario(); - Timer_countdown_init(); } void Device_init() { diff --git a/src/ItemsCmd.cpp b/src/ItemsCmd.cpp deleted file mode 100644 index cbac328a..00000000 --- a/src/ItemsCmd.cpp +++ /dev/null @@ -1,334 +0,0 @@ -//#include "BufferExecute.h" -// -//#include "BufferExecute.h" -//#include "Class/NotAsync.h" -//#include "Cmd.h" -//#include "Global.h" -//#include "Module/Terminal.h" -//#include "Servo/Servos.h" -// -//#include "items/vSensorDallas.h" -// -//Terminal *term = nullptr; -// -//boolean but[NUM_BUTTONS]; -//Bounce *buttons = new Bounce[NUM_BUTTONS]; -// -//#ifdef ESP8266 -//SoftwareSerial *mySerial = nullptr; -//#else -//HardwareSerial *mySerial = nullptr; -//#endif -// -//void getData(); -// -//void cmd_init() { - //sCmd.addCommand("button-out", buttonOut); - //sCmd.addCommand("pwm-out", pwmOut); - //sCmd.addCommand("button-in", buttonIn); - - //sCmd.addCommand("input-digit", input); - //sCmd.addCommand("input-time", inputTime); - //sCmd.addCommand("output-text", textOut); - - //sCmd.addCommand("analog-adc", analogAdc); - //sCmd.addCommand("ultrasonic-cm", ultrasonicCm); - //sCmd.addCommand("dallas-temp", dallas); - - //sCmd.addCommand("dht-temp", dhtTemp); - //sCmd.addCommand("dht-hum", dhtHum); - - //sCmd.addCommand("bme280-temp", bme280Temp); - //sCmd.addCommand("bme280-hum", bme280Hum); - //sCmd.addCommand("bme280-press", bme280Press); - - //sCmd.addCommand("bmp280-temp", bmp280Temp); - //sCmd.addCommand("bmp280-press", bmp280Press); - - //sCmd.addCommand("modbus", modbus); - - //sCmd.addCommand("uptime", sysUptime); - - //sCmd.addCommand("logging", logging); - - //sCmd.addCommand("impuls-out", impuls); - - -//} - -// sCmd.addCommand("timerStart", timerStart_); -// sCmd.addCommand("timerStop", timerStop_); - -//#ifdef DHT_ENABLED -// sCmd.addCommand("dhtT", dhtT); -// sCmd.addCommand("dhtH", dhtH); -// sCmd.addCommand("dhtPerception", dhtP); -// sCmd.addCommand("dhtComfort", dhtC); -// sCmd.addCommand("dhtDewpoint", dhtD); -//#endif - -//#ifdef BMP_ENABLED -// sCmd.addCommand("bmp280T", bmp280T); -// sCmd.addCommand("bmp280P", bmp280P); -//#endif -// -//#ifdef BME_ENABLED -// sCmd.addCommand("bme280T", bme280T); -// sCmd.addCommand("bme280P", bme280P); -// sCmd.addCommand("bme280H", bme280H); -// sCmd.addCommand("bme280A", bme280A); -//#endif -// -//#ifdef STEPPER_ENABLED -// sCmd.addCommand("stepper", stepper); -// sCmd.addCommand("stepperSet", stepperSet); -//#endif -// -//#ifdef SERVO_ENABLED -// sCmd.addCommand("servo", servo_); -// sCmd.addCommand("servoSet", servoSet); -//#endif -// -//#ifdef SERIAL_ENABLED -// sCmd.addCommand("serialBegin", serialBegin); -// sCmd.addCommand("serialWrite", serialWrite); -// sCmd.addCommand("getData", getData); -//#endif -// -//#ifdef LOGGING_ENABLED -// sCmd.addCommand("logging", logging); -//#endif -// -// sCmd.addCommand("mqtt", mqttOrderSend); -// sCmd.addCommand("http", httpOrderSend); -// -//#ifdef PUSH_ENABLED -// sCmd.addCommand("push", pushControl); -//#endif -// -// sCmd.addCommand("firmwareUpdate", firmwareUpdate); -// sCmd.addCommand("firmwareVersion", firmwareVersion); - -//void text() { -// String number = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String page_number = sCmd.next(); -// -// createWidget(widget_name, page_name, page_number, "anydata", "text" + number); -//} -// -//void textSet() { -// String number = sCmd.next(); -// String text = sCmd.next(); -// text.replace("_", " "); -// -// if (text.indexOf("-time") >= 0) { -// text.replace("-time", ""); -// text.replace("#", " "); -// text = text + " " + timeNow->getDateTimeDotFormated(); -// } -// -// jsonWriteStr(configLiveJson, "text" + number, text); -// publishStatus("text" + number, text); -//} -//===================================================================================================================================== -////=========================================Модуль шагового мотора====================================================================== -//#ifdef STEPPER_ENABLED -////stepper 1 12 13 -//void stepper() { -// String stepper_number = sCmd.next(); -// String pin_step = sCmd.next(); -// String pin_dir = sCmd.next(); -// -// jsonWriteStr(configOptionJson, "stepper" + stepper_number, pin_step + " " + pin_dir); -// pinMode(pin_step.toInt(), OUTPUT); -// pinMode(pin_dir.toInt(), OUTPUT); -//} -// -////stepperSet 1 100 5 -//void stepperSet() { -// String stepper_number = sCmd.next(); -// String steps = sCmd.next(); -// jsonWriteStr(configOptionJson, "steps" + stepper_number, steps); -// String stepper_speed = sCmd.next(); -// String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper" + stepper_number), " "); -// String pin_dir = deleteBeforeDelimiter(jsonReadStr(configOptionJson, "stepper" + stepper_number), " "); -// Serial.println(pin_step); -// Serial.println(pin_dir); -// if (steps.toInt() > 0) digitalWrite(pin_dir.toInt(), HIGH); -// if (steps.toInt() < 0) digitalWrite(pin_dir.toInt(), LOW); -// if (stepper_number == "1") { -// ts.add( -// STEPPER1, stepper_speed.toInt(), [&](void *) { -// int steps_int = abs(jsonReadInt(configOptionJson, "steps1") * 2); -// static int count; -// count++; -// String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper1"), " "); -// digitalWrite(pin_step.toInt(), !digitalRead(pin_step.toInt())); -// yield(); -// if (count > steps_int) { -// digitalWrite(pin_step.toInt(), LOW); -// ts.remove(STEPPER1); -// count = 0; -// } -// }, -// nullptr, true); -// } -// if (stepper_number == "2") { -// ts.add( -// STEPPER2, stepper_speed.toInt(), [&](void *) { -// int steps_int = abs(jsonReadInt(configOptionJson, "steps2") * 2); -// static int count; -// count++; -// String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper2"), " "); -// digitalWrite(pin_step.toInt(), !digitalRead(pin_step.toInt())); -// yield(); -// if (count > steps_int) { -// digitalWrite(pin_step.toInt(), LOW); -// ts.remove(STEPPER2); -// count = 0; -// } -// }, -// nullptr, true); -// } -//} -//#endif -////==================================================================================================================================================== -////=================================================================Сервоприводы======================================================================= -//#ifdef SERVO_ENABLED -////servo 1 13 50 Мой#сервопривод Сервоприводы 0 100 0 180 2 -//void servo_() { -// String number = sCmd.next(); -// uint8_t pin = String(sCmd.next()).toInt(); -// int value = String(sCmd.next()).toInt(); -// -// String widget = sCmd.next(); -// String page = sCmd.next(); -// -// int min_value = String(sCmd.next()).toInt(); -// int max_value = String(sCmd.next()).toInt(); -// int min_deg = String(sCmd.next()).toInt(); -// int max_deg = String(sCmd.next()).toInt(); -// -// String pageNumber = sCmd.next(); -// -// jsonWriteStr(configOptionJson, "servo_pin" + number, String(pin, DEC)); -// -// value = map(value, min_value, max_value, min_deg, max_deg); -// -// Servo *servo = myServo.create(number.toInt(), pin); -// servo->write(value); -//#ifdef ESP32 -// myServo1.attach(servo_pin.toInt(), 500, 2400); -// myServo1.write(start_state_int); -//#endif -// -// jsonWriteInt(configOptionJson, "s_min_val" + number, min_value); -// jsonWriteInt(configOptionJson, "s_max_val" + number, max_value); -// jsonWriteInt(configOptionJson, "s_min_deg" + number, min_deg); -// jsonWriteInt(configOptionJson, "s_max_deg" + number, max_deg); -// -// jsonWriteInt(configLiveJson, "servo" + number, value); -// -// createWidgetParam(widget, page, pageNumber, "range", "servo" + number, "min", String(min_value), "max", String(max_value), "k", "1"); -//} -// -//void servoSet() { -// String number = sCmd.next(); -// int value = String(sCmd.next()).toInt(); -// -// value = map(value, -// jsonReadInt(configOptionJson, "s_min_val" + number), -// jsonReadInt(configOptionJson, "s_max_val" + number), -// jsonReadInt(configOptionJson, "s_min_deg" + number), -// jsonReadInt(configOptionJson, "s_max_deg" + number)); -// -// Servo *servo = myServo.get(number.toInt()); -// if (servo) { -// servo->write(value); -// } -// -// eventGen2("servo", number); -// jsonWriteInt(configLiveJson, "servo" + number, value); -// publishStatus("servo" + number, String(value, DEC)); -//} -//#endif -////==================================================================================================================================================== -////=============================================================Модуль сериал порта======================================================================= -// -//#ifdef SERIAL_ENABLED -//void serialBegin() { -// String s_speed = sCmd.next(); -// String rxPin = sCmd.next(); -// String txPin = sCmd.next(); -// -// if (mySerial) { -// delete mySerial; -// } -// -//#ifdef ESP8266 -// mySerial = new SoftwareSerial(rxPin.toInt(), txPin.toInt()); -// mySerial->begin(s_speed.toInt()); -//#else -// mySerial = new HardwareSerial(2); -// mySerial->begin(rxPin.toInt(), txPin.toInt()); -//#endif -// -// term = new Terminal(mySerial); -// term->setEOL(LF); -// term->enableColors(false); -// term->enableControlCodes(false); -// term->enableEcho(false); -// term->setOnReadLine([](const char *str) { -// String line = String(str); -// loopCmdAdd(line); -// }); -//} -// -//void getData() { -// String param = sCmd.next(); -// String res = param.length() ? jsonReadStr(configLiveJson, param) : configLiveJson; -// if (term) { -// term->println(res.c_str()); -// } -//} -// -//void serialWrite() { -// String payload = sCmd.next(); -// if (term) { -// term->println(payload.c_str()); -// } -//} -//#endif -////==================================================================================================================================================== -////=================================================Глобальные команды удаленного управления=========================================================== -// -//void mqttOrderSend() { -// String id = sCmd.next(); -// String order = sCmd.next(); -// -// String all_line = jsonReadStr(configSetupJson, "mqttPrefix") + "/" + id + "/order"; -// mqtt.publish(all_line.c_str(), order.c_str(), false); -//} -// -//void httpOrderSend() { -// String ip = sCmd.next(); -// String order = sCmd.next(); -// order.replace("_", "%20"); -// String url = "http://" + ip + "/cmd?command=" + order; -// getURL(url); -//} -// -//void firmwareUpdate() { -// myNotAsyncActions->make(do_UPGRADE); -//} -// -//void firmwareVersion() { -// String widget = sCmd.next(); -// String page = sCmd.next(); -// String pageNumber = sCmd.next(); -// -// jsonWriteStr(configLiveJson, "firmver", FIRMWARE_VERSION); -// createWidget(widget, page, pageNumber, "anydata", "firmver"); -//} diff --git a/src/MqttDiscovery.cpp b/src/MqttDiscovery.cpp deleted file mode 100644 index 7ccd0bc1..00000000 --- a/src/MqttDiscovery.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "MqttDiscovery.h" - -namespace Discovery { - -static const char json_is_defined[] = "is_defined"; -static const char jsonId[] = "id"; -static const char jsonBatt[] = "batt"; -static const char jsonLux[] = "lux"; -static const char jsonPres[] = "pres"; -static const char jsonFer[] = "fer"; -static const char jsonMoi[] = "moi"; -static const char jsonHum[] = "hum"; -static const char jsonTemp[] = "tem"; -static const char jsonStep[] = "steps"; -static const char jsonWeight[] = "weight"; -static const char jsonPresence[] = "presence"; -static const char jsonAltim[] = "altim"; -static const char jsonAltif[] = "altift"; -static const char jsonTempf[] = "tempf"; -static const char jsonMsg[] = "message"; -static const char jsonVal[] = "value"; -static const char jsonVolt[] = "volt"; -static const char jsonCurrent[] = "current"; -static const char jsonPower[] = "power"; -static const char jsonGpio[] = "gpio"; -static const char jsonFtcd[] = "ftcd"; -static const char jsonWm2[] = "wattsm2"; -static const char jsonAdc[] = "adc"; -static const char jsonPa[] = "pa"; - -const String getValueJson(const char* str) { - char buf[32]; - sprintf(buf, "{{ value_json.%s }}", str); - return buf; -} - -void createDiscovery( - const char* type, const char* name, const char* clazz, - const char* value_template, const char* payload_on, const char* payload_off, - const char* maasure_unit, int off_delay, const char* has_payload, const char* no_payload, - const char* avail_topi, const char* cmd_topic, const char* state_topic, bool child) { - //const char* unique_id = getUniqueId(name).c_str(); -} - -void createADC(const char* name) { - createDiscovery( - "Type", "Name", "Clazz", - "Value", "Payload", "NoPayload", - "Measure", 0, "HasPayload", "NoPayload", - "", "", "", false); -} - -void createSwitch(const char* name) { - createDiscovery( - "Type", "Name", "Clazz", - "Value", "Payload", "NoPayload", - "Measure", 0, "HasPayload", "NoPayload", - "", "", "", false); -} -// component, -// type, -// name, -// availability topic, -// device class, -// value template, payload on, payload off, unit of measurement -const char* BMEsensor[6][8] = { - {"sensor", "tempc", "bme", "temperature", "", "", "°C"}, //jsonTemp - {"sensor", "tempf", "bme", "temperature", "", "", "°F"}, //jsonTempf - {"sensor", "pa", "bme", "", "", "", "hPa"}, //jsonPa - {"sensor", "hum", "bme", "humidity", "", "", "%"}, // jsonHum - {"sensor", "altim", "bme", "", "", "", "m"}, //jsonAltim - {"sensor", "altift", "bme", "", "", "", "ft"} // jsonAltif -}; - -} // namespace Discovery diff --git a/src/PushingBox.cpp b/src/PushingBox.cpp deleted file mode 100644 index 5c2f91e4..00000000 --- a/src/PushingBox.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Global.h" - -void Push_init() { - server.on("/pushingboxDate", HTTP_GET, [](AsyncWebServerRequest* request) { - if (request->hasArg("pushingbox_id")) { - jsonWriteStr(configSetupJson, "pushingbox_id", request->getParam("pushingbox_id")->value()); - } - - saveConfig(); - - request->send(200, "text/text", "ok"); // отправляем ответ о выполнении - }); -} - -void pushControl() { - String title = sCmd.next(); - title.replace("#", " "); - String body = sCmd.next(); - body.replace("#", " "); - - static String body_old; - - const char* logServer = "api.pushingbox.com"; - String deviceId = jsonReadStr(configSetupJson, "pushingbox_id"); - - //Serial.println("- starting client"); - - WiFiClient client_push; - - //Serial.println("- connecting to pushing server: " + String(logServer)); - if (!client_push.connect(logServer, 80)) { - //Serial.println("- not connected"); - } else { - //Serial.println("- succesfully connected"); - - String postStr = "devid="; - postStr += String(deviceId); - - postStr += "&title="; - postStr += String(title); - - postStr += "&body="; - postStr += String(body); - - postStr += "\r\n\r\n"; - - //Serial.println("- sending data..."); - - client_push.print(F("POST /pushingbox HTTP/1.1\n")); - client_push.print(F("Host: api.pushingbox.com\n")); - client_push.print(F("Connection: close\n")); - client_push.print(F("Content-Type: application/x-www-form-urlencoded\n")); - client_push.print(F("Content-Length: ")); - client_push.print(postStr.length()); - client_push.print("\n\n"); - client_push.print(postStr); - } - client_push.stop(); - //Serial.println("- stopping the client"); -} diff --git a/src/Sensors.cpp b/src/Sensors.cpp deleted file mode 100644 index 4b657757..00000000 --- a/src/Sensors.cpp +++ /dev/null @@ -1,405 +0,0 @@ -//#include "Cmd.h" -//#include "Global.h" - -//GMedian<10, int> medianFilter; -// -//Adafruit_BMP280 bmp; -//Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor(); -//Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor(); -// -//Adafruit_BME280 bme; -//Adafruit_Sensor *bme_temp = bme.getTemperatureSensor(); -//Adafruit_Sensor *bme_pressure = bme.getPressureSensor(); -//Adafruit_Sensor *bme_humidity = bme.getHumiditySensor(); - -///const String perceptionStr(byte value); -///const String comfortStr(ComfortState value); - -//void bmp280T_reading(); - - - - - -////========================================================================================================================================= -////=========================================Модуль сенсоров DHT============================================================================= -//#ifdef DHT_ENABLED -////dhtT t 2 dht11 Температура#DHT,#t°C Датчики any-data 1 -//void dhtT() { -// String value_name = sCmd.next(); -// String pin = sCmd.next(); -// String sensor_type = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //dhtT_value_name = value_name; -// if (sensor_type == "dht11") { -// dht.setup(pin.toInt(), DHTesp::DHT11); -// } -// if (sensor_type == "dht22") { -// dht.setup(pin.toInt(), DHTesp::DHT22); -// } -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// //sensors_reading_map[4] = 1; -//} -// -//void dhtT_reading() { -// float value = 0; -// static int counter; -// if (dht.getStatus() != 0 && counter < 5) { -// // publishStatus(dhtT_value_name, String(dht.getStatusString())); -// counter++; -// } else { -// counter = 0; -// value = dht.getTemperature(); -// if (String(value) != "nan") { -// //eventGen2(dhtT_value_name, ""); -// //jsonWriteStr(configLiveJson, dhtT_value_name, String(value)); -// // publishStatus(dhtT_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + dhtT_value_name + "' data: " + String(value)); -// } -// } -//} -// -////dhtH h 2 dht11 Влажность#DHT,#t°C Датчики any-data 1 -//void dhtH() { -// String value_name = sCmd.next(); -// String pin = sCmd.next(); -// String sensor_type = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //dhtH_value_name = value_name; -// if (sensor_type == "dht11") { -// dht.setup(pin.toInt(), DHTesp::DHT11); -// } -// if (sensor_type == "dht22") { -// dht.setup(pin.toInt(), DHTesp::DHT22); -// } -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// //sensors_reading_map[5] = 1; -//} -// -//void dhtH_reading() { -// float value = 0; -// static int counter; -// if (dht.getStatus() != 0 && counter < 5) { -// // publishStatus(dhtH_value_name, String(dht.getStatusString())); -// counter++; -// } else { -// counter = 0; -// value = dht.getHumidity(); -// if (String(value) != "nan") { -// //eventGen2(dhtH_value_name, ""); -// //jsonWriteStr(configLiveJson, dhtH_value_name, String(value)); -// // publishStatus(dhtH_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + dhtH_value_name + "' data: " + String(value)); -// } -// } -//} -// -////dhtPerception Восприятие: Датчики 4 -//void dhtP() { -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String page_number = sCmd.next(); -// createWidgetByType(widget_name, page_name, page_number, "any-data", "dhtPerception"); -// //sensors_reading_map[6] = 1; -//} -// -//void dhtP_reading() { -// byte value; -// if (dht.getStatus() != 0) { -// publishStatus("dhtPerception", String(dht.getStatusString())); -// } else { -// //value = dht.computePerception(jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); -// String final_line = perceptionStr(value); -// jsonWriteStr(configLiveJson, "dhtPerception", final_line); -// eventGen2("dhtPerception", ""); -// publishStatus("dhtPerception", final_line); -// if (mqtt.connected()) { -// SerialPrint("I", "Sensor", "'dhtPerception' data: " + final_line); -// } -// } -//} -// -////dhtComfort Степень#комфорта: Датчики 3 -//void dhtC() { -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String page_number = sCmd.next(); -// createWidgetByType(widget_name, page_name, page_number, "anydata", "dhtComfort"); -// //sensors_reading_map[7] = 1; -//} -// -//void dhtC_reading() { -// ComfortState cf; -// if (dht.getStatus() != 0) { -// publishStatus("dhtComfort", String(dht.getStatusString())); -// } else { -// //dht.getComfortRatio(cf, jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); -// String final_line = comfortStr(cf); -// jsonWriteStr(configLiveJson, "dhtComfort", final_line); -// eventGen2("dhtComfort", ""); -// publishStatus("dhtComfort", final_line); -// SerialPrint("I", "Sensor", "'dhtComfort' send date " + final_line); -// } -//} -// -//const String perceptionStr(byte value) { -// String res; -// switch (value) { -// case 0: -// res = F("Сухой воздух"); -// break; -// case 1: -// res = F("Комфортно"); -// break; -// case 2: -// res = F("Уютно"); -// break; -// case 3: -// res = F("Хорошо"); -// break; -// case 4: -// res = F("Неудобно"); -// break; -// case 5: -// res = F("Довольно неудобно"); -// break; -// case 6: -// res = F("Очень неудобно"); -// break; -// case 7: -// res = F("Невыносимо"); -// default: -// res = F("Unknown"); -// break; -// } -// return res; -//} -// -//const String comfortStr(ComfortState value) { -// String res; -// switch (value) { -// case Comfort_OK: -// res = F("Отлично"); -// break; -// case Comfort_TooHot: -// res = F("Очень жарко"); -// break; -// case Comfort_TooCold: -// res = F("Очень холодно"); -// break; -// case Comfort_TooDry: -// res = F("Очень сухо"); -// break; -// case Comfort_TooHumid: -// res = F("Очень влажно"); -// break; -// case Comfort_HotAndHumid: -// res = F("Жарко и влажно"); -// break; -// case Comfort_HotAndDry: -// res = F("Жарко и сухо"); -// break; -// case Comfort_ColdAndHumid: -// res = F("Холодно и влажно"); -// break; -// case Comfort_ColdAndDry: -// res = F("Холодно и сухо"); -// break; -// default: -// res = F("Неизвестно"); -// break; -// }; -// return res; -//} -// -////dhtDewpoint Точка#росы: Датчики 5 -//void dhtD() { -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String page_number = sCmd.next(); -// createWidgetByType(widget_name, page_name, page_number, "anydata", "dhtDewpoint"); -// //sensors_reading_map[8] = 1; -//} -// -//void dhtD_reading() { -// float value; -// if (dht.getStatus() != 0) { -// publishStatus("dhtDewpoint", String(dht.getStatusString())); -// } else { -// //value = dht.computeDewPoint(jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); -// jsonWriteInt(configLiveJson, "dhtDewpoint", value); -// eventGen2("dhtDewpoint", ""); -// publishStatus("dhtDewpoint", String(value)); -// SerialPrint("I", "Sensor", "'dhtDewpoint' data: " + String(value)); -// } -//} -//#endif -//=========================================i2c bus esp8266 scl-4 sda-5 ==================================================================== -//========================================================================================================================================= -//=========================================Модуль сенсоров bmp280========================================================================== - -////bmp280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -//void bmp280T() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bmp280T_value_name = value_name; -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// bmp.begin(hexStringToUint8(address)); -// bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ -// Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */ -// Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ -// Adafruit_BMP280::FILTER_X16, /* Filtering. */ -// Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ -// //bmp_temp->printSensorDetails(); -// //sensors_reading_map[9] = 1; -//} -// -//void bmp280T_reading() { -// float value = 0; -// sensors_event_t temp_event; -// bmp_temp->getEvent(&temp_event); -// value = temp_event.temperature; -// //jsonWriteStr(configLiveJson, bmp280T_value_name, String(value)); -// //eventGen2(bmp280T_value_name, ""); -// // publishStatus(bmp280T_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + bmp280T_value_name + "' data: " + String(value)); -//} -// -////bmp280P press1 0x76 Давление#bmp280 Датчики any-data 2 -//void bmp280P() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bmp280P_value_name = value_name; -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// bmp.begin(hexStringToUint8(address)); -// bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ -// Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */ -// Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ -// Adafruit_BMP280::FILTER_X16, /* Filtering. */ -// Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ -// //bmp_temp->printSensorDetails(); -// //sensors_reading_map[10] = 1; -//} -// -//void bmp280P_reading() { -// float value = 0; -// sensors_event_t pressure_event; -// bmp_pressure->getEvent(&pressure_event); -// value = pressure_event.pressure; -// value = value / 1.333224; -// //jsonWriteStr(configLiveJson, bmp280P_value_name, String(value)); -// //eventGen2(bmp280P_value_name, ""); -// // publishStatus(bmp280P_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + bmp280P_value_name + "' data: " + String(value)); -//} -// -////========================================================================================================================================= -////=============================================Модуль сенсоров bme280====================================================================== -////bme280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -//void bme280T() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bme280T_value_name = value_name; -// //createWidgetByType(widget_name, page_name, page_number, type, value_name); -// //bme.begin(hexStringToUint8(address)); -// //sensors_reading_map[11] = 1; -//} -// -//void bme280T_reading() { -// float value = 0; -// value = bme.readTemperature(); -// //jsonWriteStr(configLiveJson, bme280T_value_name, String(value)); -// //eventGen2(bme280T_value_name, ""); -// // publishStatus(bme280T_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + bme280T_value_name + "' data: " + String(value)); -//} -// -////bme280P pres1 0x76 Давление#bmp280 Датчики any-data 1 -//void bme280P() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bme280P_value_name = value_name; -// //createWidgetByType(widget_name, page_name, page_number, type, value_name); -// //bme.begin(hexStringToUint8(address)); -// //sensors_reading_map[12] = 1; -//} -// -//void bme280P_reading() { -// float value = 0; -// value = bme.readPressure(); -// value = value / 1.333224 / 100; -// //jsonWriteStr(configLiveJson, bme280P_value_name, String(value)); -// //eventGen2(bme280P_value_name, ""); -// // publishStatus(bme280P_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + bme280P_value_name + "' data: " + String(value)); -//} -// -////bme280H hum1 0x76 Влажность#bmp280 Датчики any-data 1 -//void bme280H() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bme280H_value_name = value_name; -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// bme.begin(hexStringToUint8(address)); -// //sensors_reading_map[13] = 1; -//} -// -//void bme280H_reading() { -// float value = 0; -// value = bme.readHumidity(); -// //jsonWriteStr(configLiveJson, bme280H_value_name, String(value)); -// //eventGen2(bme280H_value_name, ""); -// // publishStatus(bme280H_value_name, String(value)); -// //SerialPrint("I", "Sensor", "'" + bme280H_value_name + "' data: " + String(value)); -//} -// -////bme280A altit1 0x76 Высота#bmp280 Датчики any-data 1 -//void bme280A() { -// String value_name = sCmd.next(); -// String address = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String page_number = sCmd.next(); -// //bme280A_value_name = value_name; -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// bme.begin(hexStringToUint8(address)); -// //sensors_reading_map[14] = 1; -//} -// -//void bme280A_reading() { -// float value = bme.readAltitude(1013.25); -// //jsonWriteStr(configLiveJson, bme280A_value_name, String(value, 2)); -// -// //eventGen2(bme280A_value_name, ""); -// -// // publishStatus(bme280A_value_name, String(value)); -// -// //SerialPrint("I", "Sensor", "'" + bme280A_value_name + "' data: " + String(value)); -//} diff --git a/src/Telegram.cpp b/src/Telegram.cpp index a26ca23e..bd478505 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -52,7 +52,7 @@ void telegramMsgParse(String msg) { } else if (msg.indexOf("get") != -1) { msg = deleteBeforeDelimiter(msg, "_"); - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), jsonReadStr(configLiveJson, msg)); + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), getValue(msg)); //jsonReadStr(configLiveJson , msg)); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + String(msg)); } else if (msg.indexOf("all") != -1) { @@ -99,7 +99,7 @@ String returnListOfParams() { count++; if (count > 1) { String id = selectFromMarkerToMarker(buf, ";", 2); - String value = jsonReadStr(configLiveJson, id); + String value = getValue(id); //jsonReadStr(configLiveJson , id); String page = selectFromMarkerToMarker(buf, ";", 4); page.replace("#", " "); String name = selectFromMarkerToMarker(buf, ";", 5); diff --git a/src/Timers.cpp b/src/Timers.cpp deleted file mode 100644 index b45ab7b6..00000000 --- a/src/Timers.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "Global.h" - -//================================================================================================================ -//=========================================Таймеры================================================================= -void Timer_countdown_init() { - ts.add( - TIMER_COUNTDOWN, 1000, [&](void*) { - String old_line = jsonReadStr(configOptionJson, "timers"); - if (old_line != "") { - //Serial.println(old_line); - int i = 0; - do { - String timer = selectFromMarkerToMarker(old_line, ",", i); - //Serial.print("timer no " + String(i) + ": "); - //Serial.println(timer); - if (timer == "not found" || timer == "") return; - int number = selectToMarker(timer, ":").toInt(); - int time = readTimer(number); - if (time == 0) { - delTimer(String(number)); - jsonWriteStr(configLiveJson, "timer" + String(number), "0"); - eventGen2("timer", String(number)); - } else { - time--; - addTimer(String(number), String(time)); - } - i++; - } while (i <= 9); - } - }, - nullptr, true); -} - -void timerStart_() { - String number = sCmd.next(); - String period_of_time = sCmd.next(); - String type = sCmd.next(); - if (period_of_time.indexOf("digit") != -1) { - //period_of_time = add_set(period_of_time); - period_of_time = jsonReadStr(configLiveJson, period_of_time); - } - if (type == "sec") period_of_time = period_of_time; - if (type == "min") period_of_time = String(period_of_time.toInt() * 60); - if (type == "hours") period_of_time = String(period_of_time.toInt() * 60 * 60); - addTimer(number, period_of_time); - jsonWriteStr(configLiveJson, "timer" + number, "1"); -} -void addTimer(String number, String time) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - String new_timer = number + ":" + time; - int psn1 = tmp.indexOf(number + ":"); //0 ищем позицию таймера который надо заменить - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - String timer = tmp.substring(psn1, psn2); //1:60 выделяем таймер который надо заменить - ///tmp.replace(timer, new_timer); //заменяем таймер на новый (во всей стороке) - tmp.replace(timer + ",", ""); - tmp += new_timer + ","; - } else { //если его нет - tmp += new_timer + ","; - } - jsonWriteStr(configOptionJson, "timers", tmp); - //Serial.println("ura"); -} - -void timerStop_() { - String number = sCmd.next(); - delTimer(number); -} - -void delTimer(String number) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - int psn1 = tmp.indexOf(number + ":"); //0 ищем позицию таймера который надо удалить - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - String timer = tmp.substring(psn1, psn2) + ","; //1:60, выделяем таймер который надо удалить - tmp.replace(timer, ""); //удаляем таймер - jsonWriteStr(configOptionJson, "timers", tmp); - } -} - -int readTimer(int number) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - int psn1 = tmp.indexOf(String(number) + ":"); //0 ищем позицию таймера который надо прочитать - String timer; - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - timer = tmp.substring(psn1, psn2); //1:60 выделяем таймер который надо прочитать - timer = deleteBeforeDelimiter(timer, ":"); - } - return timer.toInt(); -} \ No newline at end of file diff --git a/src/items/vCountDown.cpp b/src/items/vCountDown.cpp index 118f7b3a..58effe32 100644 --- a/src/items/vCountDown.cpp +++ b/src/items/vCountDown.cpp @@ -62,7 +62,7 @@ void countDown() { void countDownExecute() { String key = sCmd.order(); String countDownPeriod = sCmd.next(); - + int number = getKeyNum(key, countDown_KeyList); if (myCountDown != nullptr) { diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 95d1bef5..f7d2db7c 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -20,7 +20,7 @@ void LoggingClass::loop() { difference = currentMillis - prevMillis; if (difference >= _period) { prevMillis = millis(); - addNewDelOldData("logs/" + _key + ".txt", _maxPoints, jsonReadStr(configLiveJson, _loggingValueKey)); + addNewDelOldData("logs/" + _key + ".txt", _maxPoints, getValue(_loggingValueKey)); //jsonReadStr(configLiveJson , _loggingValueKey)); } } diff --git a/src/main.cpp b/src/main.cpp index 4337dc23..6acb6bba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -98,7 +98,7 @@ void setup() { //esp_log_level_set("esp_littlefs", ESP_LOG_NONE); ts.add( - TEST, 1000 * 60, [&](void*) { + SYGNAL, 1000 * 60, [&](void*) { SerialPrint("I", "System", printMemoryStatus()); switch (RSSIquality()) { case 0: @@ -126,6 +126,8 @@ void setup() { }, nullptr, true); + + just_load = false; initialized = true; } From 38924e4ed38c2e068d3a420d334ebbd0da0525ab Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 21 Nov 2020 06:03:02 +0300 Subject: [PATCH 19/94] Single network space for scenario added --- data/config.json | 2 +- data/set.device.json | 19 ++++++ include/Class/ScenarioClass3.h | 4 +- include/Consts.h | 6 +- src/Class/ScenarioClass3.cpp | 21 ++++++- src/RemoteOrdersUdp.cpp | 107 ++++++++++++++++++++------------- src/Web.cpp | 16 ++++- 7 files changed, 122 insertions(+), 53 deletions(-) diff --git a/data/config.json b/data/config.json index d060d7cc..bc169e5b 100644 --- a/data/config.json +++ b/data/config.json @@ -17,7 +17,7 @@ "telegonof": "0", "weblogin": "admin", "webpass": "admin", - "udponoff": "1", + "onescen": "1", "blink": "1", "oneWirePin": "2", "serverip": "http://206.189.49.244" diff --git a/data/set.device.json b/data/set.device.json index a5aefdac..297c9c37 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -170,6 +170,25 @@ { "type": "hr" }, + { + "type": "checkbox", + "name": "onescen", + "title": "Включить единые сценарии для всех устройств", + "action": "/set?onescen=[[onescen]]", + "state": "{{onescen}}" + }, + { + "type": "hr" + }, + { + "type": "button", + "title": "Отправить сценарии всем устройствам в локальной сети", + "action": "/set?scenudp", + "class": "btn btn-block btn-default" + }, + { + "type": "hr" + }, { "type": "button", "title": "Очистить графики и введенные данные", diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index 70782092..0d2eba1e 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -69,4 +69,6 @@ public: } }; -extern Scenario* myScenario; \ No newline at end of file +extern Scenario* myScenario; + +extern void streamEventUDP(String event); \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index 5a99eae0..d33a22dc 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -31,9 +31,8 @@ //#define MDNS_ENABLED //#define WEBSOCKET_ENABLED //#define LAYOUT_IN_RAM -//#define UDP_ENABLED +#define UDP_ENABLED //#define SSDP_ENABLED -#define SAVE_SETTINGS_TO_FLASH //=========Sensors enable/disable================================================================================================================================= #define LEVEL_ENABLED @@ -83,12 +82,11 @@ enum NotAsyncActions { do_ZERO, do_UPGRADE, do_GETLASTVERSION, - do_UDPDATAPARSE, - do_MQTTUDP, do_BUSSCAN, do_MQTTPARAMSCHANGED, do_deviceInit, do_delChoosingItems, + do_sendScenUDP, do_LAST, }; diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index 91776583..5bd439c9 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -1,13 +1,28 @@ #include "Class/ScenarioClass3.h" #include "MqttClient.h" +#include "RemoteOrdersUdp.h" Scenario* myScenario; void eventGen2(String eventName, String eventValue) { if (!jsonReadBool(configSetupJson, "scen")) { return; } - //Serial.println(eventName + " " + eventValue); - eventBuf += eventName + " " + eventValue + ","; + String event = eventName + " " + eventValue + ","; + eventBuf += event; - //publish(mqttPrefix, eventName + " " + eventValue + ","); + streamEventUDP(event); +} + +void streamEventUDP(String event) { + #ifdef UDP_ENABLED + + if (!jsonReadBool(configSetupJson, "onescen")) { + return; + } + + if (event.indexOf("timenow") == -1) { + event = "iotm;event:" + event; + asyncUdp.broadcastTo(event.c_str(), 4210); + } + #endif } \ No newline at end of file diff --git a/src/RemoteOrdersUdp.cpp b/src/RemoteOrdersUdp.cpp index 9b3cfa35..2a705c68 100644 --- a/src/RemoteOrdersUdp.cpp +++ b/src/RemoteOrdersUdp.cpp @@ -1,26 +1,33 @@ #include "RemoteOrdersUdp.h" #include #include "Global.h" +#include "Class/NotAsync.h" +#include "Init.h" #ifdef UDP_ENABLED AsyncUDP asyncUdp; void asyncUdpInit() { + + if (!jsonReadBool(configSetupJson, "onescen")) { + return; + } + if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210)) { asyncUdp.onPacket([](AsyncUDPPacket packet) { - Serial.print("UDP Packet Type: "); - Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); - - Serial.print("From: "); - Serial.print(packet.remoteIP()); - Serial.print(":"); - Serial.println(packet.remotePort()); - - Serial.print("To: "); - Serial.print(packet.localIP()); - Serial.print(":"); - Serial.println(packet.localPort()); + //Serial.print("UDP Packet Type: "); + //Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + // + //Serial.print("From: "); + //Serial.print(packet.remoteIP()); + //Serial.print(":"); + //Serial.println(packet.remotePort()); + // + //Serial.print("To: "); + //Serial.print(packet.localIP()); + //Serial.print(":"); + //Serial.println(packet.localPort()); //Serial.print(", Length: "); //Serial.print(packet.length()); // @@ -30,35 +37,65 @@ void asyncUdpInit() { String data = uint8tToString(packet.data(), packet.length()); Serial.print("[i] [udp] Packet received: '"); Serial.print(data); + if (udpPacketValidation(data)) { + Serial.println("', packet valid"); udpPacketParse(data); - //Serial.println("', Packet valid"); - } - else { - //Serial.println("', Packet invalid"); } + + //else { + // //Serial.println("', Packet invalid"); + //} //reply to the client - - String ip = WiFi.localIP().toString(); - asyncUdp.broadcastTo(ip.c_str(), packet.remotePort()); - + //String ip = WiFi.localIP().toString(); + //asyncUdp.broadcastTo(ip.c_str(), packet.remotePort()); //packet.printf(ip.c_str(), packet.length()); }); } - ts.add( - UDP, 10000, [&](void*) { + myNotAsyncActions->add( + do_sendScenUDP, [&](void*) { - Serial.println("sended"); - asyncUdp.broadcastTo("Anyone here?", 64130); - //asyncUdp.broadcast("test"); - //asyncUdp.print("Hello Server!"); + String scen = "iotm;scen:"; + scen += readFile(String(DEVICE_SCENARIO_FILE), 2048); + + asyncUdp.broadcastTo(scen.c_str(), 4210); }, - nullptr, true); + nullptr); + //ts.add( + //UDP, 10000, [&](void*) { + //Serial.println("sended"); + //asyncUdp.broadcastTo("iotm;Anyone here?", 4210); + //asyncUdp.broadcast("test"); + //asyncUdp.print("Hello Server!"); + //}, + //nullptr, true); + +} + +bool udpPacketValidation(String& data) { + if (data.indexOf("iotm;") != -1) { + return true; + } + else { + return false; + } +} + +void udpPacketParse(String& data) { + if (data.indexOf("scen:") != -1) { + data = deleteBeforeDelimiter(data, ":"); + writeFile(String(DEVICE_SCENARIO_FILE), data); + loadScenario(); + } + else if (data.indexOf("event:") != -1) { + data = deleteBeforeDelimiter(data, ":"); + eventBuf += data; + } } String uint8tToString(uint8_t* data, size_t len) { @@ -68,20 +105,4 @@ String uint8tToString(uint8_t* data, size_t len) { } return ret; } - -bool udpPacketValidation(String& data) { - if (data.indexOf("iotm;") != -1 && data.indexOf(getChipId()) != -1) { - return true; - } - else { - return false; - } -} - -//iotm;chipid;button-out-1_1 -void udpPacketParse(String& data) { - data = selectFromMarkerToMarker(data, ";", 2); - data.replace("_", " "); - orderBuf += data + ","; -} #endif \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index c77e1266..3cef8edc 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -5,6 +5,7 @@ #include "ItemsList.h" #include "items/vLogging.h" #include "Telegram.h" +#include "RemoteOrdersUdp.h" bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { if (request->hasArg("preset")) { @@ -60,6 +61,19 @@ void web_init() { request->send(200); } + if (request->hasArg("onescen")) { + bool value = request->getParam("onescen")->value().toInt(); + jsonWriteBool(configSetupJson, "onescen", value); + saveConfig(); + asyncUdpInit(); + request->send(200); + } + + if (request->hasArg("scenudp")) { + myNotAsyncActions->make(do_sendScenUDP); + request->send(200); + } + #ifdef LOGGING_ENABLED if (request->hasArg("cleanlog")) { cleanLogAndData(); @@ -182,7 +196,7 @@ void web_init() { } if (request->hasArg("mqttsend")) { - myNotAsyncActions->make(do_MQTTUDP); + //myNotAsyncActions->make(do_MQTTUDP); request->send(200); } From e88718ada0b7c0409159c010c95508bdbb216047 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 2 Dec 2020 04:12:10 +0300 Subject: [PATCH 20/94] 268 Single Scenario changed to MQTT source --- data/config.json | 3 +- data/set.device.json | 10 +++--- include/Consts.h | 7 ++-- include/MqttClient.h | 1 + src/Class/ScenarioClass3.cpp | 2 +- src/MqttClient.cpp | 64 ++++++++++++++++++++++++++---------- src/RemoteOrdersUdp.cpp | 2 +- src/Web.cpp | 28 ++++++++++++---- 8 files changed, 83 insertions(+), 34 deletions(-) diff --git a/data/config.json b/data/config.json index bc169e5b..d5ebf98f 100644 --- a/data/config.json +++ b/data/config.json @@ -17,7 +17,8 @@ "telegonof": "0", "weblogin": "admin", "webpass": "admin", - "onescen": "1", + "snaUdp": "0", + "snaMqtt": "0", "blink": "1", "oneWirePin": "2", "serverip": "http://206.189.49.244" diff --git a/data/set.device.json b/data/set.device.json index 297c9c37..a8438cc1 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -172,18 +172,18 @@ }, { "type": "checkbox", - "name": "onescen", + "name": "snaMqtt", "title": "Включить единые сценарии для всех устройств", - "action": "/set?onescen=[[onescen]]", - "state": "{{onescen}}" + "action": "/set?snaMqtt=[[snaMqtt]]", + "state": "{{snaMqtt}}" }, { "type": "hr" }, { "type": "button", - "title": "Отправить сценарии всем устройствам в локальной сети", - "action": "/set?scenudp", + "title": "Синхронизировать сценарии на всех устройствах", + "action": "/set?scenMqtt", "class": "btn btn-block btn-default" }, { diff --git a/include/Consts.h b/include/Consts.h index d33a22dc..5f679e8c 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -3,11 +3,11 @@ //===========Firmware============================================================================================================================================= #ifdef ESP8266 #define FIRMWARE_NAME "esp8266-iotm" -#define FIRMWARE_VERSION 267 +#define FIRMWARE_VERSION 268 #endif #ifdef ESP32 #define FIRMWARE_NAME "esp32-iotm" -#define FIRMWARE_VERSION 267 +#define FIRMWARE_VERSION 268 #endif #define FLASH_4MB true @@ -31,7 +31,7 @@ //#define MDNS_ENABLED //#define WEBSOCKET_ENABLED //#define LAYOUT_IN_RAM -#define UDP_ENABLED +//#define UDP_ENABLED //#define SSDP_ENABLED //=========Sensors enable/disable================================================================================================================================= @@ -87,6 +87,7 @@ enum NotAsyncActions { do_deviceInit, do_delChoosingItems, do_sendScenUDP, + do_sendScenMQTT, do_LAST, }; diff --git a/include/MqttClient.h b/include/MqttClient.h index 1acc89b3..f2d7b82c 100644 --- a/include/MqttClient.h +++ b/include/MqttClient.h @@ -17,6 +17,7 @@ boolean publishChart(const String& topic, const String& data); boolean publishControl(String id, String topic, String state); boolean publishChart_test(const String& topic, const String& data); boolean publishStatus(const String& topic, const String& data); +boolean publishInfo(const String& topic, const String& data); void publishWidgets(); void publishState(); diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index 5bd439c9..085f8784 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -16,7 +16,7 @@ void eventGen2(String eventName, String eventValue) { void streamEventUDP(String event) { #ifdef UDP_ENABLED - if (!jsonReadBool(configSetupJson, "onescen")) { + if (!jsonReadBool(configSetupJson, "snaUdp")) { return; } diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 38844fb0..3486745d 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -39,6 +39,13 @@ void mqttInit() { } }, nullptr, true); + + myNotAsyncActions->add( + do_sendScenMQTT, [&](void*) { + String scen = readFile(String(DEVICE_SCENARIO_FILE), 2048); + publishInfo("scen", scen); + }, + nullptr); } void mqttDisconnect() { @@ -63,8 +70,11 @@ void mqttSubscribe() { mqtt.subscribe(mqttPrefix.c_str()); mqtt.subscribe((mqttRootDevice + "/+/control").c_str()); mqtt.subscribe((mqttRootDevice + "/update").c_str()); - //mqtt.subscribe((mqttRootDevice + "/order").c_str()); - //mqtt.subscribe((mqttPrefix + "/event").c_str()); + + if (jsonReadBool(configSetupJson, "snaMqtt")) { + mqtt.subscribe((mqttPrefix + "/+/+/status").c_str()); + mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); + } } boolean mqttConnect() { @@ -101,14 +111,14 @@ boolean mqttConnect() { void mqttCallback(char* topic, uint8_t* payload, size_t length) { String topicStr = String(topic); - SerialPrint("I", "MQTT", topicStr); + //SerialPrint("I", "=>MQTT", topicStr); String payloadStr; payloadStr.reserve(length + 1); for (size_t i = 0; i < length; i++) { payloadStr += (char)payload[i]; } - SerialPrint("I", "MQTT", payloadStr); + //SerialPrint("I", "=>MQTT", payloadStr); if (payloadStr.startsWith("HELLO")) { SerialPrint("I", "MQTT", "Full update"); @@ -119,7 +129,8 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { #endif } - else if (topicStr.indexOf("control")) { + + else if (topicStr.indexOf("control") != -1) { String key = selectFromMarkerToMarker(topicStr, "/", 3); @@ -128,23 +139,37 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { orderBuf += payloadStr; orderBuf += ","; - } - else if (topicStr.indexOf("order")) { - //payloadStr.replace("_", " "); - //orderBuf += payloadStr; - //orderBuf += ","; - + SerialPrint("I", "=>MQTT", "Msg from iotmanager app: " + key + " " + payloadStr); } - //else if (topicStr.indexOf("event")) { - // eventBuf += payloadStr; - //} + else if (topicStr.indexOf("status") != -1) { + if (!jsonReadBool(configSetupJson, "snaMqtt")) { + return; + } + if (topicStr.indexOf(chipId) == -1) { + String devId = selectFromMarkerToMarker(topicStr, "/", 2); + String key = selectFromMarkerToMarker(topicStr, "/", 3); + String value = jsonReadStr(payloadStr, "status"); - else if (topicStr.indexOf("update")) { - if (payloadStr == "1") { - myNotAsyncActions->make(do_UPGRADE); + SerialPrint("I", "=>MQTT", "Msg from other device: '" + devId + "' " + key + " " + value); + + eventGen2(key, value); } } + + else if (topicStr.indexOf("info") != -1) { + if (topicStr.indexOf("scen") != -1) { + writeFile(String(DEVICE_SCENARIO_FILE), payloadStr); + loadScenario(); + SerialPrint("I", "=>MQTT", "Scenario received"); + } + } + + //else if (topicStr.indexOf("update")) { + // if (payloadStr == "1") { + // myNotAsyncActions->make(do_UPGRADE); + // } + //} } boolean publish(const String& topic, const String& data) { @@ -190,6 +215,11 @@ boolean publishStatus(const String& topic, const String& data) { return mqtt.publish(path.c_str(), json.c_str(), false); } +boolean publishInfo(const String& topic, const String& data) { + String path = mqttRootDevice + "/" + topic + "/info"; + return mqtt.publish(path.c_str(), data.c_str(), false); +} + #ifdef LAYOUT_IN_RAM void publishWidgets() { if (all_widgets != "") { diff --git a/src/RemoteOrdersUdp.cpp b/src/RemoteOrdersUdp.cpp index 2a705c68..c3428fec 100644 --- a/src/RemoteOrdersUdp.cpp +++ b/src/RemoteOrdersUdp.cpp @@ -9,7 +9,7 @@ AsyncUDP asyncUdp; void asyncUdpInit() { - if (!jsonReadBool(configSetupJson, "onescen")) { + if (!jsonReadBool(configSetupJson, "snaUdp")) { return; } diff --git a/src/Web.cpp b/src/Web.cpp index 3cef8edc..d3b68397 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -61,16 +61,32 @@ void web_init() { request->send(200); } - if (request->hasArg("onescen")) { - bool value = request->getParam("onescen")->value().toInt(); - jsonWriteBool(configSetupJson, "onescen", value); + //if (request->hasArg("snaUdp")) { + // bool value = request->getParam("snaUdp")->value().toInt(); + // jsonWriteBool(configSetupJson, "snaUdp", value); + // saveConfig(); + // #ifdef UDP_ENABLED + // asyncUdpInit(); + // #endif + // request->send(200); + //} + + //if (request->hasArg("scenUdp")) { + // myNotAsyncActions->make(do_sendScenUDP); + // request->send(200); + //} + + if (request->hasArg("snaMqtt")) { + bool value = request->getParam("snaMqtt")->value().toInt(); + jsonWriteBool(configSetupJson, "snaMqtt", value); saveConfig(); - asyncUdpInit(); + mqtt.subscribe((mqttPrefix + "/+/+/status").c_str()); + mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); request->send(200); } - if (request->hasArg("scenudp")) { - myNotAsyncActions->make(do_sendScenUDP); + if (request->hasArg("scenMqtt")) { + myNotAsyncActions->make(do_sendScenMQTT); request->send(200); } From 49f11841be00ffcb367e7aa5178d98b6e8bb65b9 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 2 Dec 2020 04:50:29 +0300 Subject: [PATCH 21/94] Telegram --- data/config.json | 1 + data/set.telegram.json | 31 +++++++++++++++++++++++++------ include/Telegram.h | 1 + src/Telegram.cpp | 33 ++++++++++++++++++++------------- src/Web.cpp | 13 +++++++++++-- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/data/config.json b/data/config.json index d5ebf98f..20cb7760 100644 --- a/data/config.json +++ b/data/config.json @@ -15,6 +15,7 @@ "scen": "1", "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", + "teleginput":"0", "weblogin": "admin", "webpass": "admin", "snaUdp": "0", diff --git a/data/set.telegram.json b/data/set.telegram.json index 76560093..43db41e8 100644 --- a/data/set.telegram.json +++ b/data/set.telegram.json @@ -21,30 +21,49 @@ }, { "type": "checkbox", - "name": "tel", + "name": "telegonof", "title": "Включить телеграм", - "action": "/set?telegonof=[[tel]]", + "action": "/set?telegonof=[[telegonof]]", "state": "{{telegonof}}" }, { "type": "hr" }, + { + "type": "checkbox", + "name": "teleginput", + "title": "Включить прием входящих сообщений", + "action": "/set?teleginput=[[teleginput]]", + "state": "{{teleginput}}" + }, + { + "type": "hr" + }, { "type": "h4", - "style": "width:40%;float:left;", - "title": "Telegram API token:" + "title": "Telegram chat ID" + }, + { + "type": "input", + "title": "", + "name": "chatId-arg", + "state": "{{chatId}}" + }, + + { + "type": "h4", + "title": "Telegram API token" }, { "type": "input", "title": "", "name": "telegramApi-arg", - "style": "width:60%;float:right", "state": "{{telegramApi}}" }, { "type": "button", "title": "{{ButSave}}", - "action": "set?telegramApi=[[telegramApi-arg]]", + "action": "set?telegramApi=[[telegramApi-arg]]&chatId=[[chatId-arg]]", "class": "btn btn-block btn-default", "style": "width:100%;display:inline" }, diff --git a/include/Telegram.h b/include/Telegram.h index 9fd18857..0571fd40 100644 --- a/include/Telegram.h +++ b/include/Telegram.h @@ -5,5 +5,6 @@ extern void sendTelegramMsg(); extern void telegramInit(); extern void handleTelegram(); extern bool isTelegramEnabled(); +extern bool isTelegramInputOn(); extern void telegramMsgParse(String msg); extern String returnListOfParams(); \ No newline at end of file diff --git a/src/Telegram.cpp b/src/Telegram.cpp index bd478505..cc560330 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -25,17 +25,19 @@ void telegramInit() { void handleTelegram() { if (telegramInitBeen) { if (isTelegramEnabled()) { - TBMessage msg; - static unsigned long prevMillis; - unsigned long currentMillis = millis(); - unsigned long difference = currentMillis - prevMillis; - if (difference >= 5000) { - prevMillis = millis(); - if (myBot->getNewMessage(msg)) { - SerialPrint("->", "Telegram", "chat ID: " + String(msg.sender.id) + ", msg: " + String(msg.text)); - jsonWriteInt(configSetupJson, "chatId", msg.sender.id); - saveConfig(); - telegramMsgParse(String(msg.text)); + if (isTelegramInputOn()) { + TBMessage msg; + static unsigned long prevMillis; + unsigned long currentMillis = millis(); + unsigned long difference = currentMillis - prevMillis; + if (difference >= 10000) { + prevMillis = millis(); + if (myBot->getNewMessage(msg)) { + SerialPrint("->", "Telegram", "chat ID: " + String(msg.sender.id) + ", msg: " + String(msg.text)); + jsonWriteInt(configSetupJson, "chatId", msg.sender.id); + saveConfig(); + telegramMsgParse(String(msg.text)); + } } } } @@ -76,16 +78,21 @@ void sendTelegramMsg() { myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); } - } else if (type == "2") { + } + else if (type == "2") { myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); - } + } } bool isTelegramEnabled() { return jsonReadBool(configSetupJson, "telegonof"); } +bool isTelegramInputOn() { + return jsonReadBool(configSetupJson, "teleginput"); +} + String returnListOfParams() { String cmdStr = readFile(DEVICE_CONFIG_FILE, 4096); diff --git a/src/Web.cpp b/src/Web.cpp index d3b68397..c571ccaf 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -229,14 +229,23 @@ void web_init() { //==============================push settings============================================= if (request->hasArg("telegramApi")) { jsonWriteStr(configSetupJson, "telegramApi", request->getParam("telegramApi")->value()); - //telegramInit(); + saveConfig(); + request->send(200); + } + if (request->hasArg("chatId")) { + jsonWriteStr(configSetupJson, "chatId", request->getParam("chatId")->value()); saveConfig(); request->send(200); } if (request->hasArg("telegonof")) { bool value = request->getParam("telegonof")->value().toInt(); jsonWriteBool(configSetupJson, "telegonof", value); - //telegramInit(); + saveConfig(); + request->send(200); + } + if (request->hasArg("teleginput")) { + bool value = request->getParam("teleginput")->value().toInt(); + jsonWriteBool(configSetupJson, "teleginput", value); saveConfig(); request->send(200); } From 7043855e3dc77f793aab53ada2d40d4b6373bf73 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:05:16 +0300 Subject: [PATCH 22/94] randomSeed --- data/set.device.json | 2 +- src/ItemsList.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/data/set.device.json b/data/set.device.json index a8438cc1..3f4cd654 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -43,7 +43,7 @@ }, { "type": "h4", - "title": "Версия файловой системы: 267" + "title": "Версия файловой системы: 268" }, { "type": "h4", diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 290380f2..ee865c2b 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -23,9 +23,12 @@ void itemsListInit() { void addItem(String name) { String item = readFile("items/" + name + ".txt", 1024); - name = deleteToMarkerLast(name, "."); + name = selectToMarker(name, "-"); - item.replace("id", name + "-" + String(getNewElementNumber("id.txt"))); + randomSeed(micros()); + unsigned int num = random(0, 1000); + + item.replace("id", name + String(num)); // "-" + String(getNewElementNumber("id.txt"))); item.replace("order", String(getNewElementNumber("order.txt"))); if (item.indexOf("pin") != -1) { //all cases (random pins from available) From d15cf619f130c03dc0b80d5726aad0716a1e5ba0 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 6 Dec 2020 00:34:30 +0300 Subject: [PATCH 23/94] Fixed Ultrasonic, Start uart --- data/config.json | 4 +- data/items/ultrasonic-cm.txt | 2 +- include/BufferExecute.h | 4 +- include/SoftUART.h | 7 ++ include/items/SensorConvertingClass.h | 2 +- include/items/SensorUltrasonicClass.h | 98 +++++++++++++-------------- include/items/vSensorUltrasonic.h | 47 +++++++++++++ src/BufferExecute.cpp | 3 +- src/Init.cpp | 5 ++ src/SoftUART.cpp | 27 ++++++++ src/items/SensorUltrasonicClass.cpp | 40 +++++------ src/items/vSensorUltrasonic.cpp | 89 ++++++++++++++++++++++++ src/main.cpp | 6 ++ 13 files changed, 258 insertions(+), 76 deletions(-) create mode 100644 include/SoftUART.h create mode 100644 include/items/vSensorUltrasonic.h create mode 100644 src/SoftUART.cpp create mode 100644 src/items/vSensorUltrasonic.cpp diff --git a/data/config.json b/data/config.json index 20cb7760..eec796ae 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", - "routerpass": "hostel3333", + "routerssid": "VOLODYA", + "routerpass": "BELCHENKO", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "wqtt.ru", diff --git a/data/items/ultrasonic-cm.txt b/data/items/ultrasonic-cm.txt index cca50890..c5d518c3 100644 --- a/data/items/ultrasonic-cm.txt +++ b/data/items/ultrasonic-cm.txt @@ -1 +1 @@ -0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1] \ No newline at end of file +0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] \ No newline at end of file diff --git a/include/BufferExecute.h b/include/BufferExecute.h index a23ba5c5..a236e57d 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -15,8 +15,8 @@ extern void buttonInSet(); extern void analogAdc(); extern void analogReading(); -extern void ultrasonicCm(); -extern void ultrasonicReading(); +//extern void ultrasonicCm(); +//extern void ultrasonicReading(); extern void dallasTemp(); extern void dallasReading(); diff --git a/include/SoftUART.h b/include/SoftUART.h new file mode 100644 index 00000000..1955616f --- /dev/null +++ b/include/SoftUART.h @@ -0,0 +1,7 @@ +#pragma once + +#include "SoftwareSerial.h" + +extern void uartInit(); +extern void uartHandle(); +extern void parse(String& incStr); \ No newline at end of file diff --git a/include/items/SensorConvertingClass.h b/include/items/SensorConvertingClass.h index 5ddf59c8..f7e1e5cd 100644 --- a/include/items/SensorConvertingClass.h +++ b/include/items/SensorConvertingClass.h @@ -29,4 +29,4 @@ class SensorConvertingClass : public LineParsing { } return input; } -}; \ No newline at end of file +}; diff --git a/include/items/SensorUltrasonicClass.h b/include/items/SensorUltrasonicClass.h index 6adce30d..96d6883c 100644 --- a/include/items/SensorUltrasonicClass.h +++ b/include/items/SensorUltrasonicClass.h @@ -1,49 +1,49 @@ -#pragma once -#include - -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" -#include "GyverFilters.h" - -GMedian<6, int> testFilter; - -class SensorUltrasonic : public SensorConvertingClass { - public: - SensorUltrasonic() : SensorConvertingClass(){}; - void init() { - sensorReadingMap10sec += _key + ","; - String trig = selectFromMarkerToMarker(_pin, ",", 0); - String echo = selectFromMarkerToMarker(_pin, ",", 1); - pinMode(trig.toInt(), OUTPUT); - pinMode(echo.toInt(), INPUT); - jsonWriteStr(configOptionJson, _key + "_trig", trig); - jsonWriteStr(configOptionJson, _key + "_echo", echo); - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - } - - void SensorUltrasonicRead(String key) { - int trig = jsonReadStr(configOptionJson, key + "_trig").toInt(); - int echo = jsonReadStr(configOptionJson, key + "_echo").toInt(); - int value; - - digitalWrite(trig, LOW); - delayMicroseconds(2); - digitalWrite(trig, HIGH); - delayMicroseconds(10); - digitalWrite(trig, LOW); - long duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m - value = duration_ / 29 / 2; - - value = testFilter.filtered(value); - - value = this->mapping(key, value); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } -}; -extern SensorUltrasonic mySensorUltrasonic; \ No newline at end of file +//#pragma once +//#include +// +//#include "Class/LineParsing.h" +//#include "Global.h" +//#include "items/SensorConvertingClass.h" +//#include "GyverFilters.h" +// +//GMedian<6, int> testFilter; +// +//class SensorUltrasonic : public SensorConvertingClass { +// public: +// SensorUltrasonic() : SensorConvertingClass(){}; +// void init() { +// sensorReadingMap10sec += _key + ","; +// String trig = selectFromMarkerToMarker(_pin, ",", 0); +// String echo = selectFromMarkerToMarker(_pin, ",", 1); +// pinMode(trig.toInt(), OUTPUT); +// pinMode(echo.toInt(), INPUT); +// jsonWriteStr(configOptionJson, _key + "_trig", trig); +// jsonWriteStr(configOptionJson, _key + "_echo", echo); +// jsonWriteStr(configOptionJson, _key + "_map", _map); +// jsonWriteStr(configOptionJson, _key + "_с", _c); +// } +// +// void SensorUltrasonicRead(String key) { +// int trig = jsonReadStr(configOptionJson, key + "_trig").toInt(); +// int echo = jsonReadStr(configOptionJson, key + "_echo").toInt(); +// int value; +// +// digitalWrite(trig, LOW); +// delayMicroseconds(2); +// digitalWrite(trig, HIGH); +// delayMicroseconds(10); +// digitalWrite(trig, LOW); +// long duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m +// value = duration_ / 29 / 2; +// +// value = testFilter.filtered(value); +// +// value = this->mapping(key, value); +// float valueFl = this->correction(key, value); +// eventGen2(key, String(valueFl)); +// jsonWriteStr(configLiveJson, key, String(valueFl)); +// publishStatus(key, String(valueFl)); +// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); +// } +//}; +//extern SensorUltrasonic mySensorUltrasonic; \ No newline at end of file diff --git a/include/items/vSensorUltrasonic.h b/include/items/vSensorUltrasonic.h new file mode 100644 index 00000000..a0c6311c --- /dev/null +++ b/include/items/vSensorUltrasonic.h @@ -0,0 +1,47 @@ +#pragma once +#include "Global.h" +#include +#include "items/SensorConvertingClass.h" +#include "GyverFilters.h" + +class SensorUltrasonic; + +typedef std::vector MySensorUltrasonicVector; + + + +class SensorUltrasonic : public SensorConvertingClass { +public: + + SensorUltrasonic(String key, unsigned long interval, unsigned int trig, unsigned int echo, int map1, int map2, int map3, int map4, float c); + ~SensorUltrasonic(); + + void loop(); + void readUltrasonic(); + +private: + + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + + unsigned long _interval; + + String _key; + unsigned int _echo; + unsigned int _trig; + + int _map1; + int _map2; + int _map3; + int _map4; + + float _c; + +}; + +extern MySensorUltrasonicVector* mySensorUltrasonic; + +extern void ultrasonic(); + + diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 5a36c915..ad33b79c 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -2,6 +2,7 @@ #include "Global.h" // #include "items/vSensorDallas.h" +#include "items/vSensorUltrasonic.h" #include "items/vButtonOut.h" #include "items/vPwmOut.h" #include "items/vInOutput.h" @@ -53,7 +54,7 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), analogAdc); } else if (order == F("ultrasonic-cm")) { - sCmd.addCommand(order.c_str(), ultrasonicCm); + sCmd.addCommand(order.c_str(), ultrasonic); } else if (order == F("dallas-temp")) { sCmd.addCommand(order.c_str(), dallas); diff --git a/src/Init.cpp b/src/Init.cpp index afd5af33..6f3db433 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -6,6 +6,7 @@ #include "items/vImpulsOut.h" #include "items/vButtonOut.h" #include "items/vSensorDallas.h" +#include "items/vSensorUltrasonic.h" #include "items/vInOutput.h" #include "items/vPwmOut.h" #include "items/vCountDown.h" @@ -43,6 +44,10 @@ void Device_init() { if (mySensorDallas2 != nullptr) { mySensorDallas2->clear(); } + //======clear ultrasonic params====== + if (mySensorUltrasonic != nullptr) { + mySensorUltrasonic->clear(); + } //======clear logging params====== if (myLogging != nullptr) { myLogging->clear(); diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp new file mode 100644 index 00000000..10c9a3e1 --- /dev/null +++ b/src/SoftUART.cpp @@ -0,0 +1,27 @@ +#include "SoftUART.h" + +SoftwareSerial* myUART{ nullptr }; + +void uartInit() { + if (!myUART) { + myUART = new SoftwareSerial(4, 5); + } +} + +void uartHandle() { + static String incStr; + if (myUART->available()) { + char inc; + inc = myUART->read(); + incStr += inc; + if (inc == 0x0A) { + parse(incStr); + incStr = ""; + } + } +} + +void parse(String& incStr) { + + +} \ No newline at end of file diff --git a/src/items/SensorUltrasonicClass.cpp b/src/items/SensorUltrasonicClass.cpp index cd72a470..1d2fb965 100644 --- a/src/items/SensorUltrasonicClass.cpp +++ b/src/items/SensorUltrasonicClass.cpp @@ -1,20 +1,20 @@ -#include "BufferExecute.h" -#include "items/SensorUltrasonicClass.h" -//#ifdef SensorUltrasonicEnabled -//=========================================Модуль ультрозвукового дальномера================================================================== -//ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[1,100,1,100];c[1] -//========================================================================================================================================= -SensorUltrasonic mySensorUltrasonic; -void ultrasonicCm() { - mySensorUltrasonic.update(); - String key = mySensorUltrasonic.gkey(); - sCmd.addCommand(key.c_str(), ultrasonicReading); - mySensorUltrasonic.init(); - mySensorUltrasonic.clear(); -} - -void ultrasonicReading() { - String key = sCmd.order(); - mySensorUltrasonic.SensorUltrasonicRead(key); -} -//#endif \ No newline at end of file +//#include "BufferExecute.h" +//#include "items/SensorUltrasonicClass.h" +////#ifdef SensorUltrasonicEnabled +////=========================================Модуль ультрозвукового дальномера================================================================== +////ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[1,100,1,100];c[1] +////========================================================================================================================================= +//SensorUltrasonic mySensorUltrasonic; +//void ultrasonicCm() { +// mySensorUltrasonic.update(); +// String key = mySensorUltrasonic.gkey(); +// sCmd.addCommand(key.c_str(), ultrasonicReading); +// mySensorUltrasonic.init(); +// mySensorUltrasonic.clear(); +//} +// +//void ultrasonicReading() { +// String key = sCmd.order(); +// mySensorUltrasonic.SensorUltrasonicRead(key); +//} +////#endif \ No newline at end of file diff --git a/src/items/vSensorUltrasonic.cpp b/src/items/vSensorUltrasonic.cpp new file mode 100644 index 00000000..273424cf --- /dev/null +++ b/src/items/vSensorUltrasonic.cpp @@ -0,0 +1,89 @@ +#include "items/vSensorUltrasonic.h" +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" +#include + +GMedian<5, int> testFilter; + +SensorUltrasonic::SensorUltrasonic(String key, unsigned long interval, unsigned int trig, unsigned int echo, int map1, int map2, int map3, int map4, float c) { + _interval = interval * 1000; + _key = key; + _trig = trig; + _echo = echo; + + _map1 = map1; + _map2 = map2; + _map3 = map3; + _map4 = map4; + + _c = c; + + pinMode(_trig, OUTPUT); + pinMode(_echo, INPUT); +} + +SensorUltrasonic::~SensorUltrasonic() {} + +void SensorUltrasonic::loop() { + currentMillis = millis(); + difference = currentMillis - prevMillis; + if (difference >= _interval) { + prevMillis = millis(); + readUltrasonic(); + } +} + +void SensorUltrasonic::readUltrasonic() { + + static unsigned int counter; + counter++; + + int value; + + digitalWrite(_trig, LOW); + delayMicroseconds(2); + digitalWrite(_trig, HIGH); + delayMicroseconds(10); + digitalWrite(_trig, LOW); + long duration_ = pulseIn(_echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m + value = duration_ / 29 / 2; + + value = testFilter.filtered(value); + + value = map(value, _map1, _map2, _map3, _map4); + float valueFloat = value * _c; + + if (counter > 10) { + eventGen2(_key, String(valueFloat)); + jsonWriteStr(configLiveJson, _key, String(valueFloat)); + publishStatus(_key, String(valueFloat)); + SerialPrint("I", "Sensor", "'" + _key + "' data: " + String(valueFloat)); + } +} + +MySensorUltrasonicVector* mySensorUltrasonic = nullptr; + +void ultrasonic() { + myLineParsing.update(); + String interval = myLineParsing.gint(); + String pin = myLineParsing.gpin(); + String key = myLineParsing.gkey(); + String map = myLineParsing.gmap(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + unsigned int trig = selectFromMarkerToMarker(pin, ",", 0).toInt(); + unsigned int echo = selectFromMarkerToMarker(pin, ",", 1).toInt(); + + int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); + int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); + int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); + int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); + + static bool firstTime = true; + if (firstTime) mySensorUltrasonic = new MySensorUltrasonicVector(); + firstTime = false; + mySensorUltrasonic->push_back(SensorUltrasonic(key, interval.toInt(), trig, echo, map1, map2, map3, map4, c.toFloat())); +} + diff --git a/src/main.cpp b/src/main.cpp index 6acb6bba..1b146fce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,7 @@ #include "items/vImpulsOut.h" #include "items/vSensorDallas.h" #include "items/vCountDown.h" +#include "items/vSensorUltrasonic.h" #include "Telegram.h" void not_async_actions(); @@ -169,6 +170,11 @@ void loop() { mySensorDallas2->at(i).loop(); } } + if (mySensorUltrasonic != nullptr) { + for (unsigned int i = 0; i < mySensorUltrasonic->size(); i++) { + mySensorUltrasonic->at(i).loop(); + } + } if (myCountDown != nullptr) { for (unsigned int i = 0; i < myCountDown->size(); i++) { myCountDown->at(i).loop(); From 44369912058c6f0f8a950e5b49309e11a1f0b44d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 6 Dec 2020 00:40:48 +0300 Subject: [PATCH 24/94] esp32 uart --- src/SoftUART.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 10c9a3e1..1b6b0e7b 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -1,10 +1,20 @@ #include "SoftUART.h" -SoftwareSerial* myUART{ nullptr }; +#ifdef ESP8266 +SoftwareSerial* myUART = nullptr; +#else +HardwareSerial* myUART = nullptr; +#endif void uartInit() { if (!myUART) { +#ifdef ESP8266 myUART = new SoftwareSerial(4, 5); + myUART->begin(9600); +#else + myUART = new HardwareSerial(2); + myUART->begin(4, 5); +#endif } } @@ -23,5 +33,5 @@ void uartHandle() { void parse(String& incStr) { - + } \ No newline at end of file From 8dd4d8491c1797efdaa10cb3251060b370908c71 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 6 Dec 2020 00:59:47 +0300 Subject: [PATCH 25/94] UART in Progress --- data/set.utilities.json | 53 +++++++++++++++++++++++++++++++++++++++++ src/SoftUART.cpp | 6 ++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/data/set.utilities.json b/data/set.utilities.json index 9314cd8b..c5d7ebc8 100644 --- a/data/set.utilities.json +++ b/data/set.utilities.json @@ -34,6 +34,59 @@ "title": "Сканировать", "action": "/set?i2c", "class": "btn btn-block btn-default" + }, + { + "type": "hr" + }, + { + "type": "h3", + "title": "UART" + }, + { + "type": "checkbox", + "name": "uart", + "title": "Включить UART", + "action": "/set?uart=[[uart]]", + "state": "{{uart}}" + }, + { + "type": "hr" + }, + { + "type": "h4", + "title": "Скорость", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "uartS-arg", + "state": "{{uartS}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "Пин TX", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "uartTX-arg", + "state": "{{uartTX}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "Пин RX", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "uartTX-arg", + "state": "{{uartRX}}", + "style": "width:40%;float:right" } ] } \ No newline at end of file diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 1b6b0e7b..bf399adb 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -24,9 +24,9 @@ void uartHandle() { char inc; inc = myUART->read(); incStr += inc; - if (inc == 0x0A) { - parse(incStr); - incStr = ""; + if (inc == '\n') { + parse(incStr); + incStr = ""; } } } From 6e44f763859177c6083251c719a81c9b19c26ff9 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 6 Dec 2020 02:08:37 +0300 Subject: [PATCH 26/94] UART working condition --- data/config.json | 6 +++++- data/set.utilities.json | 9 ++++++++- src/SoftUART.cpp | 25 +++++++++++++++++++------ src/Web.cpp | 26 ++++++++++++++++++++++++++ src/main.cpp | 6 ++++++ 5 files changed, 64 insertions(+), 8 deletions(-) diff --git a/data/config.json b/data/config.json index eec796ae..ccae42a2 100644 --- a/data/config.json +++ b/data/config.json @@ -22,5 +22,9 @@ "snaMqtt": "0", "blink": "1", "oneWirePin": "2", - "serverip": "http://206.189.49.244" + "serverip": "http://206.189.49.244", + "uart": "0", + "uartS":"9600", + "uartTX":"12", + "uartRX":"13" } \ No newline at end of file diff --git a/data/set.utilities.json b/data/set.utilities.json index c5d7ebc8..dee60f7c 100644 --- a/data/set.utilities.json +++ b/data/set.utilities.json @@ -84,9 +84,16 @@ { "type": "input", "title": "", - "name": "uartTX-arg", + "name": "uartRX-arg", "state": "{{uartRX}}", "style": "width:40%;float:right" + }, + { + "type": "button", + "title": "{{ButSave}}", + "style": "width:100%;float:left;", + "action": "set?uartS=[[uartS-arg]]&uartTX=[[uartTX-arg]]&uartRX=[[uartRX-arg]]", + "class": "btn btn-block btn-default" } ] } \ No newline at end of file diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index bf399adb..d142e26e 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -1,4 +1,5 @@ #include "SoftUART.h" +#include "Global.h" #ifdef ESP8266 SoftwareSerial* myUART = nullptr; @@ -7,10 +8,13 @@ HardwareSerial* myUART = nullptr; #endif void uartInit() { + if (!jsonReadBool(configSetupJson, "uart")) { + return; + } if (!myUART) { #ifdef ESP8266 - myUART = new SoftwareSerial(4, 5); - myUART->begin(9600); + myUART = new SoftwareSerial(jsonReadInt(configSetupJson, "uartTX"), jsonReadInt(configSetupJson, "uartRX")); + myUART->begin(jsonReadInt(configSetupJson, "uartS")); #else myUART = new HardwareSerial(2); myUART->begin(4, 5); @@ -19,19 +23,28 @@ void uartInit() { } void uartHandle() { + if (!jsonReadBool(configSetupJson, "uart")) { + return; + } static String incStr; if (myUART->available()) { char inc; inc = myUART->read(); incStr += inc; if (inc == '\n') { - parse(incStr); - incStr = ""; + parse(incStr); + incStr = ""; } } } void parse(String& incStr) { - - + if (incStr.indexOf("set") != -1) { + incStr.replace("\r\n", ""); + incStr.replace("\r", ""); + incStr.replace("\n", ""); + incStr = deleteBeforeDelimiter(incStr, " "); + SerialPrint("I", "UART", incStr); + orderBuf += incStr; + } } \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index c571ccaf..ead2e617 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -6,6 +6,7 @@ #include "items/vLogging.h" #include "Telegram.h" #include "RemoteOrdersUdp.h" +#include "SoftUART.h" bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { if (request->hasArg("preset")) { @@ -255,6 +256,31 @@ void web_init() { myNotAsyncActions->make(do_BUSSCAN); request->redirect("/?set.utilities"); } + if (request->hasArg("uart")) { + bool value = request->getParam("uart")->value().toInt(); + jsonWriteBool(configSetupJson, "uart", value); + saveConfig(); + uartInit(); + request->send(200); + } + if (request->hasArg("uartS")) { + jsonWriteStr(configSetupJson, "uartS", request->getParam("uartS")->value()); + saveConfig(); + uartInit(); + request->send(200); + } + if (request->hasArg("uartTX")) { + jsonWriteStr(configSetupJson, "uartTX", request->getParam("uartTX")->value()); + saveConfig(); + uartInit(); + request->send(200); + } + if (request->hasArg("uartRX")) { + jsonWriteStr(configSetupJson, "uartRX", request->getParam("uartRX")->value()); + saveConfig(); + uartInit(); + request->send(200); + } //==============================developer settings============================================= if (request->hasArg("serverip")) { diff --git a/src/main.cpp b/src/main.cpp index 1b146fce..34c19945 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "items/vCountDown.h" #include "items/vSensorUltrasonic.h" #include "Telegram.h" +#include "SoftUART.h" void not_async_actions(); @@ -91,6 +92,9 @@ void setup() { SerialPrint("I", F("Bus"), F("Bus Init")); busInit(); + SerialPrint("I", F("UART"), F("UART Init")); + uartInit(); + #ifdef SSDP_ENABLED SerialPrint("I", F("SSDP"), F("Ssdp Init")); SsdpInit(); @@ -155,6 +159,8 @@ void loop() { handleTelegram(); + uartHandle(); + if (myLogging != nullptr) { for (unsigned int i = 0; i < myLogging->size(); i++) { myLogging->at(i).loop(); From 6205f6959cc4429129f115a1da3f99e9284e28d1 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 9 Dec 2020 04:08:36 +0300 Subject: [PATCH 27/94] Gisteresis termostat --- data/config.json | 4 +- data/presets/{dal.c.txt => 1.c.txt} | 0 data/presets/{dal.s.txt => 1.s.txt} | 0 data/presets/2.c.txt | 12 + data/presets/2.s.txt | 18 + data/presets/{dht.c.txt => 3.c.txt} | 0 data/presets/{dht.s.txt => 3.s.txt} | 0 data/presets/{rel.c.txt => 4.c.txt} | 0 data/presets/{rel.s.txt => 4.s.txt} | 0 data/presets/{alloff.c.txt => 5.c.txt} | 0 data/presets/{alloff.s.txt => 5.s.txt} | 0 data/set.device.json | 13 +- data/widgets/chart2.json | 4 + data/widgets/inputDigitTemp.json | 6 + data/widgets/inputTimeClock.json | 6 + doc/1.txt | 566 ------------------------- doc/2.txt | 85 ---- doc/3.txt | 32 -- doc/eagle.txt | 24 ++ include/Class/ScenarioClass3.h | 23 +- include/Consts.h | 12 +- include/Global.h | 8 +- include/items/vLogging.h | 4 +- platformio.ini | 6 +- src/Global.cpp | 8 +- src/Init.cpp | 3 +- src/ItemsList.cpp | 78 +++- src/UpgradeFirm.cpp | 8 +- src/Web.cpp | 18 +- src/items/vInOutput.cpp | 7 + src/items/vLogging.cpp | 53 ++- 31 files changed, 257 insertions(+), 741 deletions(-) rename data/presets/{dal.c.txt => 1.c.txt} (100%) rename data/presets/{dal.s.txt => 1.s.txt} (100%) create mode 100644 data/presets/2.c.txt create mode 100644 data/presets/2.s.txt rename data/presets/{dht.c.txt => 3.c.txt} (100%) rename data/presets/{dht.s.txt => 3.s.txt} (100%) rename data/presets/{rel.c.txt => 4.c.txt} (100%) rename data/presets/{rel.s.txt => 4.s.txt} (100%) rename data/presets/{alloff.c.txt => 5.c.txt} (100%) rename data/presets/{alloff.s.txt => 5.s.txt} (100%) create mode 100644 data/widgets/chart2.json create mode 100644 data/widgets/inputDigitTemp.json create mode 100644 data/widgets/inputTimeClock.json delete mode 100644 doc/1.txt delete mode 100644 doc/2.txt delete mode 100644 doc/3.txt create mode 100644 doc/eagle.txt diff --git a/data/config.json b/data/config.json index ccae42a2..10d2fcc6 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "VOLODYA", - "routerpass": "BELCHENKO", + "routerssid": "rise", + "routerpass": "hostel3333", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "wqtt.ru", diff --git a/data/presets/dal.c.txt b/data/presets/1.c.txt similarity index 100% rename from data/presets/dal.c.txt rename to data/presets/1.c.txt diff --git a/data/presets/dal.s.txt b/data/presets/1.s.txt similarity index 100% rename from data/presets/dal.s.txt rename to data/presets/1.s.txt diff --git a/data/presets/2.c.txt b/data/presets/2.c.txt new file mode 100644 index 00000000..0d7e277e --- /dev/null +++ b/data/presets/2.c.txt @@ -0,0 +1,12 @@ +0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] +0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] +0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 +0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] +0;inoutput;time1;inputTimeClock;Расписание;Утренний#период;8 +0;inoutput;threshold1;inputDigitTemp;Расписание;Температура;9 +0;inoutput;time2;inputTimeClock;Расписание;Дневной#период;10 +0;inoutput;threshold2;inputDigitTemp;Расписание;Температура;11 +0;inoutput;time3;inputTimeClock;Расписание;Вечерний#период;12 +0;inoutput;threshold3;inputDigitTemp;Расписание;Температура;13 +0;inoutput;time4;inputTimeClock;Расписание;Ночной#период;14 +0;inoutput;threshold4;inputDigitTemp;Расписание;Температура;15 \ No newline at end of file diff --git a/data/presets/2.s.txt b/data/presets/2.s.txt new file mode 100644 index 00000000..5a6f36f1 --- /dev/null +++ b/data/presets/2.s.txt @@ -0,0 +1,18 @@ +temp > threshold+-2 +heater 0 +end +temp < threshold+-2 +heater 1 +end +timenow = time1 +threshold threshold1 +end +timenow = time2 +threshold threshold2 +end +timenow = time3 +threshold threshold3 +end +timenow = time4 +threshold threshold4 +end \ No newline at end of file diff --git a/data/presets/dht.c.txt b/data/presets/3.c.txt similarity index 100% rename from data/presets/dht.c.txt rename to data/presets/3.c.txt diff --git a/data/presets/dht.s.txt b/data/presets/3.s.txt similarity index 100% rename from data/presets/dht.s.txt rename to data/presets/3.s.txt diff --git a/data/presets/rel.c.txt b/data/presets/4.c.txt similarity index 100% rename from data/presets/rel.c.txt rename to data/presets/4.c.txt diff --git a/data/presets/rel.s.txt b/data/presets/4.s.txt similarity index 100% rename from data/presets/rel.s.txt rename to data/presets/4.s.txt diff --git a/data/presets/alloff.c.txt b/data/presets/5.c.txt similarity index 100% rename from data/presets/alloff.c.txt rename to data/presets/5.c.txt diff --git a/data/presets/alloff.s.txt b/data/presets/5.s.txt similarity index 100% rename from data/presets/alloff.s.txt rename to data/presets/5.s.txt diff --git a/data/set.device.json b/data/set.device.json index 3f4cd654..67052c1f 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -72,8 +72,8 @@ "#": "Выберите элемент из списка", "/set?addItem=button-out.pin": "1.Кнопка управляющая пином", "/set?addItem=button-out.inv": "2.Кнопка управляющая пином (с инверсией)", - "/set?addItem=button-out.npin": "3.Кнопка виртуальная", - "/set?addItem=button-in": "4.Кнопка физическая", + "/set?addItem=button-out.npin": "3.Кнопка виртуальная (не привязанная к пину)", + "/set?addItem=button-in": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", "/set?addItem=pwm-out": "3.Широтно импульсная модуляция pwm", "/set?addItem=input-digit": "5.Окно ввода цифровых значений", "/set?addItem=input-time": "6.Окно ввода времени", @@ -104,10 +104,11 @@ "style": "display:inline", "title": { "#": "Выберите пресет из списка", - "/set?addPreset=dal.c": "1.Термостат на основе ds18b20 с оповещением в телеграм", - "/set?addPreset=dht.c": "2.Контроль влажности на основе DHT с оповещением в телеграм", - "/set?addPreset=rel.c": "3.Включение выключение реле в заданное время", - "/set?addPreset=alloff.c": "4.Выключить все (пример работы сценариев)" + "/set?addPreset=1.c": "1.Термостат на основе ds18b20 с оповещением в телеграм", + "/set?addPreset=2.c": "2.Гистерезис термостат на основе ds18b20 с суточным расписанием", + "/set?addPreset=3.c": "3.Контроль влажности на основе DHT с оповещением в телеграм", + "/set?addPreset=4.c": "4.Включение выключение реле в заданное время", + "/set?addPreset=5.c": "5.Выключить все (пример работы сценариев)" } }, { diff --git a/data/widgets/chart2.json b/data/widgets/chart2.json new file mode 100644 index 00000000..9ecc61e3 --- /dev/null +++ b/data/widgets/chart2.json @@ -0,0 +1,4 @@ +{ + "widget": "chart", + "dateFormat": "HH:mm" +} \ No newline at end of file diff --git a/data/widgets/inputDigitTemp.json b/data/widgets/inputDigitTemp.json new file mode 100644 index 00000000..0ed891e1 --- /dev/null +++ b/data/widgets/inputDigitTemp.json @@ -0,0 +1,6 @@ +{ + "widget" : "input", + "color" : "green", + "type" : "number", + "icon": "thermometer" +} \ No newline at end of file diff --git a/data/widgets/inputTimeClock.json b/data/widgets/inputTimeClock.json new file mode 100644 index 00000000..695f6bd1 --- /dev/null +++ b/data/widgets/inputTimeClock.json @@ -0,0 +1,6 @@ +{ + "widget" : "input", + "color" : "orange", + "type" : "time", + "icon": "alarm-outline" +} \ No newline at end of file diff --git a/doc/1.txt b/doc/1.txt deleted file mode 100644 index d08be432..00000000 --- a/doc/1.txt +++ /dev/null @@ -1,566 +0,0 @@ -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -## Возможности - - - Объединение различных по типу и назначению устройств: управление, получение данных, и настройка параметров - всё в одном приложении - - - Взаимодействие с устройствами осуществляется через "облачный" сервис с использованием протокола mqtt, позволит контролировать их из любой точки Мира (при наличии доступа в Интернет) - - - Поддержка нескольких профилей и их переключение "на лету", дает возможность объединить устройства в группы - - -Настройка (после "прошивки") производится через веб-интерфейс, чтобы получить к нему доступ необходимо соединиться с WiFi AP устройства и набрать в адресной строке браузера http://192.168.4.1. -Далее выбрать типовой шаблон автоматизации, произвести настройку под свои требования и задачи. -Основные разделы интерфейса: конфигурация и сценарии. -В окне конфигурации задаются "объекты", "элементы управления" устройства (dashboard) - им устройство будет представлено в приложении компаньоне проекта. В окне сценариев задаются реакции на события и изменения в параметрах работы системы. - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -## Команды, назначение и применение - -Команды служат для настройки и управления устройством и его взаимодействия - -**`buttonSet 1 1`** Изменит состояние "кнопки" №1, установит его в значение 1 - -**`pinSet 13 0`** Установит GPIO 13 состояние 0 - -**`pinChange 13`** Состояние GPIO 13 будет изменено на противоположное - -**`pwmSet 1 500`** Настройка pwm №1 будет использовано значение 500 - -**`timeSet 1 08-00-00`** Установит для элемента ввода времени - inputTime значение 08:00:00 - -**`digitSet 1 56`** Элемент №1 (для цифровых параметров) отобразит число 56 - -**`stepperSet 1 100 1`** Шаговый двигатель №1 - вращение 100 "шагов" по часовой стрелке (для движения в обратную сторону используются отрицательные значения параметра) - -**`servoSet 1 180`** Сервопривод №1 принять положение 180° - -**`timerStart 1 60 sec`** Установить для таймера №1 обратный отсчёт в 60 секунд - -**`timerStop 1`** Остановить таймер №1 - -**`textSet 1 Привет`** Установить для элемента текстовое поле №1 - "привет" - -**`push Внимание Протечка`** Отправить push-уведомление с темой "внимание" и содержанием "протечка" - -**`firmwareUpdate`** Обновить прошивку устройства "по воздуху" - -**`firmwareVersion Версия прошивки Системные 1`** Узнать версию прошивки устройстве - -## Сценарии - -Элементарный блок в сценарии состоит из набора команд и триггера - условия для их выполнения - -**temp > 60** -digitSet 1 60 -stepperSet 1 100 1 -textSet 1 Перегрев -**end** - -Условие: когда температура превысит 60° -Запустит: команда шаговому двигателю, в приложение отправить сообщение и цифровое значение температуры. - -В сценарии может быть несколько блоков, при необходимости из приложения есть возможность "выключать" часть из них. -Неактивные блоки сценария будут проигнорированы. - -Для взаимодействия устройств между собой предусмотрены команды mqtt и http - -**temp > 60** -mqtt 154348-134 digitSet_1_56 -mqtt 154348-136 stepperSet _1_100_1 -http 192.168.1.10 textSet_1_Перегрев -**end** - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 1.1 Объект "кнопка" - -(эти строки мы пишем в "конфигурации устройства") - -### a) кнопка управляющая выходом (пином). Пины нумеруются по системе нумирации gpio для esp контроллеров. - -`button 1 13 кухня освещение 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"13"** это номер пина которым будет управлять данная кнопка -**"кухня"** это название кнопки в приложении -**"освещение"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - - -### б) виртуальная кнопка - кнопка реакцию на которую можно задать в сценариях: - -`button 1 na запустить таймеры 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"na"** абривиатура not available означающая что эта кнопка виртуальная и что пин не установлен -**"запустить"** это название кнопки в приложении -**"таймеры"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -### в) кнопка включающая и выключающая все сценарии: - -`button 1 scenario запустить таймеры 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"scenario"** слово означающее что эта кнопка для управления сценариями -**"запустить"** это название кнопки в приложении -**"таймеры"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - - -### г) кнопка включающая выключающая определенные блоки сценариев: - -`button 1 line1,line3, Включить#отправку#push Оповещение 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"line1,line3,"** это блоки сценариев нумирация сверху вниз. Блоком считается выражение от начала до слова end -**"Включить#отправку#push"** это название кнопки в приложении -**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -## 1.2 Команды управления объектом "кнопка" - -(эти строки мы пишем в "сценариях") - - ### а) Команда включения выключения кнопки по ее номеру - -`buttonSet 1 1` - -**"buttonSet"** команда управления объектом button -**"1"** номер кнопки которой будем управлять -**"1"** состояние включено, 0 - выключено - -### б) Команда изменения состояния кнопки на противоположное - -`buttonChange 1` - -**"buttonChange"** команда управления объектом button -**"1"** номер кнопки которой будем управлять - -## 1.3 Вызов событий объектом "кнопока" - -(эти строки мы пишем в "сценариях") - -объект button может быть равен либо 0 либо 1 - -`button1 = 1` -`button2 = 0` - -Пример использования: - -`button1 = 1` -`buttonSet 2 1` -`buttonSet 3 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 2.1 Объект "физическая кнопка" - -`switch 1 0 10` - -**switch** это объект создающий физическую кнопку -**1** номер кнопки -**0** пин кнопки (при подключении необходим подтягивающий резистор) -**10** задержка для избавления от дребезга с мили секундах - -## 2.2 Вызов событий объектом "физическая кнопка" - -`switch1` может быть равна нулю или единицы, ноль - событие отбрасывания кнопки, единица - событие нажатия - -`switch1 = 1` -`buttonChange 1` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 3.1 Объект "широтноимпульсная модуляция" - -`pwm 1 12 яркость освещение 1023 1` - -**"pwm"** это объект создающий управление шим в приложении в виде ползунка -**"1"** это номер этого объекта -**"12"** это номер пина на котором будет генерироваться шим заданной в приложении величены -**"Яркость"** это название кнопки в приложении -**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка -**"1023"** это начальное значение шим сигнала и ползунка (изменяется от 0 до 1023) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -## 3.2 Команда управления объектом "широтноимпульсная модуляция" - -`pwmSet 1 500` - -**"pwmSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять -**"500"** значение которое установится после выполнения команды (от 0 до 1023) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 4.1 Объект "окно ввода времени" - -`inputTime time1 Во#сколько#включить? Таймеры 20-30-00 1` - -**inputTime** это объект создающий окно ввода в приложении -**time1** переменная в которую будет записано время введенное в окно -**Во#сколько#включить?** это название окна в приложении -**Таймеры** это название вкладки в приложении -**20-30-00** начальное значение времени после загрузки устройства -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 4.2 Управление объектом "окно ввода времени" - -`timeSet 1 08-00-00` - -**"timeSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `time1` -**"08-00-00"** время которое хотим установить - -В окно ввода можно вводить время в приложении но если необходимо изменить время автоматически -по какому нибудь событию то можно использовать команду выше - **timeSet**. - -## 4.3 Вызов событий объектом "окно ввода времени" - -`timenow = time1` -`buttonSet 1 1` -`end` - -`timenow` всегда хранит в себе текущее время, и поэтому исходя из данного сценария кнопка номер 1 включится в то время которое будет введено в окно ввода `time1` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 5.1 Объект "окно ввода цифры" - -`inputDigit digit1 Через#сколько#секунд#выключить? Таймеры 5 2` - -**inputDigit** это объект создающий окно ввода в приложении -**digit1** переменная в которую будет записана цифра, введенная в окно -**Через#сколько#секунд#выключить?** это название окна в приложении -**Таймеры** это название вкладки в приложении -**5** цифра по умолчанию, после загрузки модуля -**2** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 5.2 Управление объектом "окно ввода цифры": - -`digitSet 1 56` - -**"digitSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `digit1` -**"56"** цифра которую хотим установить - -В окно ввода можно вводить цифры в приложении, но если необходимо изменить цифру автоматически -по какому нибудь событию, то можно использовать команду выше - **digitSet**. - -## 5.3 Вызов событий объектом "окно ввода цифры" - -`dallas > digit1` -`buttonSet 1 0` -`end` - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 6.1 Объект "dallas" (сенсор температуры ds18b20) - -`dallas temp1 2 123456 Водонагреватель,#t°C Термостат any-data 1` - -**dallas** это объект чтения датчика температуры -**2** пин датчика температуры -**Водонагреватель,#t°C** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 6.2 Вызов событий объектом "dallas" - -В сценариях dallas можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`dallas > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`dallas > 60` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 7.1 Объект "analog" (аналоговый вход контроллера) - -`analog adc 0 Аналоговый#вход,#% Датчики progress-round 310 620 1 100 1` - -**analog** это объект чтения аналогового входа -**adc** это переменная -**0** пин аналогового входа (для esp8266 всегда 0, для esp32 пока что не доделал читаться будет всегда пин 34) -**Аналоговый#вход,#%** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения -**310** начальная величина читаемого диапазона -**620** конечная величина читаемого диапазона -**1** начальная величина выводимого диапазона -**100** конечная величина выводимого диапазона -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 7.2 Вызов событий объектом "analog" - -В сценариях analog можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`analog > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`analog > 50` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 8.1 Объект "level" (ультразвуковой дальномер JSN-SR04T, HC-SR04, HY-SRF05) - -`level Вода#в#баке,#% Датчики any-data 125 20 1` - -**level** это объект чтения датчика расстояния -**Вода#в#баке** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета -**125** расстояние от датчика до дна бака в сантиметрах -**20** расстояние от датчика до поверхности воды, когда бак полный, в сантиметрах -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -Подключать дальномер нужно: - -| | trig | echo | -| :-: | :-: | :-: | -| wemos | D5 | D6 | -| esp | 14 | 12 | - -## 8.2 Вызов событий объектом "level" - -В сценариях level можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`level > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`level > 95` -`buttonSet 1 0` -`end` - - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 9.1 Объект "dht" (Сенсоры DHT11, DHT22, DHT33, DHT44, AM2302, RHT03) - -dhtT DHT11 2 Температура#DHT,#t°C Датчики any-data 1 -dhtH DHT11 2 Влажность#DHT,#% Датчики any-data 2 -dhtComfort Степень#комфорта: Датчики 3 -dhtPerception Восприятие: Датчики 4 -dhtDewpoint Точка#росы: Датчики 5 - -**dhtT** или **dhtH** температура или влажность -**DHT11** или **DHT22** чтение DHT11 или DHT22, DHT33, DHT44, AM2302, RHT03 соответственно -**2** пин датчика -**Температура#DHT,#t°C** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 9.2 Вызов событий объектом "dhtT" или "dhtH" - -В сценариях "dhtT" или "dhtH" можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`dhtT > digit1` -`buttonSet 1 0` -`end` - -`dhtH > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`dhtT > 50` -`buttonSet 1 0` -`end` - -`dhtH < 40` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 10.1 Объект "stepper" (Драйвер шагового двигателя A4988) - -stepper 1 12 4 -stepper 2 13 5 - -**stepper** объект создающий шаговый двигатель -**1** номер шаговика -**12** номер пина количества шагов -**4** номер пина направления - -## 10.2 управление объектом "stepper" - -`stepperSet 1 200 1` - -**stepperSet** команда управления шаговым двигателем -**1** номер шагового двигателя (их может быть не более двух) -**200** количество шагов (обратное направление отрицательное значение параметра) -**1** интервал между шагами (мс) - -`button1 = 1` -`stepperSet 1 200 1` -`end` -`button1 = 0` -`stepperSet 1 -200 1` -`end` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 11.1 Объект "обратный таймер" - -Прежде чем читать этот раздел запустите пресет №3 на устройстве. -Нажав на кнопку "Выберите во что вы хотите превратить esp" - -Можно использовать цифры из окон ввода: - -`timerStart 1 digit1 sec` - -Можно писать цифры прям в объект: - -`timerStart 1 10 sec` - -Можно установить часы минуты или секунды: - -`timerStart 1 10 sec` -`timerStart 1 10 min` -`timerStart 1 10 hours` - -Используем это объект в сценариях вот так: - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` - -Смысл в том что при нажатии на кнопку один запуститься обратный отчет, на величину digit1 секунд. Если напишем например: - -`dallas < 60` -`timerStart 1 digit1 sec` -`end` - -то такой же отчет запустится когда значение температуры вырастит больше 60 градусов. Таким образом обратный отчет можно запустить реакцией на любое событие. Итак теперь обратный отчет запущен, обратный таймер уменьшается, и нам надо назначить действие на тот момент когда он обнулится. Для этого я придумал выражение: `timer1 = 0` - -Используем его и в общем получаем вот такой сценарий: - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` -`timer1 = 0` -`buttonSet 1 0` -`end` - -Когда таймер закончит отсчёт, кнопка станет "неактивной". Используйте преет №3, как пример подобного сценария -Например: - -`dallas < 60` -`buttonSet 1 0` -`buttonSet 2 0` -`pwmSet 1 1023` -`mqtt 2653450020 buttonChange_1` -`mqtt 2653450020 pinSet_13_1` -`http 192.168.1.32 pinSet_14_1` -`end` - -Вот что может произойти на разных устройствах по одному событию повышения температуры... - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 12 Журнал (лог) данных - - -`logging analog 1 100 slow Аналоговый#вход Датчики 7` - -**logging** объект для логирования -**analog** или **dhtT** или **dhtH** какой сенсор будем логировать, можно указать любой -**1** период между точками в минутах -**100** количество точек (старые точки будут удаляться по мере добавления новых) -**slow** или **fast** метод выгрузки графика в приложение, slow - выгружает график по одной точке (меньше расходуется оперативка, лучше использовать для esp8266), fast - выгрузка графика сразу (больше расход оперативки, подходит для esp32) -**Аналоговый#вход** название графика в приложении -**Датчики** название вкладки в приложении -**7** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 13 Взаимодействие устройств между собой - -Устройства могут между собой обмениваться командами. Команды можно отправлять по http или по mqtt. -По событию на одном устройстве можно вызвать действие на другом. Например на esp01 стоит датчик температуры, реле стоит на esp02. - -Настройки esp01: - -`dhtT temp 2 dht11 Температура#DHT,#t°C Датчики any-data 1` - -`temp < 40` -`http 192.168.10.25 buttonSet_1_1` -`end` - -Настройки esp02: - -`button 1 13 Включить#реле Реле 0 1` - -И теперь когда температура датчика на esp01 станет меньше 40 градусов то на esp02 будет отправлена команда на включение кнопки: buttonSet_1_1 - -Если вы хотите отправить команду через mqtt то сценарий будет выглядеть следующим образом: - -`temp < 40` -`mqtt 12343442-12413131 buttonSet_1_1` -`end` - -где `12343442-12413131` id esp02 той на которую отправляем команду. Id можно взять в веб интерфейсе на странице конфигурация устройства. Или в списке устройств в сети. - -Теперь рассмотрим вариант внешнего управления esp с помощью get запросов. - -`http://192.168.88.239/cmd?command=buttonSet%201%201` - -Разберем эту строку. Сама команда в ней выглядит вот так: buttonSet%201%201. `%20` заменяют пробел. - -То есть что бы составить get запрос на изменение например pwm нужно: - -Взять команду `pwmSet 1 500` -Заменить в ней пробелы на `%20` получится так: `pwmSet%201%20500` -И добавить ее в конец строки `http://192.168.88.239/cmd?command=` где указывается ip адрес устройства - -В итоге получится http://192.168.88.239/cmd?command=pwmSet%201%20500 - - - - - - \ No newline at end of file diff --git a/doc/2.txt b/doc/2.txt deleted file mode 100644 index 529f22bb..00000000 --- a/doc/2.txt +++ /dev/null @@ -1,85 +0,0 @@ -# В этой инструкции будет описано как с esp отправлять email и push - -# Часть 1. Привязать email и pushbullet к сайту pushingbox - -### 1. Необходимо перейти на сайт: [pushingbox](https://www.pushingbox.com/) -### 2. Войти с помощью google -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_1.png) -### 3. Перейти в мои сервисы и добавить новый сервис -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_2.png) -### 4. Нас интересуют два сервиса email и pushbullet -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_4%2B.png) -### 5. Выбираем сначало сервис для отправки email. В окно `Name of your email configuration` - вводим слово "email". В окно `Email address` - вводим ваш email адрес. жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_5.png) -manager_modules_firmware/blob/master/push_instruction/Screenshot_6.png) -### 6.1 Привязываем pushbullet. Переходим на сайт [pushbullet.com](https://www.pushbullet.com/) -### 6.2 Входим с гуглом или фейсбуком -### 6.3 Идем в настройки -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_7.png) -### 6.4 Создаем токен -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_8.png) -### 6.5 Идем опять в сервисы и теперь выбираем сервис pushbullet [pushingbox.com/services](https://www.pushingbox.com/services.php) нажимаем add service -### Берем токен, и вставляем его в окно Access token. -### Окно Device token (optional) оставляем пустым. -### В окно Name of your Pushbullet configuration пишем слово "push". -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_6.png) - -### 7. Теперь наш email и pushbullet привязаны к pushingbox. Далее можно скачать приложение pushbullet на телефон и войти с гуглом или фейсбуком сответственно с пунктом 6.3 этой инструкции -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_9.png) - -# Часть 2. Создание сценариев отправки email - -### 8.1. Сценарий для отправки email. Заходим в My Scenarios: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_10.png) - -### 8.2 Пишем слово email (это имя сценария отправки email) жмем add: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_12.png) -### 8.3 Нажимаем add an action -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_14.png) -### 8.4 Выбираем наш email который мы зарегестрировали ранее и нажимаем Add an action with this service -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_11.png) -### 8.5 Делаем все как на скриншоте и жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_15.png) -### 8.6 Возвращаемся на мои сценарии -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_17.png) -### 8.7 Вставляем токен в веб интерфейс esp -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_18.png) - -# Часть 3. Создание сценариев отправки push - -### 9.1. Сценарий для отправки push. Заходим в My Scenarios: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_10.png) - -### 9.2 Пишем слово push (это имя сценария отправки email) жмем add: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_19.png) -### 9.3 Нажимаем add an action -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_20.png) -### 9.4 Выбираем наш pushbullet который мы зарегестрировали ранее и нажимаем Add an action with this service -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_11.png) -### 9.5 Делаем все как на скриншоте и жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_15.png) -### 9.6 Возвращаемся на мои сценарии -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_21.png) -### 9.7 Вставляем токен в веб интерфейс esp -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_18.png) - -# Часть 4. Итог - -При создании такой конфигурации как на картинке: - -`button 1 na Отправить#push Push 0 1` - - -`button1 = 1` -`push внимание кнопка#нажата` -`end` - -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_22.png) - -Если мы введем токен для email то будут приходить email -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_17.png) - -Если для push то будут приходить push в pushbullet -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_21.png) - -Способ описанный в данной инструкции более сложный в настройке но зато очень надежный. \ No newline at end of file diff --git a/doc/3.txt b/doc/3.txt deleted file mode 100644 index 31226847..00000000 --- a/doc/3.txt +++ /dev/null @@ -1,32 +0,0 @@ -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 1. Скачать архив из [релизов](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/releases) или из закрепленного сообщения группы телеграм с последней версией прошивки - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - - -### 2. Для ESP8266 c 4 и больше мб памяти (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_2.png) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 2. Для ESP8266 c 1 мб памяти (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1mb_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1mb_2.png) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 2. Для ESP32 (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp32_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp32_2.png) \ No newline at end of file diff --git a/doc/eagle.txt b/doc/eagle.txt new file mode 100644 index 00000000..8a590610 --- /dev/null +++ b/doc/eagle.txt @@ -0,0 +1,24 @@ +eagle.flash.512k0.ld 512K (no SPIFFS) +eagle.flash.512k64.ld 512K (64K SPIFFS) +eagle.flash.512k128.ld 512K (128K SPIFFS) +eagle.flash.1m0.ld 1M (no SPIFFS) +eagle.flash.1m64.ld 1M (64K SPIFFS) +eagle.flash.1m128.ld 1M (128K SPIFFS) +eagle.flash.1m144.ld 1M (144K SPIFFS) +eagle.flash.1m160.ld 1M (160K SPIFFS) +eagle.flash.1m192.ld 1M (192K SPIFFS) +eagle.flash.1m256.ld 1M (256K SPIFFS) +eagle.flash.1m512.ld 1M (512K SPIFFS) +eagle.flash.2m.ld 2M (1M SPIFFS) +eagle.flash.4m1m.ld 4M (1M SPIFFS) +eagle.flash.4m2m.ld 4M (2M SPIFFS) +eagle.flash.4m.ld 4M (3M SPIFFS) +eagle.flash.8m.ld 8M (7M SPIFFS) +eagle.flash.16m.ld 16M (15M SPIFFS) + +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1F0000, +app1, app, ota_1, 0x200000, 0x1F0000, +spiffs, data, spiffs, 0x3F0000,0x10000, \ No newline at end of file diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index 0d2eba1e..cc8b0b5b 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -30,7 +30,28 @@ public: String setEventSign = selectFromMarkerToMarker(condition, " ", 1); String setEventValue = selectFromMarkerToMarker(condition, " ", 2); - if (!isDigitStr(setEventValue)) setEventValue = getValue(setEventValue); //jsonReadStr(configLiveJson , setEventValue); + + if (!isDigitStr(setEventValue)) { + if (setEventValue.indexOf("+-") != -1) { + String setEventValueName = selectToMarker(setEventValue, "+-"); + String gisteresisValue = selectToMarkerLast(setEventValue, "+-"); + gisteresisValue.replace("+-", ""); + String value = getValue(setEventValueName); + String upValue = String(value.toFloat() + gisteresisValue.toFloat()); + String lowValue = String(value.toFloat() - gisteresisValue.toFloat()); + + if (setEventSign == ">") { + setEventValue = upValue; + } + else if (setEventSign == "<") { + setEventValue = lowValue; + + } + } + else { + setEventValue = getValue(setEventValue); + } + } boolean flag = false; diff --git a/include/Consts.h b/include/Consts.h index 5f679e8c..7fb57c28 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,15 +1,14 @@ #pragma once //===========Firmware============================================================================================================================================= +#define FIRMWARE_VERSION 268 +//#define FLASH_SIZE_1MB #ifdef ESP8266 #define FIRMWARE_NAME "esp8266-iotm" -#define FIRMWARE_VERSION 268 #endif #ifdef ESP32 #define FIRMWARE_NAME "esp32-iotm" -#define FIRMWARE_VERSION 268 #endif -#define FLASH_4MB true //===========FSystem============================================================================================================================================== #define NUM_BUTTONS 6 @@ -86,6 +85,8 @@ enum NotAsyncActions { do_MQTTPARAMSCHANGED, do_deviceInit, do_delChoosingItems, + do_addItem, + do_addPreset, do_sendScenUDP, do_sendScenMQTT, do_LAST, @@ -117,4 +118,7 @@ enum ConfigType_t { //17.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 45.7% (used 37476 bytes from 81920 bytes) -//Flash: [===== ] 54.5% (used 569296 bytes from 1044464 bytes) \ No newline at end of file +//Flash: [===== ] 54.5% (used 569296 bytes from 1044464 bytes) + +//RAM: [===== ] 45.6% (used 37336 bytes from 81920 bytes) +//Flash: [====== ] 55.3% (used 577396 bytes from 1044464 bytes) \ No newline at end of file diff --git a/include/Global.h b/include/Global.h index 6fd81b67..4be48511 100644 --- a/include/Global.h +++ b/include/Global.h @@ -85,14 +85,18 @@ extern int pwmOut_EnterCounter; extern String countDown_KeyList; extern int countDown_EnterCounter; //========================================= +extern String logging_KeyList; +extern int logging_EnterCounter; +//========================================= // Sensors extern String sensorReadingMap10sec; extern String sensorReadingMap30sec; +extern String itemName; +extern String presetName; + -extern String loggingKeyList; -extern int enter_to_logging_counter; extern int scenario_line_status[40]; extern int lastVersion; diff --git a/include/items/vLogging.h b/include/items/vLogging.h index 53c098af..de217983 100644 --- a/include/items/vLogging.h +++ b/include/items/vLogging.h @@ -14,6 +14,7 @@ class LoggingClass { ~LoggingClass(); void loop(); + void execute(String payload); private: @@ -25,12 +26,13 @@ class LoggingClass { String _loggingValueKey; String _key; - void addNewDelOldData(const String filename, size_t maxPoints, String payload); + }; extern MyLoggingVector* myLogging; extern void logging(); +extern void loggingExecute(); extern void choose_log_date_and_send(); extern void sendLogData(String file, String topic); extern void cleanLogAndData(); diff --git a/platformio.ini b/platformio.ini index 443e042f..db994f90 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,8 +35,10 @@ extra_scripts = ./tools/littlefsbuilder.py ;============================================================================================================================================= [env:esp8266_01_1m] framework = arduino -board = esp01_1m -board_build.ldscript = eagle.flash.1m512.ld +;board = esp01_1m +board = nodemcuv2 +;board_build.ldscript = eagle.flash.1m512.ld +board_build.ldscript = eagle.flash.1m256.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} diff --git a/src/Global.cpp b/src/Global.cpp index 5b5318f3..1cd2a46c 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -55,14 +55,16 @@ int pwmOut_EnterCounter = -1; String countDown_KeyList = ""; int countDown_EnterCounter = -1; //========================================= +String logging_KeyList = ""; +int logging_EnterCounter = -1; +//========================================= // Sensors String sensorReadingMap10sec; String sensorReadingMap30sec; -// Logging -String loggingKeyList; -int enter_to_logging_counter; +String itemName; +String presetName; // Upgrade String serverIP; diff --git a/src/Init.cpp b/src/Init.cpp index 6f3db433..98586aa3 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -52,7 +52,8 @@ void Device_init() { if (myLogging != nullptr) { myLogging->clear(); } - loggingKeyList = ""; + logging_KeyList = ""; + logging_EnterCounter = -1; //======clear impuls params======= if (myImpulsOut != nullptr) { myImpulsOut->clear(); diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index ee865c2b..b18d8f16 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -18,36 +18,56 @@ void itemsListInit() { delChoosingItems(); }, nullptr); +#ifdef FLASH_SIZE_1MB + myNotAsyncActions->add( + do_addItem, [&](void*) { + addItem(itemName); + itemName = ""; + }, + nullptr); + + myNotAsyncActions->add( + do_addPreset, [&](void*) { + addPreset(presetName); + presetName = ""; + }, + nullptr); +#endif } void addItem(String name) { +#ifdef FLASH_SIZE_1MB + String url = serverIP + F("/projects/iotmanager/config/items/") + name + ".txt"; + String item = getURL(url); + Serial.println(url); + Serial.println(item); + if (item == "error") return; +#endif +#ifndef FLASH_SIZE_1MB String item = readFile("items/" + name + ".txt", 1024); +#endif name = selectToMarker(name, "-"); randomSeed(micros()); unsigned int num = random(0, 1000); - item.replace("id", name + String(num)); // "-" + String(getNewElementNumber("id.txt"))); + item.replace("id", name + String(num)); item.replace("order", String(getNewElementNumber("order.txt"))); if (item.indexOf("pin") != -1) { //all cases (random pins from available) item.replace("pin", "pin[" + String(getFreePinAll()) + "]"); - } else - - if (item.indexOf("gol") != -1) { //analog + } + else if (item.indexOf("gol") != -1) { //analog item.replace("gol", "pin[" + String(getFreePinAnalog()) + "]"); - } else - - if (item.indexOf("cin") != -1) { //ultrasonic + } + else if (item.indexOf("cin") != -1) { //ultrasonic item.replace("cin", "pin[" + String(getFreePinAll()) + "," + String(getFreePinAll()) + "]"); - } else - - if (item.indexOf("sal") != -1) { //dallas + } + else if (item.indexOf("sal") != -1) { //dallas item.replace("sal", "pin[2]"); - } else - - if (item.indexOf("thd") != -1) { //dht11/22 + } + else if (item.indexOf("thd") != -1) { //dht11/22 item.replace("thd", "pin[2]"); } @@ -58,12 +78,32 @@ void addItem(String name) { } void addPreset(String name) { +#ifdef FLASH_SIZE_1MB + String url2 = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; + String preset = getURL(url2); + Serial.println(url2); + Serial.println(preset); + if (preset == "error") return; +#endif +#ifndef FLASH_SIZE_1MB String preset = readFile("presets/" + name + ".txt", 4048); +#endif + addFile(DEVICE_CONFIG_FILE, "\n" + preset); - name.replace(".c",".s"); + name.replace(".c", ".s"); +#ifdef FLASH_SIZE_1MB + String url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; + String scenario = getURL(url); + Serial.println(url); + Serial.println(scenario); + if (scenario == "error") return; +#endif +#ifndef FLASH_SIZE_1MB String scenario = readFile("presets/" + name + ".txt", 4048); +#endif + removeFile(DEVICE_SCENARIO_FILE); addFile(DEVICE_SCENARIO_FILE, scenario); loadScenario(); @@ -89,16 +129,17 @@ uint8_t getNewElementNumber(String file) { uint8_t getFreePinAll() { #ifdef ESP8266 - uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; + uint8_t pins[] = { 0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5 }; #endif #ifdef ESP32 - uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; + uint8_t pins[] = { 0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5 }; #endif uint8_t array_sz = sizeof(pins) / sizeof(pins[0]); uint8_t i = getNewElementNumber("pins.txt"); if (i < array_sz) { return pins[i]; - } else { + } + else { return 0; } } @@ -121,7 +162,8 @@ void delChoosingItems() { String item = configFile.readStringUntil('\n'); if (firstLine) { finalConf += item; - } else { + } + else { int checkbox = selectToMarker(item, ";").toInt(); if (checkbox == 0) { finalConf += "\n" + item; diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index 4a8889ad..9583a818 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -42,11 +42,11 @@ void getLastVersion() { #endif if (tmp == "error") { lastVersion = -1; - } + } else { lastVersion = tmp.toInt(); } -} + } else { lastVersion = -2; } @@ -102,7 +102,7 @@ bool upgradeFS() { if (retFS == HTTP_UPDATE_OK) { //если FS обновилась успешно SerialPrint("I", "Update", "LittleFS upgrade done!"); ret = true; -} + } return ret; } @@ -122,7 +122,7 @@ bool upgradeBuild() { if (retBuild == HTTP_UPDATE_OK) { //если BUILD обновился успешно SerialPrint("I", "Update", "BUILD upgrade done!"); ret = true; -} + } return ret; } diff --git a/src/Web.cpp b/src/Web.cpp index ead2e617..ee5f8dc5 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -20,14 +20,24 @@ void web_init() { server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { //==============================set.device.json==================================================================================================== if (request->hasArg("addItem")) { - String name = request->getParam("addItem")->value(); - addItem(name); +#ifdef FLASH_SIZE_1MB + itemName = request->getParam("addItem")->value(); + myNotAsyncActions->make(do_addItem); +#endif +#ifndef FLASH_SIZE_1MB + addItem(request->getParam("addItem")->value()); +#endif request->redirect("/?set.device"); } if (request->hasArg("addPreset")) { - String name = request->getParam("addPreset")->value(); - addPreset(name); +#ifdef FLASH_SIZE_1MB + presetName = request->getParam("addPreset")->value(); + myNotAsyncActions->make(do_addPreset); +#endif +#ifndef FLASH_SIZE_1MB + addPreset(request->getParam("addPreset")->value()); +#endif request->redirect("/?set.device"); } diff --git a/src/items/vInOutput.cpp b/src/items/vInOutput.cpp index c842ac62..d15c0b23 100644 --- a/src/items/vInOutput.cpp +++ b/src/items/vInOutput.cpp @@ -53,6 +53,13 @@ void inOutputExecute() { String key = sCmd.order(); String value = sCmd.next(); + + if (!isDigitStr(value)) { //если значение - текст + if (value.indexOf(":") == -1) { //если этот текст не время + value = getValue(value); + } + } + int number = getKeyNum(key, inOutput_KeyList); if (myInOutput != nullptr) { diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index f7d2db7c..b4cbc6d2 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -16,27 +16,36 @@ LoggingClass::LoggingClass(unsigned long period, unsigned int maxPoints, String LoggingClass::~LoggingClass() {} void LoggingClass::loop() { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _period) { - prevMillis = millis(); - addNewDelOldData("logs/" + _key + ".txt", _maxPoints, getValue(_loggingValueKey)); //jsonReadStr(configLiveJson , _loggingValueKey)); + if (_period > 0) { + currentMillis = millis(); + difference = currentMillis - prevMillis; + if (difference >= _period) { + prevMillis = millis(); + execute(""); + } } } -void LoggingClass::addNewDelOldData(const String filename, size_t maxPoints, String payload) { +void LoggingClass::execute(String payload) { + + if (_period > 0) { + payload = getValue(_loggingValueKey); + } + + String filename = "logs/" + _key + ".txt"; String logData = readFile(filename, 5120); + size_t lines_cnt = itemsCount(logData, "\r\n"); SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " (" + String(lines_cnt, DEC) + ")"); - if ((lines_cnt > maxPoints + 1) || !lines_cnt) { + if ((lines_cnt > _maxPoints + 1) || !lines_cnt) { removeFile(filename); lines_cnt = 0; } if (payload != "") { - if (lines_cnt > maxPoints) { + if (lines_cnt > _maxPoints) { logData = deleteBeforeDelimiter(logData, "\r\n"); if (timeNow->hasTimeSynced()) { logData += timeNow->getTimeUnix() + " " + payload + "\r\n"; @@ -61,16 +70,40 @@ void logging() { String maxcnt = myLineParsing.gcnt(); myLineParsing.clear(); - loggingKeyList += key + ","; + logging_KeyList += key + ","; + + logging_EnterCounter++; + addKey(key, logging_KeyList, logging_EnterCounter); static bool firstTime = true; if (firstTime) myLogging = new MyLoggingVector(); firstTime = false; myLogging->push_back(LoggingClass(interv.toInt(), maxcnt.toInt(), loggingValueKey, key)); + + sCmd.addCommand(key.c_str(), loggingExecute); } +void loggingExecute() { + String key = sCmd.order(); + String value = sCmd.next(); + + if (!isDigitStr(value)) { //если значение - текст + value = getValue(value); + } + + int number = getKeyNum(key, logging_KeyList); + + if (myLogging != nullptr) { + if (number != -1) { + myLogging->at(number).execute(value); + } + } +} + + + void choose_log_date_and_send() { - String all_line = loggingKeyList; + String all_line = logging_KeyList; while (all_line.length() != 0) { String tmp = selectToMarker(all_line, ","); sendLogData("logs/" + tmp + ".txt", tmp); From 5c774c29bd6bef2dd03f00df4d711e94d793c489 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 9 Dec 2020 04:12:46 +0300 Subject: [PATCH 28/94] some --- data/presets/2.c.txt | 2 +- data/set.device.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/presets/2.c.txt b/data/presets/2.c.txt index 0d7e277e..d91fa5c7 100644 --- a/data/presets/2.c.txt +++ b/data/presets/2.c.txt @@ -1,4 +1,4 @@ -0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] +0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[60] 0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] 0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] diff --git a/data/set.device.json b/data/set.device.json index 67052c1f..c558e2a8 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -72,7 +72,7 @@ "#": "Выберите элемент из списка", "/set?addItem=button-out.pin": "1.Кнопка управляющая пином", "/set?addItem=button-out.inv": "2.Кнопка управляющая пином (с инверсией)", - "/set?addItem=button-out.npin": "3.Кнопка виртуальная (не привязанная к пину)", + "/set?addItem=button-out.npin": "3.Кнопка виртуальная (не привязанная к пину, для использования в сценариях)", "/set?addItem=button-in": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", "/set?addItem=pwm-out": "3.Широтно импульсная модуляция pwm", "/set?addItem=input-digit": "5.Окно ввода цифровых значений", From 253a9a5ae58b7c2300d951d448d1cbb572f1b268 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 9 Dec 2020 04:35:39 +0300 Subject: [PATCH 29/94] Some more --- data/presets/6.c.txt | 3 +++ data/presets/6.s.txt | 6 ++++++ data/set.device.json | 5 ++++- src/items/vCountDown.cpp | 10 +++++++--- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 data/presets/6.c.txt create mode 100644 data/presets/6.s.txt diff --git a/data/presets/6.c.txt b/data/presets/6.c.txt new file mode 100644 index 00000000..c7fd096a --- /dev/null +++ b/data/presets/6.c.txt @@ -0,0 +1,3 @@ +0;button-out;button;toggle;Таймер;Освещение;1;pin[12] +0;count-down;count;anydata;Таймер;Обратный#отчет;2 +0;inoutput;input;inputDigit;Таймер;Введите#цифру;3 \ No newline at end of file diff --git a/data/presets/6.s.txt b/data/presets/6.s.txt new file mode 100644 index 00000000..06b70a4a --- /dev/null +++ b/data/presets/6.s.txt @@ -0,0 +1,6 @@ +button = 1 +count input +end +count = 0 +button 0 +end \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index c558e2a8..d72a8e7e 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -108,7 +108,10 @@ "/set?addPreset=2.c": "2.Гистерезис термостат на основе ds18b20 с суточным расписанием", "/set?addPreset=3.c": "3.Контроль влажности на основе DHT с оповещением в телеграм", "/set?addPreset=4.c": "4.Включение выключение реле в заданное время", - "/set?addPreset=5.c": "5.Выключить все (пример работы сценариев)" + "/set?addPreset=5.c": "5.Выключить все (пример работы сценариев)", + "/set?addPreset=6.c": "6.Включить кнопку на определенное время (пример работы таймера обратного отчета)", + "/set?addPreset=7.c": "7.Охранный датчик движения", + "/set?addPreset=8.c": "8.Датчик движения включающий свет" } }, { diff --git a/src/items/vCountDown.cpp b/src/items/vCountDown.cpp index 58effe32..fe20a320 100644 --- a/src/items/vCountDown.cpp +++ b/src/items/vCountDown.cpp @@ -61,13 +61,17 @@ void countDown() { void countDownExecute() { String key = sCmd.order(); - String countDownPeriod = sCmd.next(); - + String value = sCmd.next(); + + if (!isDigitStr(value)) { //если значение - текст + value = getValue(value); + } + int number = getKeyNum(key, countDown_KeyList); if (myCountDown != nullptr) { if (number != -1) { - myCountDown->at(number).execute(countDownPeriod.toInt()); + myCountDown->at(number).execute(value.toInt()); } } } \ No newline at end of file From 81866043bc2ef9825c306b2ada9b8e08167a39ae Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 10 Dec 2020 02:11:47 +0300 Subject: [PATCH 30/94] bug with virtual button fixed --- include/items/vButtonOut.h | 4 ++-- src/items/vButtonOut.cpp | 26 +++++++++++++++----------- src/items/vInOutput.cpp | 7 +++++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/items/vButtonOut.h b/include/items/vButtonOut.h index 3568b516..1f6f2614 100644 --- a/include/items/vButtonOut.h +++ b/include/items/vButtonOut.h @@ -10,7 +10,7 @@ typedef std::vector MyButtonOutVector; class ButtonOut { public: - ButtonOut(unsigned int pin, boolean inv, String key); + ButtonOut(String pin, boolean inv, String key); ~ButtonOut(); @@ -18,7 +18,7 @@ class ButtonOut { private: - unsigned int _pin; + String _pin; boolean _inv; String _key; diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index 15790687..40263eba 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -5,27 +5,31 @@ #include //this class save data to flash -ButtonOut::ButtonOut(unsigned int pin, boolean inv, String key) { +ButtonOut::ButtonOut(String pin, boolean inv, String key) { _pin = pin; _inv = inv; _key = key; - pinMode(_pin, OUTPUT); + if (_pin != "") { + pinMode(_pin.toInt(), OUTPUT); + } int state = jsonReadInt(configStoreJson, key); this->execute(String(state)); } ButtonOut::~ButtonOut() {} void ButtonOut::execute(String state) { - if (state == "change") { - state = String(!digitalRead(_pin)); - digitalWrite(_pin, state.toInt()); - } - else { - if (_inv) { - digitalWrite(_pin, !state.toInt()); + if (state != "" && _pin != "") { + if (state == "change") { + state = String(!digitalRead(_pin.toInt())); + digitalWrite(_pin.toInt(), state.toInt()); } else { - digitalWrite(_pin, state.toInt()); + if (_inv) { + digitalWrite(_pin.toInt(), !state.toInt()); + } + else { + digitalWrite(_pin.toInt(), state.toInt()); + } } } eventGen2(_key, state); @@ -53,7 +57,7 @@ void buttonOut() { static bool firstTime = true; if (firstTime) myButtonOut = new MyButtonOutVector(); firstTime = false; - myButtonOut->push_back(ButtonOut(pin.toInt(), invb, key)); + myButtonOut->push_back(ButtonOut(pin, invb, key)); sCmd.addCommand(key.c_str(), buttonOutExecute); } diff --git a/src/items/vInOutput.cpp b/src/items/vInOutput.cpp index d15c0b23..d5966731 100644 --- a/src/items/vInOutput.cpp +++ b/src/items/vInOutput.cpp @@ -53,10 +53,13 @@ void inOutputExecute() { String key = sCmd.order(); String value = sCmd.next(); - if (!isDigitStr(value)) { //если значение - текст if (value.indexOf(":") == -1) { //если этот текст не время - value = getValue(value); + String tmp = getValue(value); + if(tmp != "no value") { + value = tmp; + value.replace("#"," "); + } } } From d17112b3ff1647ba435205db04bdfafa62e1d9d2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 10 Dec 2020 05:13:09 +0300 Subject: [PATCH 31/94] uart --- data/items/uart-button.txt | 1 + data/items/uart-widget.txt | 1 + data/presets/7.c.txt | 4 ++++ data/presets/7.s.txt | 10 ++++++++++ data/presets/8.c.txt | 4 ++++ data/presets/8.s.txt | 7 +++++++ data/presets/9.c.txt | 2 ++ data/presets/9.s.txt | 3 +++ data/set.device.json | 5 ++++- data/widgets/anydataAlarm.json | 6 ++++++ data/widgets/inputDigitTemp.json | 1 + data/widgets/inputTimeClock.json | 1 + include/SoftUART.h | 2 ++ include/items/vButtonOut.h | 3 ++- src/SoftUART.cpp | 30 ++++++++++++++++-------------- src/Web.cpp | 1 + src/items/vButtonOut.cpp | 14 ++++++++++++-- src/items/vInOutput.cpp | 19 ++++++++++++------- src/main.cpp | 6 ++++-- 19 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 data/items/uart-button.txt create mode 100644 data/items/uart-widget.txt create mode 100644 data/presets/7.c.txt create mode 100644 data/presets/7.s.txt create mode 100644 data/presets/8.c.txt create mode 100644 data/presets/8.s.txt create mode 100644 data/presets/9.c.txt create mode 100644 data/presets/9.s.txt create mode 100644 data/widgets/anydataAlarm.json diff --git a/data/items/uart-button.txt b/data/items/uart-button.txt new file mode 100644 index 00000000..ee2a6b22 --- /dev/null +++ b/data/items/uart-button.txt @@ -0,0 +1 @@ +0;button-out;id;toggle;Кнопки;Освещение;order;type[UART] \ No newline at end of file diff --git a/data/items/uart-widget.txt b/data/items/uart-widget.txt new file mode 100644 index 00000000..469ab016 --- /dev/null +++ b/data/items/uart-widget.txt @@ -0,0 +1 @@ +0;inoutput;id;anydata;Вывод;Вывод#uart;order \ No newline at end of file diff --git a/data/presets/7.c.txt b/data/presets/7.c.txt new file mode 100644 index 00000000..984c6b8d --- /dev/null +++ b/data/presets/7.c.txt @@ -0,0 +1,4 @@ +0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 +0;inoutput;time;anydataTime;Сигнализация;Время:;2 +0;button-in;sensor;na;na;na;3;pin[0];db[20] +0;button-out;reset;toggle;Сигнализация;Сбросить;4 \ No newline at end of file diff --git a/data/presets/7.s.txt b/data/presets/7.s.txt new file mode 100644 index 00000000..32d28e78 --- /dev/null +++ b/data/presets/7.s.txt @@ -0,0 +1,10 @@ +sensor = 1 +text обнаружено +time %date% +telegram text обнаружено#движение 1 +end +reset = 1 +text не#обнаружено +time %date% +reset 0 +end \ No newline at end of file diff --git a/data/presets/8.c.txt b/data/presets/8.c.txt new file mode 100644 index 00000000..d18bdd0b --- /dev/null +++ b/data/presets/8.c.txt @@ -0,0 +1,4 @@ +0;button-in;sensor;na;na;na;1;pin[0];db[20] +0;button-out;light;toggle;Освещение;Освещение;2;pin[13] +0;count-down;count;anydata;Освещение;Обратный#отчет;3 +0;inoutput;period;inputDigit;Освещение;Период#включения;4 \ No newline at end of file diff --git a/data/presets/8.s.txt b/data/presets/8.s.txt new file mode 100644 index 00000000..10d55654 --- /dev/null +++ b/data/presets/8.s.txt @@ -0,0 +1,7 @@ +sensor = 1 +light 1 +count period +end +count = 0 +light 0 +end \ No newline at end of file diff --git a/data/presets/9.c.txt b/data/presets/9.c.txt new file mode 100644 index 00000000..502f3ae4 --- /dev/null +++ b/data/presets/9.c.txt @@ -0,0 +1,2 @@ +0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] +0;button-in;switch;na;na;na;2;pin[0];db[20] \ No newline at end of file diff --git a/data/presets/9.s.txt b/data/presets/9.s.txt new file mode 100644 index 00000000..6f6d7dcc --- /dev/null +++ b/data/presets/9.s.txt @@ -0,0 +1,3 @@ +switch = 1 +light change +end \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index d72a8e7e..5f566c11 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -93,6 +93,8 @@ "/set?addItem=impuls-out": "20.Создать импульсы через заданный промежуток времени (управление шд)", "/set?addItem=count-down": "21.Таймер обратного отчета", "/set?addItem=modbus": "22.Прочитать регистр modbus устройства", + "/set?addItem=uart-button": "23.UART кнопка (шлет свое состояние в UART)", + "/set?addItem=uart-widget": "24.UART виджет (позволяет вывести полученные данные в любой виджет)", "/set?addItem=logging": "a.Логгирование и вывод в график любой величины", "/set?addItem=uptime": "b.Отобразить время работы устройства" } @@ -111,7 +113,8 @@ "/set?addPreset=5.c": "5.Выключить все (пример работы сценариев)", "/set?addPreset=6.c": "6.Включить кнопку на определенное время (пример работы таймера обратного отчета)", "/set?addPreset=7.c": "7.Охранный датчик движения", - "/set?addPreset=8.c": "8.Датчик движения включающий свет" + "/set?addPreset=8.c": "8.Датчик движения включающий свет с настраиваемой задержкой", + "/set?addPreset=9.c": "9.Управление светом с помощью выключателя и приложения" } }, { diff --git a/data/widgets/anydataAlarm.json b/data/widgets/anydataAlarm.json new file mode 100644 index 00000000..d793d350 --- /dev/null +++ b/data/widgets/anydataAlarm.json @@ -0,0 +1,6 @@ +{ + "widget": "anydata", + "after": "", + "color":"red", + "icon": "walk" +} \ No newline at end of file diff --git a/data/widgets/inputDigitTemp.json b/data/widgets/inputDigitTemp.json index 0ed891e1..28f19081 100644 --- a/data/widgets/inputDigitTemp.json +++ b/data/widgets/inputDigitTemp.json @@ -2,5 +2,6 @@ "widget" : "input", "color" : "green", "type" : "number", + "size" : "small", "icon": "thermometer" } \ No newline at end of file diff --git a/data/widgets/inputTimeClock.json b/data/widgets/inputTimeClock.json index 695f6bd1..ab3186f8 100644 --- a/data/widgets/inputTimeClock.json +++ b/data/widgets/inputTimeClock.json @@ -2,5 +2,6 @@ "widget" : "input", "color" : "orange", "type" : "time", + "size" : "small", "icon": "alarm-outline" } \ No newline at end of file diff --git a/include/SoftUART.h b/include/SoftUART.h index 1955616f..7af320cc 100644 --- a/include/SoftUART.h +++ b/include/SoftUART.h @@ -2,6 +2,8 @@ #include "SoftwareSerial.h" +extern SoftwareSerial* myUART; + extern void uartInit(); extern void uartHandle(); extern void parse(String& incStr); \ No newline at end of file diff --git a/include/items/vButtonOut.h b/include/items/vButtonOut.h index 1f6f2614..7a11bc82 100644 --- a/include/items/vButtonOut.h +++ b/include/items/vButtonOut.h @@ -10,7 +10,7 @@ typedef std::vector MyButtonOutVector; class ButtonOut { public: - ButtonOut(String pin, boolean inv, String key); + ButtonOut(String pin, boolean inv, String key, String type); ~ButtonOut(); @@ -21,6 +21,7 @@ class ButtonOut { String _pin; boolean _inv; String _key; + String _type; }; diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index d142e26e..70cf2680 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -23,26 +23,28 @@ void uartInit() { } void uartHandle() { - if (!jsonReadBool(configSetupJson, "uart")) { - return; - } - static String incStr; - if (myUART->available()) { - char inc; - inc = myUART->read(); - incStr += inc; - if (inc == '\n') { - parse(incStr); - incStr = ""; + if (myUART != nullptr) { + if (!jsonReadBool(configSetupJson, "uart")) { + return; + } + static String incStr; + if (myUART->available()) { + char inc; + inc = myUART->read(); + incStr += inc; + if (inc == '\n') { + parse(incStr); + incStr = ""; + } } } } void parse(String& incStr) { + incStr.replace("\r\n", ""); + incStr.replace("\r", ""); + incStr.replace("\n", ""); if (incStr.indexOf("set") != -1) { - incStr.replace("\r\n", ""); - incStr.replace("\r", ""); - incStr.replace("\n", ""); incStr = deleteBeforeDelimiter(incStr, " "); SerialPrint("I", "UART", incStr); orderBuf += incStr; diff --git a/src/Web.cpp b/src/Web.cpp index ee5f8dc5..613b930c 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -38,6 +38,7 @@ void web_init() { #ifndef FLASH_SIZE_1MB addPreset(request->getParam("addPreset")->value()); #endif + jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); request->redirect("/?set.device"); } diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index 40263eba..bc1f6e2c 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -2,13 +2,15 @@ #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" +#include "SoftUART.h" #include //this class save data to flash -ButtonOut::ButtonOut(String pin, boolean inv, String key) { +ButtonOut::ButtonOut(String pin, boolean inv, String key, String type) { _pin = pin; _inv = inv; _key = key; + _type = type; if (_pin != "") { pinMode(_pin.toInt(), OUTPUT); } @@ -18,6 +20,13 @@ ButtonOut::ButtonOut(String pin, boolean inv, String key) { ButtonOut::~ButtonOut() {} void ButtonOut::execute(String state) { + if (_type == "UART") { + if (jsonReadBool(configSetupJson, "uart")) { + if (myUART != nullptr) { + myUART->print(_key + " " + state); + } + } + } if (state != "" && _pin != "") { if (state == "change") { state = String(!digitalRead(_pin.toInt())); @@ -45,6 +54,7 @@ void buttonOut() { String key = myLineParsing.gkey(); String pin = myLineParsing.gpin(); String inv = myLineParsing.ginv(); + String type = myLineParsing.gtype(); bool invb = false; if (inv.toInt() == 1) invb = true; @@ -57,7 +67,7 @@ void buttonOut() { static bool firstTime = true; if (firstTime) myButtonOut = new MyButtonOutVector(); firstTime = false; - myButtonOut->push_back(ButtonOut(pin, invb, key)); + myButtonOut->push_back(ButtonOut(pin, invb, key, type)); sCmd.addCommand(key.c_str(), buttonOutExecute); } diff --git a/src/items/vInOutput.cpp b/src/items/vInOutput.cpp index d5966731..6581c1fb 100644 --- a/src/items/vInOutput.cpp +++ b/src/items/vInOutput.cpp @@ -1,10 +1,10 @@ #include "items/vInOutput.h" - #include - #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" +#include "Clock.h" + //this class save date to flash InOutput::InOutput(String key, String widget) { _key = key; @@ -12,7 +12,7 @@ InOutput::InOutput(String key, String widget) { if (value == "") { if (widget.indexOf("Digit") != -1) { - value = "52"; + value = "25"; } if (widget.indexOf("Time") != -1) { value = "12:00"; @@ -52,13 +52,18 @@ void inOutput() { void inOutputExecute() { String key = sCmd.order(); String value = sCmd.next(); + //String type = sCmd.next(); + if (!isDigitStr(value)) { //если значение - текст if (value.indexOf(":") == -1) { //если этот текст не время - String tmp = getValue(value); - if(tmp != "no value") { - value = tmp; - value.replace("#"," "); + String valueJson = getValue(value); + if (valueJson != "no value") { //если это ключ переменной + value = valueJson; + } + else { //если это просто текст + value.replace("#", " "); + value.replace("%date%", timeNow->getDateTimeDotFormated()); } } } diff --git a/src/main.cpp b/src/main.cpp index 34c19945..44c52ff1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,9 @@ void setup() { fileSystemInit(); SerialPrint("I", F("FS"), F("FS Init")); + SerialPrint("I", F("UART"), F("UART Init")); + uartInit(); + loadConfig(); SerialPrint("I", F("Conf"), F("Config Init")); @@ -92,8 +95,7 @@ void setup() { SerialPrint("I", F("Bus"), F("Bus Init")); busInit(); - SerialPrint("I", F("UART"), F("UART Init")); - uartInit(); + #ifdef SSDP_ENABLED SerialPrint("I", F("SSDP"), F("Ssdp Init")); From f487a691fb244e6a2940d2bdf1ada60ae4ce770c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 10 Dec 2020 19:12:15 +0300 Subject: [PATCH 32/94] uart working version --- data/set.device.json | 2 +- include/Clock.h | 2 +- include/Cmd.h | 2 +- include/Consts.h | 2 +- include/Init.h | 4 +- include/Utils/TimeUtils.h | 2 +- include/Utils/WebUtils.h | 3 +- src/BufferExecute.cpp | 1 + src/Bus.cpp | 7 ++- src/Clock.cpp | 4 +- src/Init.cpp | 12 ++++-- src/ItemsList.cpp | 3 +- src/RemoteOrdersUdp.cpp | 2 + src/SSDP.cpp | 1 + src/SoftUART.cpp | 7 +-- src/Telegram.cpp | 1 + src/UpgradeFirm.cpp | 1 + src/Utils/FileUtils.cpp | 3 +- src/Utils/TimeUtils.cpp | 28 +++++++----- src/Utils/WebUtils.cpp | 40 +++++++++++++++-- src/Utils/WiFiUtils.cpp | 6 ++- src/Utils/statUtils.cpp | 1 + src/Web.cpp | 4 +- src/WebServer.cpp | 2 + src/items/vButtonOut.cpp | 6 ++- src/main.cpp | 91 +++++---------------------------------- 26 files changed, 119 insertions(+), 118 deletions(-) diff --git a/data/set.device.json b/data/set.device.json index 5f566c11..c1163c58 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -43,7 +43,7 @@ }, { "type": "h4", - "title": "Версия файловой системы: 268" + "title": "Версия файловой системы: 270" }, { "type": "h4", diff --git a/include/Clock.h b/include/Clock.h index dea567c8..fdb55cbe 100644 --- a/include/Clock.h +++ b/include/Clock.h @@ -5,7 +5,7 @@ #include "Utils/TimeUtils.h" #include "Utils\SerialPrint.h" -extern void clock_init(); +extern void clockInit(); #ifdef ESP8266 #include "sntp.h" diff --git a/include/Cmd.h b/include/Cmd.h index eb27380d..2fec07d9 100644 --- a/include/Cmd.h +++ b/include/Cmd.h @@ -42,7 +42,7 @@ extern void bme280A_reading(); //extern void dhtC_reading(); //extern void dhtD_reading(); -extern void handle_time_init(); +extern void timeInit(); extern void stepper(); extern void stepperSet(); extern void servo_(); diff --git a/include/Consts.h b/include/Consts.h index 7fb57c28..0d41037f 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,7 +1,7 @@ #pragma once //===========Firmware============================================================================================================================================= -#define FIRMWARE_VERSION 268 +#define FIRMWARE_VERSION 270 //#define FLASH_SIZE_1MB #ifdef ESP8266 #define FIRMWARE_NAME "esp8266-iotm" diff --git a/include/Init.h b/include/Init.h index bb2b2ca7..e7fc3157 100644 --- a/include/Init.h +++ b/include/Init.h @@ -1,10 +1,10 @@ #pragma once extern void loadConfig(); -extern void all_init(); +extern void espInit(); extern void statistics_init(); extern void loadScenario(); -extern void Device_init(); +extern void deviceInit(); extern void prsets_init(); extern void handle_uptime(); extern void handle_statistics(); diff --git a/include/Utils/TimeUtils.h b/include/Utils/TimeUtils.h index 024d1bc8..60bf405f 100644 --- a/include/Utils/TimeUtils.h +++ b/include/Utils/TimeUtils.h @@ -50,4 +50,4 @@ int getOffsetInMinutes(int timezone); */ void breakEpochToTime(unsigned long epoch, Time_t& tm); -void handle_time_init(); \ No newline at end of file +void timeInit(); \ No newline at end of file diff --git a/include/Utils/WebUtils.h b/include/Utils/WebUtils.h index 5479314c..15e412db 100644 --- a/include/Utils/WebUtils.h +++ b/include/Utils/WebUtils.h @@ -4,4 +4,5 @@ extern String getURL(const String& urls); extern const String getMethodName(AsyncWebServerRequest* request); -extern const String getRequestInfo(AsyncWebServerRequest* request); \ No newline at end of file +extern const String getRequestInfo(AsyncWebServerRequest* request); +extern void wifiSignalInit(); \ No newline at end of file diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index ad33b79c..4af187a8 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -144,6 +144,7 @@ void sensorsInit() { } }, nullptr, true); + SerialPrint("I", F("Sensors"), F("Sensors Init")); } void addKey(String& key, String& keyNumberTable, int number) { diff --git a/src/Bus.cpp b/src/Bus.cpp index 12cbcf6a..eeff8108 100644 --- a/src/Bus.cpp +++ b/src/Bus.cpp @@ -10,12 +10,14 @@ void busInit() { tmp = i2c_scan(); Serial.println(tmp); jsonWriteStr(configLiveJson, "i2c", tmp); - } else { + } + else { Serial.println(tmp); jsonWriteStr(configLiveJson, "i2c", tmp); } }, nullptr); + SerialPrint("I", F("Bus"), F("Bus Init")); } String i2c_scan() { @@ -32,7 +34,8 @@ String i2c_scan() { } if (count == 0) { return "error"; - } else { + } + else { return out; } } \ No newline at end of file diff --git a/src/Clock.cpp b/src/Clock.cpp index 888f9d5b..2ae4132a 100644 --- a/src/Clock.cpp +++ b/src/Clock.cpp @@ -3,14 +3,14 @@ #include "Global.h" Clock* timeNow; -void clock_init() { +void clockInit() { timeNow = new Clock; timeNow->setNtpPool(jsonReadStr(configSetupJson, "ntp")); timeNow->setTimezone(jsonReadStr(configSetupJson, "timezone").toInt()); - ts.add( TIME_SYNC, 30000, [&](void*) { timeNow->hasSync(); }, nullptr, true); + SerialPrint("I", F("Time"), F("Clock Init")); } diff --git a/src/Init.cpp b/src/Init.cpp index 98586aa3..25885189 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -26,17 +26,20 @@ void loadConfig() { prex = jsonReadStr(configSetupJson, "mqttPrefix") + "/" + chipId; - Serial.println(configSetupJson); + //Serial.println(configSetupJson); serverIP = jsonReadStr(configSetupJson, "serverip"); + + SerialPrint("I", F("Conf"), F("Config Json Init")); } -void all_init() { - Device_init(); +void espInit() { + deviceInit(); loadScenario(); + SerialPrint("I", F("esp"), F("esp Init")); } -void Device_init() { +void deviceInit() { sensorReadingMap10sec = ""; @@ -110,6 +113,7 @@ void uptime_init() { handle_uptime(); }, nullptr, true); + SerialPrint("I", F("Uptime"), F("Uptime Init")); } void handle_uptime() { diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index b18d8f16..d80c11c0 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -9,7 +9,7 @@ static const char* firstLine PROGMEM = "Удалить;Тип элемента;I void itemsListInit() { myNotAsyncActions->add( do_deviceInit, [&](void*) { - Device_init(); + deviceInit(); }, nullptr); @@ -33,6 +33,7 @@ void itemsListInit() { }, nullptr); #endif + SerialPrint("I", F("Items"), F("Items Init")); } void addItem(String name) { diff --git a/src/RemoteOrdersUdp.cpp b/src/RemoteOrdersUdp.cpp index c3428fec..70ff516c 100644 --- a/src/RemoteOrdersUdp.cpp +++ b/src/RemoteOrdersUdp.cpp @@ -75,6 +75,8 @@ void asyncUdpInit() { //}, //nullptr, true); + SerialPrint("I", F("UDP"), "Udp Init"); + } bool udpPacketValidation(String& data) { diff --git a/src/SSDP.cpp b/src/SSDP.cpp index c84d99ca..fb431bbd 100644 --- a/src/SSDP.cpp +++ b/src/SSDP.cpp @@ -47,6 +47,7 @@ void SsdpInit() { SSDP.setDeviceType(F("upnp:rootdevice")); SSDP.setSchemaURL(F("description.xml")); SSDP.begin(); + SerialPrint("I", F("SSDP"), F("Ssdp Init")); } String xmlNode(String tags, String data) { diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 70cf2680..91ba781d 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -20,10 +20,11 @@ void uartInit() { myUART->begin(4, 5); #endif } + SerialPrint("I", F("UART"), F("UART Init")); } void uartHandle() { - if (myUART != nullptr) { + if (myUART) { if (!jsonReadBool(configSetupJson, "uart")) { return; } @@ -34,7 +35,7 @@ void uartHandle() { incStr += inc; if (inc == '\n') { parse(incStr); - incStr = ""; + incStr = ""; } } } @@ -46,7 +47,7 @@ void parse(String& incStr) { incStr.replace("\n", ""); if (incStr.indexOf("set") != -1) { incStr = deleteBeforeDelimiter(incStr, " "); - SerialPrint("I", "UART", incStr); orderBuf += incStr; + SerialPrint("I", "=>UART", incStr); } } \ No newline at end of file diff --git a/src/Telegram.cpp b/src/Telegram.cpp index cc560330..6a5237ca 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -19,6 +19,7 @@ void telegramInit() { else { SerialPrint("E", "Telegram", "Not connected"); } + SerialPrint("I", F("Telegram"), F("Telegram Init")); } } diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index 9583a818..63e7168f 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -31,6 +31,7 @@ void upgradeInit() { } } }; + SerialPrint("I", F("Update"), F("Updater Init")); } void getLastVersion() { diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp index dd13682f..70955088 100644 --- a/src/Utils/FileUtils.cpp +++ b/src/Utils/FileUtils.cpp @@ -10,9 +10,10 @@ const String filepath(const String& filename) { bool fileSystemInit() { if (!LittleFS.begin()) { - SerialPrint("[E]","Files","init"); + SerialPrint("E", F("FS"), F("FS Init ERROR, may be FS was not flashed")); return false; } + SerialPrint("I", F("FS"), F("FS Init")); return true; } diff --git a/src/Utils/TimeUtils.cpp b/src/Utils/TimeUtils.cpp index 5420b4a1..b377965a 100644 --- a/src/Utils/TimeUtils.cpp +++ b/src/Utils/TimeUtils.cpp @@ -2,8 +2,8 @@ #include "Global.h" #include "Utils/StringUtils.h" -static const uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static const char* week_days[7] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"}; +static const uint8_t days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const char* week_days[7] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; // String getTimeUnix() { // time_t t; @@ -118,7 +118,8 @@ const String prettySeconds(unsigned long time_s) { if (days) { sprintf_P(buf, TIME_FORMAT_WITH_DAYS, days, hours, minutes, seconds); - } else { + } + else { sprintf_P(buf, TIME_FORMAT, hours, minutes, seconds); } return String(buf); @@ -138,15 +139,18 @@ unsigned long millis_passed(unsigned long start, unsigned long finish) { unsigned long passed = finish - start; if (passed <= __LONG_MAX__) { result = static_cast(passed); - } else { + } + else { result = static_cast((__LONG_MAX__ - finish) + start + 1u); } - } else { + } + else { unsigned long passed = start - finish; if (passed <= __LONG_MAX__) { result = static_cast(passed); result = -1 * result; - } else { + } + else { result = static_cast((__LONG_MAX__ - start) + finish + 1u); result = -1 * result; } @@ -194,16 +198,19 @@ void breakEpochToTime(unsigned long epoch, Time_t& tm) { if (1 == month) { // february if (LEAP_YEAR(year)) { month_length = 29; - } else { + } + else { month_length = 28; } - } else { + } + else { month_length = days_in_month[month]; } if (time >= month_length) { time -= month_length; - } else { + } + else { break; } } @@ -212,7 +219,7 @@ void breakEpochToTime(unsigned long epoch, Time_t& tm) { tm.valid = (epoch > MIN_DATETIME); } -void handle_time_init() { +void timeInit() { ts.add( TIME, 1000, [&](void*) { String timenow = timeNow->getTimeWOsec(); @@ -224,4 +231,5 @@ void handle_time_init() { } }, nullptr, true); + SerialPrint("I", F("Time"), F("Handle time init")); } diff --git a/src/Utils/WebUtils.cpp b/src/Utils/WebUtils.cpp index 8c766b0e..f3807912 100644 --- a/src/Utils/WebUtils.cpp +++ b/src/Utils/WebUtils.cpp @@ -8,7 +8,8 @@ String getURL(const String& urls) { int httpCode = http.GET(); if (httpCode == HTTP_CODE_OK) { res = http.getString(); - } else { + } + else { res = "error"; } http.end(); @@ -68,9 +69,11 @@ const String getRequestInfo(AsyncWebServerRequest* request) { AsyncWebParameter* p = request->getParam(i); if (p->isFile()) { res += "FILE"; - } else if (p->isPost()) { + } + else if (p->isPost()) { res += "POST"; - } else { + } + else { res += "GET"; } res += ' '; @@ -86,3 +89,34 @@ const String getRequestInfo(AsyncWebServerRequest* request) { } return res; } + +void wifiSignalInit() { + ts.add( + SYGNAL, 1000 * 60, [&](void*) { + SerialPrint("I", "System", printMemoryStatus()); + switch (RSSIquality()) { + case 0: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); + break; + case 1: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); + break; + case 2: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); + break; + case 3: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); + break; + case 4: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); + break; + case 5: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); + break; + case 6: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); + break; + } + }, + nullptr, true); +} diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index 5b163c93..10ecd39c 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -1,13 +1,16 @@ #include "Utils/WiFiUtils.h" void routerConnect() { + + WiFi.setAutoConnect(false); + WiFi.persistent(false); + setLedStatus(LED_SLOW); WiFi.mode(WIFI_STA); byte tries = 20; String _ssid = jsonReadStr(configSetupJson, "routerssid"); String _password = jsonReadStr(configSetupJson, "routerpass"); - //WiFi.persistent(false); if (_ssid == "" && _password == "") { WiFi.begin(); @@ -39,6 +42,7 @@ void routerConnect() { setLedStatus(LED_OFF); mqttInit(); } + SerialPrint("I", F("WIFI"), F("Network Init")); } bool startAPMode() { diff --git a/src/Utils/statUtils.cpp b/src/Utils/statUtils.cpp index fad0a29d..b7e7c690 100644 --- a/src/Utils/statUtils.cpp +++ b/src/Utils/statUtils.cpp @@ -25,6 +25,7 @@ void initSt() { }, nullptr, true); } + SerialPrint("I", F("Stat"), F("Stat Init")); } void decide() { diff --git a/src/Web.cpp b/src/Web.cpp index 613b930c..8e4b6eb9 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -311,7 +311,7 @@ void web_init() { // itemsLine = request->getParam("line")->value(); // } // delElementFlag = true; - // Device_init(); + // deviceInit(); // request->redirect("/?setn.device"); //}); @@ -360,6 +360,8 @@ void web_init() { myNotAsyncActions->make(do_UPGRADE); request->send(200, "text/html"); }); + + SerialPrint("I", F("Web"), F("WebAdmin Init")); } void setConfigParam(const char* param, const String& value) { diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 9a4611f4..c955e9a2 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -75,6 +75,8 @@ void init() { initOta(); initMDNS(); initWS(); + + SerialPrint("I", F("HTTP"), F("HttpServer Init")); } void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index bc1f6e2c..ff98e2ee 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -22,8 +22,10 @@ ButtonOut::~ButtonOut() {} void ButtonOut::execute(String state) { if (_type == "UART") { if (jsonReadBool(configSetupJson, "uart")) { - if (myUART != nullptr) { - myUART->print(_key + " " + state); + if (myUART) { + String msg = _key + " " + state; + myUART->print(msg); + SerialPrint("I", "<=UART", msg); } } } diff --git a/src/main.cpp b/src/main.cpp index 44c52ff1..45398eef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,116 +29,48 @@ Timings metric; boolean initialized = false; void setup() { - WiFi.setAutoConnect(false); - WiFi.persistent(false); - Serial.begin(115200); Serial.flush(); Serial.println(); Serial.println(F("--------------started----------------")); - setChipId(); - myNotAsyncActions = new NotAsync(do_LAST); myScenario = new Scenario(); + //=========================================initialisation============================================================== + setChipId(); fileSystemInit(); - SerialPrint("I", F("FS"), F("FS Init")); - - SerialPrint("I", F("UART"), F("UART Init")); - uartInit(); - loadConfig(); - SerialPrint("I", F("Conf"), F("Config Init")); - - clock_init(); - SerialPrint("I", F("Time"), F("Clock Init")); - - handle_time_init(); - SerialPrint("I", F("Time"), F("Handle time init")); - - sensorsInit(); - SerialPrint("I", F("Sensors"), F("Sensors Init")); - + uartInit(); + clockInit(); + timeInit(); + sensorsInit(); //Will be remooved itemsListInit(); - SerialPrint("I", F("Items"), F("Items Init")); - - all_init(); - SerialPrint("I", F("Init"), F("Init Init")); - + espInit(); routerConnect(); - SerialPrint("I", F("WIFI"), F("Network Init")); - telegramInit(); - SerialPrint("I", F("Telegram"), F("Telegram Init")); - uptime_init(); - SerialPrint("I", F("Uptime"), F("Uptime Init")); - upgradeInit(); - SerialPrint("I", F("Update"), F("Updater Init")); - HttpServer::init(); - SerialPrint("I", F("HTTP"), F("HttpServer Init")); - web_init(); - SerialPrint("I", F("Web"), F("WebAdmin Init")); - initSt(); - SerialPrint("I", F("Stat"), F("Stat Init")); - + busInit(); #ifdef UDP_ENABLED - SerialPrint("I", F("UDP"), "Udp Init"); asyncUdpInit(); #endif - - SerialPrint("I", F("Bus"), F("Bus Init")); - busInit(); - - - #ifdef SSDP_ENABLED - SerialPrint("I", F("SSDP"), F("Ssdp Init")); SsdpInit(); #endif //esp_log_level_set("esp_littlefs", ESP_LOG_NONE); - ts.add( - SYGNAL, 1000 * 60, [&](void*) { - SerialPrint("I", "System", printMemoryStatus()); - switch (RSSIquality()) { - case 0: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); - break; - case 1: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); - break; - case 2: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); - break; - case 3: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); - break; - case 4: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); - break; - case 5: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); - break; - case 6: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); - break; - } - }, - nullptr, true); - - - just_load = false; initialized = true; } + + + void loop() { if (!initialized) { return; @@ -154,7 +86,6 @@ void loop() { myButtonIn.loop(); myScenario->loop(); loopCmdExecute(); - //loopSerial(); myNotAsyncActions->loop(); ts.update(); From 71c8fec27b7f310ed19f221e09d42852b03d84be Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 10 Dec 2020 20:19:21 +0300 Subject: [PATCH 33/94] bug fixed --- data/set.device.json | 2 +- include/Consts.h | 2 +- include/Utils/WebUtils.h | 1 - include/Utils/WiFiUtils.h | 2 ++ src/Utils/WebUtils.cpp | 30 ------------------------------ src/Utils/WiFiUtils.cpp | 32 ++++++++++++++++++++++++++++++++ src/main.cpp | 1 + 7 files changed, 37 insertions(+), 33 deletions(-) diff --git a/data/set.device.json b/data/set.device.json index c1163c58..4a501636 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -43,7 +43,7 @@ }, { "type": "h4", - "title": "Версия файловой системы: 270" + "title": "Версия файловой системы: 271" }, { "type": "h4", diff --git a/include/Consts.h b/include/Consts.h index 0d41037f..1efaa87f 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,7 +1,7 @@ #pragma once //===========Firmware============================================================================================================================================= -#define FIRMWARE_VERSION 270 +#define FIRMWARE_VERSION 271 //#define FLASH_SIZE_1MB #ifdef ESP8266 #define FIRMWARE_NAME "esp8266-iotm" diff --git a/include/Utils/WebUtils.h b/include/Utils/WebUtils.h index 15e412db..f9b74587 100644 --- a/include/Utils/WebUtils.h +++ b/include/Utils/WebUtils.h @@ -5,4 +5,3 @@ extern String getURL(const String& urls); extern const String getMethodName(AsyncWebServerRequest* request); extern const String getRequestInfo(AsyncWebServerRequest* request); -extern void wifiSignalInit(); \ No newline at end of file diff --git a/include/Utils/WiFiUtils.h b/include/Utils/WiFiUtils.h index a6dbcc34..7a634009 100644 --- a/include/Utils/WiFiUtils.h +++ b/include/Utils/WiFiUtils.h @@ -12,3 +12,5 @@ boolean RouterFind(String ssid); uint8_t RSSIquality(); +extern void wifiSignalInit(); + diff --git a/src/Utils/WebUtils.cpp b/src/Utils/WebUtils.cpp index f3807912..d7083c62 100644 --- a/src/Utils/WebUtils.cpp +++ b/src/Utils/WebUtils.cpp @@ -90,33 +90,3 @@ const String getRequestInfo(AsyncWebServerRequest* request) { return res; } -void wifiSignalInit() { - ts.add( - SYGNAL, 1000 * 60, [&](void*) { - SerialPrint("I", "System", printMemoryStatus()); - switch (RSSIquality()) { - case 0: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); - break; - case 1: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); - break; - case 2: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); - break; - case 3: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); - break; - case 4: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); - break; - case 5: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); - break; - case 6: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); - break; - } - }, - nullptr, true); -} diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index 10ecd39c..a8326df2 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -141,4 +141,36 @@ uint8_t RSSIquality() { return res; } +void wifiSignalInit() { + ts.add( + SYGNAL, 1000 * 60, [&](void*) { + SerialPrint("I", "System", printMemoryStatus()); + switch (RSSIquality()) { + case 0: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); + break; + case 1: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); + break; + case 2: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); + break; + case 3: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); + break; + case 4: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); + break; + case 5: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); + break; + case 6: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); + break; + } + }, + nullptr, true); +} + + diff --git a/src/main.cpp b/src/main.cpp index 45398eef..41936b07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,6 +55,7 @@ void setup() { web_init(); initSt(); busInit(); + wifiSignalInit(); #ifdef UDP_ENABLED asyncUdpInit(); #endif From 71468599feb7092bf2c51de963bda8776fc64b4f Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 11 Dec 2020 03:21:50 +0300 Subject: [PATCH 34/94] stability of san --- include/Consts.h | 10 +- include/MqttClient.h | 1 + include/items/SensorModbusClass.h | 168 +++++++++++++------------- include/items/SensorUltrasonicClass.h | 49 -------- include/items/vSensorUltrasonic.h | 2 - platformio.ini | 1 - src/Class/ScenarioClass3.cpp | 3 +- src/ItemsList.cpp | 19 +-- src/MqttClient.cpp | 21 ++-- src/Web.cpp | 7 +- src/items/SensorUltrasonicClass.cpp | 20 --- 11 files changed, 125 insertions(+), 176 deletions(-) delete mode 100644 include/items/SensorUltrasonicClass.h delete mode 100644 src/items/SensorUltrasonicClass.cpp diff --git a/include/Consts.h b/include/Consts.h index 1efaa87f..d017003f 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,7 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 271 -//#define FLASH_SIZE_1MB +//#define FLASH_SIZE_1MB true #ifdef ESP8266 #define FIRMWARE_NAME "esp8266-iotm" #endif @@ -121,4 +121,10 @@ enum ConfigType_t { //Flash: [===== ] 54.5% (used 569296 bytes from 1044464 bytes) //RAM: [===== ] 45.6% (used 37336 bytes from 81920 bytes) -//Flash: [====== ] 55.3% (used 577396 bytes from 1044464 bytes) \ No newline at end of file +//Flash: [====== ] 55.3% (used 577396 bytes from 1044464 bytes) + + +//eventBuf - буфер событий которые проверяются в сценариях, +//и если событие удовлетворяет какому нибудь условию то выполняются указанные команды + +//orderBuf - буфер команд которые выполняются сейчас же \ No newline at end of file diff --git a/include/MqttClient.h b/include/MqttClient.h index f2d7b82c..88a166d7 100644 --- a/include/MqttClient.h +++ b/include/MqttClient.h @@ -17,6 +17,7 @@ boolean publishChart(const String& topic, const String& data); boolean publishControl(String id, String topic, String state); boolean publishChart_test(const String& topic, const String& data); boolean publishStatus(const String& topic, const String& data); +boolean publishEvent(const String& topic, const String& data); boolean publishInfo(const String& topic, const String& data); void publishWidgets(); diff --git a/include/items/SensorModbusClass.h b/include/items/SensorModbusClass.h index a93063c8..494540ea 100644 --- a/include/items/SensorModbusClass.h +++ b/include/items/SensorModbusClass.h @@ -1,83 +1,85 @@ -//#pragma once -//#include -//#include -//#include -// -//#include "Class/LineParsing.h" -//#include "Global.h" -//#include "items/SensorConvertingClass.h" -// -//ModbusMaster modbus1; -//SoftwareSerial uart(13, 12); // RX, TX -// -//class SensorModbusClass : public SensorConvertingClass { -// public: -// SensorModbusClass() : SensorConvertingClass(){}; -// -// void SensorModbusInit() { -// uart.begin(9600); -// jsonWriteStr(configOptionJson, _key + "_map", _map); -// jsonWriteStr(configOptionJson, _key + "_с", _c); -// sensorReadingMap10sec += _key + " " + _addr + " " + _reg + ","; -// Serial.println(sensorReadingMap10sec); -// } -// -// void SensorModbusRead(String key, uint8_t slaveAddress, uint16_t regAddress) { -// int value; -// -// modbus1.begin(slaveAddress, uart); -// uint16_t reqisterValue = modbus1.readHoldingRegisters(regAddress, 1); -// if (getResultMsg(&modbus1, reqisterValue)) { -// reqisterValue = modbus1.getResponseBuffer(0); -// value = reqisterValue; -// } else { -// value = NULL; -// } -// -// int valueFl = this->correction(key, value); -// eventGen2(key, String(valueFl)); -// jsonWriteStr(configLiveJson, key, String(valueFl)); -// publishStatus(key, String(valueFl)); -// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl) + ", Slave dev addr: " + String(slaveAddress) + ", Register: " + String(regAddress)); -// } -// -// bool getResultMsg(ModbusMaster* modbus1, uint16_t result) { -// String tmpstr; -// switch (result) { -// case modbus1->ku8MBSuccess: -// return true; -// tmpstr += "Ok"; -// break; -// case modbus1->ku8MBIllegalFunction: -// tmpstr += "Illegal Function"; -// break; -// case modbus1->ku8MBIllegalDataAddress: -// tmpstr += "Illegal Data Address"; -// break; -// case modbus1->ku8MBIllegalDataValue: -// tmpstr += "Illegal Data Value"; -// break; -// case modbus1->ku8MBSlaveDeviceFailure: -// tmpstr += "Slave Device Failure"; -// break; -// case modbus1->ku8MBInvalidSlaveID: -// tmpstr += "Invalid Slave ID"; -// break; -// case modbus1->ku8MBInvalidFunction: -// tmpstr += "Invalid Function"; -// break; -// case modbus1->ku8MBResponseTimedOut: -// tmpstr += "Response Timed Out"; -// break; -// case modbus1->ku8MBInvalidCRC: -// tmpstr += "Invalid CRC"; -// break; -// default: -// tmpstr += "Unknown error: " + String(result); -// break; -// } -// SerialPrint("I", "Modbus", tmpstr); -// return false; -// } -//}; -//extern SensorModbusClass mySensorModbus; \ No newline at end of file +#ifdef modbus +#pragma once +#include +#include +#include + +#include "Class/LineParsing.h" +#include "Global.h" +#include "items/SensorConvertingClass.h" + +ModbusMaster modbus1; +SoftwareSerial uart(13, 12); // RX, TX + +class SensorModbusClass : public SensorConvertingClass { + public: + SensorModbusClass() : SensorConvertingClass(){}; + + void SensorModbusInit() { + uart.begin(9600); + jsonWriteStr(configOptionJson, _key + "_map", _map); + jsonWriteStr(configOptionJson, _key + "_с", _c); + sensorReadingMap10sec += _key + " " + _addr + " " + _reg + ","; + Serial.println(sensorReadingMap10sec); + } + + void SensorModbusRead(String key, uint8_t slaveAddress, uint16_t regAddress) { + int value; + + modbus1.begin(slaveAddress, uart); + uint16_t reqisterValue = modbus1.readHoldingRegisters(regAddress, 1); + if (getResultMsg(&modbus1, reqisterValue)) { + reqisterValue = modbus1.getResponseBuffer(0); + value = reqisterValue; + } else { + value = NULL; + } + + int valueFl = this->correction(key, value); + eventGen2(key, String(valueFl)); + jsonWriteStr(configLiveJson, key, String(valueFl)); + publishStatus(key, String(valueFl)); + SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl) + ", Slave dev addr: " + String(slaveAddress) + ", Register: " + String(regAddress)); + } + + bool getResultMsg(ModbusMaster* modbus1, uint16_t result) { + String tmpstr; + switch (result) { + case modbus1->ku8MBSuccess: + return true; + tmpstr += "Ok"; + break; + case modbus1->ku8MBIllegalFunction: + tmpstr += "Illegal Function"; + break; + case modbus1->ku8MBIllegalDataAddress: + tmpstr += "Illegal Data Address"; + break; + case modbus1->ku8MBIllegalDataValue: + tmpstr += "Illegal Data Value"; + break; + case modbus1->ku8MBSlaveDeviceFailure: + tmpstr += "Slave Device Failure"; + break; + case modbus1->ku8MBInvalidSlaveID: + tmpstr += "Invalid Slave ID"; + break; + case modbus1->ku8MBInvalidFunction: + tmpstr += "Invalid Function"; + break; + case modbus1->ku8MBResponseTimedOut: + tmpstr += "Response Timed Out"; + break; + case modbus1->ku8MBInvalidCRC: + tmpstr += "Invalid CRC"; + break; + default: + tmpstr += "Unknown error: " + String(result); + break; + } + SerialPrint("I", "Modbus", tmpstr); + return false; + } +}; +extern SensorModbusClass mySensorModbus; +#endif \ No newline at end of file diff --git a/include/items/SensorUltrasonicClass.h b/include/items/SensorUltrasonicClass.h deleted file mode 100644 index 96d6883c..00000000 --- a/include/items/SensorUltrasonicClass.h +++ /dev/null @@ -1,49 +0,0 @@ -//#pragma once -//#include -// -//#include "Class/LineParsing.h" -//#include "Global.h" -//#include "items/SensorConvertingClass.h" -//#include "GyverFilters.h" -// -//GMedian<6, int> testFilter; -// -//class SensorUltrasonic : public SensorConvertingClass { -// public: -// SensorUltrasonic() : SensorConvertingClass(){}; -// void init() { -// sensorReadingMap10sec += _key + ","; -// String trig = selectFromMarkerToMarker(_pin, ",", 0); -// String echo = selectFromMarkerToMarker(_pin, ",", 1); -// pinMode(trig.toInt(), OUTPUT); -// pinMode(echo.toInt(), INPUT); -// jsonWriteStr(configOptionJson, _key + "_trig", trig); -// jsonWriteStr(configOptionJson, _key + "_echo", echo); -// jsonWriteStr(configOptionJson, _key + "_map", _map); -// jsonWriteStr(configOptionJson, _key + "_с", _c); -// } -// -// void SensorUltrasonicRead(String key) { -// int trig = jsonReadStr(configOptionJson, key + "_trig").toInt(); -// int echo = jsonReadStr(configOptionJson, key + "_echo").toInt(); -// int value; -// -// digitalWrite(trig, LOW); -// delayMicroseconds(2); -// digitalWrite(trig, HIGH); -// delayMicroseconds(10); -// digitalWrite(trig, LOW); -// long duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m -// value = duration_ / 29 / 2; -// -// value = testFilter.filtered(value); -// -// value = this->mapping(key, value); -// float valueFl = this->correction(key, value); -// eventGen2(key, String(valueFl)); -// jsonWriteStr(configLiveJson, key, String(valueFl)); -// publishStatus(key, String(valueFl)); -// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); -// } -//}; -//extern SensorUltrasonic mySensorUltrasonic; \ No newline at end of file diff --git a/include/items/vSensorUltrasonic.h b/include/items/vSensorUltrasonic.h index a0c6311c..368af649 100644 --- a/include/items/vSensorUltrasonic.h +++ b/include/items/vSensorUltrasonic.h @@ -8,8 +8,6 @@ class SensorUltrasonic; typedef std::vector MySensorUltrasonicVector; - - class SensorUltrasonic : public SensorConvertingClass { public: diff --git a/platformio.ini b/platformio.ini index db994f90..7b6ac912 100644 --- a/platformio.ini +++ b/platformio.ini @@ -37,7 +37,6 @@ extra_scripts = ./tools/littlefsbuilder.py framework = arduino ;board = esp01_1m board = nodemcuv2 -;board_build.ldscript = eagle.flash.1m512.ld board_build.ldscript = eagle.flash.1m256.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index 085f8784..ec2f4efb 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -10,7 +10,8 @@ void eventGen2(String eventName, String eventValue) { String event = eventName + " " + eventValue + ","; eventBuf += event; - streamEventUDP(event); + if (jsonReadBool(configSetupJson, "snaMqtt")) publishEvent(eventName, eventValue); + //streamEventUDP(event); } void streamEventUDP(String event) { diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index d80c11c0..acab5352 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -41,7 +41,6 @@ void addItem(String name) { String url = serverIP + F("/projects/iotmanager/config/items/") + name + ".txt"; String item = getURL(url); Serial.println(url); - Serial.println(item); if (item == "error") return; #endif #ifndef FLASH_SIZE_1MB @@ -75,30 +74,31 @@ void addItem(String name) { item.replace("\r\n", ""); item.replace("\r", ""); item.replace("\n", ""); + addFile(DEVICE_CONFIG_FILE, "\n" + item); + Serial.println(item); } void addPreset(String name) { #ifdef FLASH_SIZE_1MB - String url2 = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; - String preset = getURL(url2); - Serial.println(url2); - Serial.println(preset); + String url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; + String preset = getURL(url); + Serial.println(url); if (preset == "error") return; #endif #ifndef FLASH_SIZE_1MB String preset = readFile("presets/" + name + ".txt", 4048); #endif - + addFile(DEVICE_CONFIG_FILE, "\n" + preset); + Serial.println(preset); name.replace(".c", ".s"); #ifdef FLASH_SIZE_1MB - String url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; + url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; String scenario = getURL(url); Serial.println(url); - Serial.println(scenario); if (scenario == "error") return; #endif #ifndef FLASH_SIZE_1MB @@ -106,8 +106,11 @@ void addPreset(String name) { #endif removeFile(DEVICE_SCENARIO_FILE); + + addFile(DEVICE_SCENARIO_FILE, scenario); loadScenario(); + Serial.println(scenario); } void delAllItems() { diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 3486745d..2d31e71b 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -72,7 +72,7 @@ void mqttSubscribe() { mqtt.subscribe((mqttRootDevice + "/update").c_str()); if (jsonReadBool(configSetupJson, "snaMqtt")) { - mqtt.subscribe((mqttPrefix + "/+/+/status").c_str()); + mqtt.subscribe((mqttPrefix + "/+/+/event").c_str()); mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); } } @@ -142,26 +142,24 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { SerialPrint("I", "=>MQTT", "Msg from iotmanager app: " + key + " " + payloadStr); } - else if (topicStr.indexOf("status") != -1) { + else if (topicStr.indexOf("event") != -1) { if (!jsonReadBool(configSetupJson, "snaMqtt")) { return; } if (topicStr.indexOf(chipId) == -1) { String devId = selectFromMarkerToMarker(topicStr, "/", 2); String key = selectFromMarkerToMarker(topicStr, "/", 3); - String value = jsonReadStr(payloadStr, "status"); - - SerialPrint("I", "=>MQTT", "Msg from other device: '" + devId + "' " + key + " " + value); - - eventGen2(key, value); + SerialPrint("I", "=>MQTT", "Received event from other device: '" + devId + "' " + key + " " + payloadStr); + String event = key + " " + payloadStr + ","; + eventBuf += event; } } else if (topicStr.indexOf("info") != -1) { if (topicStr.indexOf("scen") != -1) { writeFile(String(DEVICE_SCENARIO_FILE), payloadStr); - loadScenario(); - SerialPrint("I", "=>MQTT", "Scenario received"); + loadScenario(); + SerialPrint("I", "=>MQTT", "Scenario received"); } } @@ -215,6 +213,11 @@ boolean publishStatus(const String& topic, const String& data) { return mqtt.publish(path.c_str(), json.c_str(), false); } +boolean publishEvent(const String& topic, const String& data) { + String path = mqttRootDevice + "/" + topic + "/event"; + return mqtt.publish(path.c_str(), data.c_str(), false); +} + boolean publishInfo(const String& topic, const String& data) { String path = mqttRootDevice + "/" + topic + "/info"; return mqtt.publish(path.c_str(), data.c_str(), false); diff --git a/src/Web.cpp b/src/Web.cpp index 8e4b6eb9..5990488b 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -323,6 +323,10 @@ void web_init() { SerialPrint("I", "Update", "firmware version: " + String(lastVersion)); String msg = ""; + //if (FLASH_SIZE_1MB) { + // msg = F("Обновление невозможно, память устройства 1 мб"); + //} + //else { if (lastVersion == FIRMWARE_VERSION) { msg = F("Актуальная версия прошивки уже установлена."); } @@ -338,6 +342,7 @@ void web_init() { else if (lastVersion < FIRMWARE_VERSION) { msg = F("Ошибка версии. Попробуйте повторить позже..."); } + //} // else if (lastVersion == "") { //msg = F("Нажмите на кнопку \"обновить прошивку\" повторно..."); @@ -361,7 +366,7 @@ void web_init() { request->send(200, "text/html"); }); - SerialPrint("I", F("Web"), F("WebAdmin Init")); + SerialPrint("I", F("Web"), F("WebAdmin Init")); } void setConfigParam(const char* param, const String& value) { diff --git a/src/items/SensorUltrasonicClass.cpp b/src/items/SensorUltrasonicClass.cpp deleted file mode 100644 index 1d2fb965..00000000 --- a/src/items/SensorUltrasonicClass.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//#include "BufferExecute.h" -//#include "items/SensorUltrasonicClass.h" -////#ifdef SensorUltrasonicEnabled -////=========================================Модуль ультрозвукового дальномера================================================================== -////ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[1,100,1,100];c[1] -////========================================================================================================================================= -//SensorUltrasonic mySensorUltrasonic; -//void ultrasonicCm() { -// mySensorUltrasonic.update(); -// String key = mySensorUltrasonic.gkey(); -// sCmd.addCommand(key.c_str(), ultrasonicReading); -// mySensorUltrasonic.init(); -// mySensorUltrasonic.clear(); -//} -// -//void ultrasonicReading() { -// String key = sCmd.order(); -// mySensorUltrasonic.SensorUltrasonicRead(key); -//} -////#endif \ No newline at end of file From 3e5d9770a1178c00acbe7f15d3360cd05935fc0f Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 11 Dec 2020 17:37:29 +0300 Subject: [PATCH 35/94] 272 --- data/config.json | 4 ++-- data/set.dev.json | 14 ++++++++++++-- data/set.device.json | 31 ++++++++++++++++++++++++------- data/set.mqtt.json | 20 ++++++++++++-------- data/set.telegram.json | 12 +++++++++--- data/set.wifi.json | 11 +++++++++-- include/Consts.h | 11 ++++++++--- src/Class/ScenarioClass3.cpp | 11 ++++++----- src/MqttClient.cpp | 6 +++--- src/Web.cpp | 28 ++++++++++++++++++++-------- 10 files changed, 105 insertions(+), 43 deletions(-) diff --git a/data/config.json b/data/config.json index 10d2fcc6..827b49ed 100644 --- a/data/config.json +++ b/data/config.json @@ -18,8 +18,8 @@ "teleginput":"0", "weblogin": "admin", "webpass": "admin", - "snaUdp": "0", - "snaMqtt": "0", + "MqttIn": "0", + "MqttOut": "0", "blink": "1", "oneWirePin": "2", "serverip": "http://206.189.49.244", diff --git a/data/set.dev.json b/data/set.dev.json index fb697b50..4815eb95 100644 --- a/data/set.dev.json +++ b/data/set.dev.json @@ -37,9 +37,19 @@ "class": "btn btn-block btn-default" }, { - "type": "link", + "type": "hr" + }, + { + "type": "h3", + "name": "reset-block", + "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", + "class": "hidden" + }, + { + "type": "button", "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/set?device=ok');}", + "action": "/set?reqReset", + "response": "[[reset-block]]", "class": "btn btn-block btn-danger" } ] diff --git a/data/set.device.json b/data/set.device.json index 4a501636..eec1e537 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -43,7 +43,7 @@ }, { "type": "h4", - "title": "Версия файловой системы: 271" + "title": "Версия файловой системы: 272" }, { "type": "h4", @@ -179,10 +179,20 @@ }, { "type": "checkbox", - "name": "snaMqtt", - "title": "Включить единые сценарии для всех устройств", - "action": "/set?snaMqtt=[[snaMqtt]]", - "state": "{{snaMqtt}}" + "name": "MqttOut", + "title": "Передавать события другим устройствам", + "action": "/set?MqttOut=[[MqttOut]]", + "state": "{{MqttOut}}" + }, + { + "type": "hr" + }, + { + "type": "checkbox", + "name": "MqttIn", + "title": "Принимать события с других устройств", + "action": "/set?MqttIn=[[MqttIn]]", + "state": "{{MqttIn}}" }, { "type": "hr" @@ -234,9 +244,16 @@ "type": "hr" }, { - "type": "link", + "type": "h3", + "name": "reset-block", + "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", + "class": "hidden" + }, + { + "type": "button", "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/set?device=ok');}", + "action": "/set?reqReset", + "response": "[[reset-block]]", "class": "btn btn-block btn-danger" } ] diff --git a/data/set.mqtt.json b/data/set.mqtt.json index a088c2dc..58223acf 100644 --- a/data/set.mqtt.json +++ b/data/set.mqtt.json @@ -9,7 +9,6 @@ "type": "h5", "title": "{{name}}", "class": "alert-default" - }, { "type": "link", @@ -20,7 +19,6 @@ { "type": "hr" }, - { "type": "h4", "title": "{{SetMQTTServerName}}", @@ -87,7 +85,6 @@ "style": "position:fixed;top:30%;left:50%;width:400px;margin-left:-200px;text-align:center;", "class": "hidden" }, - { "type": "button", "title": "{{ButSave}}", @@ -102,7 +99,6 @@ "action": "set?mqttsend", "class": "btn btn-block btn-default" }, - { "type": "button", "style": "width:100%;float:left;", @@ -115,7 +111,6 @@ "type": "text", "style": "width:100%;float:left;", "title": "

{{SetMQTTWarn1}}

" - }, { "type": "text", @@ -123,10 +118,19 @@ "title": "

{{SetMQTTWarn2}}

" }, { - "type": "link", - "style": "width:100%;float:left;", + "type": "hr" + }, + { + "type": "h3", + "name": "reset-block", + "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", + "class": "hidden" + }, + { + "type": "button", "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/restart?device=ok');}", + "action": "/set?reqReset", + "response": "[[reset-block]]", "class": "btn btn-block btn-danger" } ] diff --git a/data/set.telegram.json b/data/set.telegram.json index 43db41e8..a0857838 100644 --- a/data/set.telegram.json +++ b/data/set.telegram.json @@ -49,7 +49,6 @@ "name": "chatId-arg", "state": "{{chatId}}" }, - { "type": "h4", "title": "Telegram API token" @@ -78,9 +77,16 @@ "type": "hr" }, { - "type": "link", + "type": "h3", + "name": "reset-block", + "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", + "class": "hidden" + }, + { + "type": "button", "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/set?device=ok');}", + "action": "/set?reqReset", + "response": "[[reset-block]]", "class": "btn btn-block btn-danger" } ] diff --git a/data/set.wifi.json b/data/set.wifi.json index 936d1cc2..01fa46f9 100644 --- a/data/set.wifi.json +++ b/data/set.wifi.json @@ -175,9 +175,16 @@ "type": "hr" }, { - "type": "link", + "type": "h3", + "name": "reset-block", + "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", + "class": "hidden" + }, + { + "type": "button", "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/set?device=ok');}", + "action": "/set?reqReset", + "response": "[[reset-block]]", "class": "btn btn-block btn-danger" } ] diff --git a/include/Consts.h b/include/Consts.h index d017003f..941df935 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,13 +1,18 @@ #pragma once //===========Firmware============================================================================================================================================= -#define FIRMWARE_VERSION 271 +#define FIRMWARE_VERSION 272 //#define FLASH_SIZE_1MB true #ifdef ESP8266 -#define FIRMWARE_NAME "esp8266-iotm" +#ifdef FLASH_SIZE_1MB +#define FIRMWARE_NAME "esp8266-1mb" +#else +#define FIRMWARE_NAME "esp8266" +#endif + #endif #ifdef ESP32 -#define FIRMWARE_NAME "esp32-iotm" +#define FIRMWARE_NAME "esp32" #endif //===========FSystem============================================================================================================================================== diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index ec2f4efb..b50f3f83 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -10,20 +10,21 @@ void eventGen2(String eventName, String eventValue) { String event = eventName + " " + eventValue + ","; eventBuf += event; - if (jsonReadBool(configSetupJson, "snaMqtt")) publishEvent(eventName, eventValue); - //streamEventUDP(event); + if (jsonReadBool(configSetupJson, "MqttOut")) { + publishEvent(eventName, eventValue); + } } void streamEventUDP(String event) { - #ifdef UDP_ENABLED +#ifdef UDP_ENABLED if (!jsonReadBool(configSetupJson, "snaUdp")) { return; } - + if (event.indexOf("timenow") == -1) { event = "iotm;event:" + event; asyncUdp.broadcastTo(event.c_str(), 4210); } - #endif +#endif } \ No newline at end of file diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 2d31e71b..d5ce4107 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -71,7 +71,7 @@ void mqttSubscribe() { mqtt.subscribe((mqttRootDevice + "/+/control").c_str()); mqtt.subscribe((mqttRootDevice + "/update").c_str()); - if (jsonReadBool(configSetupJson, "snaMqtt")) { + if (jsonReadBool(configSetupJson, "MqttIn")) { mqtt.subscribe((mqttPrefix + "/+/+/event").c_str()); mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); } @@ -143,7 +143,7 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { } else if (topicStr.indexOf("event") != -1) { - if (!jsonReadBool(configSetupJson, "snaMqtt")) { + if (!jsonReadBool(configSetupJson, "MqttIn")) { return; } if (topicStr.indexOf(chipId) == -1) { @@ -215,7 +215,7 @@ boolean publishStatus(const String& topic, const String& data) { boolean publishEvent(const String& topic, const String& data) { String path = mqttRootDevice + "/" + topic + "/event"; - return mqtt.publish(path.c_str(), data.c_str(), false); + return mqtt.publish(path.c_str(), data.c_str(), true); } boolean publishInfo(const String& topic, const String& data) { diff --git a/src/Web.cpp b/src/Web.cpp index 5990488b..6be0c2cc 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -88,15 +88,22 @@ void web_init() { // request->send(200); //} - if (request->hasArg("snaMqtt")) { - bool value = request->getParam("snaMqtt")->value().toInt(); - jsonWriteBool(configSetupJson, "snaMqtt", value); + if (request->hasArg("MqttIn")) { + bool value = request->getParam("MqttIn")->value().toInt(); + jsonWriteBool(configSetupJson, "MqttIn", value); saveConfig(); - mqtt.subscribe((mqttPrefix + "/+/+/status").c_str()); + mqtt.subscribe((mqttPrefix + "/+/+/event").c_str()); mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); request->send(200); } + if (request->hasArg("MqttOut")) { + bool value = request->getParam("MqttOut")->value().toInt(); + jsonWriteBool(configSetupJson, "MqttOut", value); + saveConfig(); + request->send(200); + } + if (request->hasArg("scenMqtt")) { myNotAsyncActions->make(do_sendScenMQTT); request->send(200); @@ -175,10 +182,15 @@ void web_init() { request->send(200); } - if (request->hasArg("device")) { - if (request->getParam("device")->value() == "ok") { - ESP.restart(); - } + if (request->hasArg("reqReset")) { + String tmp = "{}"; + jsonWriteStr(tmp, "title", F("Вы действительно хотите перезагрузить устройство?
Идет перезагрузка устройства')\">Перезагрузить")); + jsonWriteStr(tmp, "class", "pop-up"); + request->send(200, "text/html", tmp); + } + + if (request->hasArg("reset")) { + ESP.restart(); request->send(200); } From b2385bbc4c0a9e8688c8089f435e85477570bea4 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 12 Dec 2020 00:43:36 +0300 Subject: [PATCH 36/94] =?UTF-8?q?1=20=D0=BC=D0=B1=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config.json | 4 ++-- data/edit.htm.gz | Bin 5455 -> 0 bytes data/icon.jpeg | Bin 15396 -> 0 bytes data/items/analog-adc.txt | 1 - data/items/bme280-hum.txt | 1 - data/items/bme280-press.txt | 1 - data/items/bme280-temp.txt | 1 - data/items/bmp280-press.txt | 1 - data/items/bmp280-temp.txt | 1 - data/items/button-in.txt | 1 - data/items/button-out.inv.txt | 1 - data/items/button-out.npin.txt | 1 - data/items/button-out.pin.txt | 1 - data/items/count-down.txt | 1 - data/items/dallas-temp.txt | 1 - data/items/dht11-hum.txt | 1 - data/items/dht11-temp.txt | 1 - data/items/dht22-hum.txt | 1 - data/items/dht22-temp.txt | 1 - data/items/impuls-out.txt | 1 - data/items/input-digit.txt | 1 - data/items/input-time.txt | 1 - data/items/logging.txt | 1 - data/items/modbus.txt | 1 - data/items/output-text.txt | 1 - data/items/pwm-out.txt | 1 - data/items/uart-button.txt | 1 - data/items/uart-widget.txt | 1 - data/items/ultrasonic-cm.txt | 1 - data/items/uptime.txt | 1 - data/presets/1.c.txt | 5 ----- data/presets/1.s.txt | 8 -------- data/presets/2.c.txt | 12 ------------ data/presets/2.s.txt | 18 ------------------ data/presets/3.c.txt | 5 ----- data/presets/3.s.txt | 8 -------- data/presets/4.c.txt | 4 ---- data/presets/4.s.txt | 8 -------- data/presets/5.c.txt | 7 ------- data/presets/5.s.txt | 16 ---------------- data/presets/6.c.txt | 3 --- data/presets/6.s.txt | 6 ------ data/presets/7.c.txt | 4 ---- data/presets/7.s.txt | 10 ---------- data/presets/8.c.txt | 4 ---- data/presets/8.s.txt | 7 ------- data/presets/9.c.txt | 2 -- data/presets/9.s.txt | 3 --- include/Consts.h | 2 +- platformio.ini | 3 ++- 50 files changed, 5 insertions(+), 161 deletions(-) delete mode 100644 data/edit.htm.gz delete mode 100644 data/icon.jpeg delete mode 100644 data/items/analog-adc.txt delete mode 100644 data/items/bme280-hum.txt delete mode 100644 data/items/bme280-press.txt delete mode 100644 data/items/bme280-temp.txt delete mode 100644 data/items/bmp280-press.txt delete mode 100644 data/items/bmp280-temp.txt delete mode 100644 data/items/button-in.txt delete mode 100644 data/items/button-out.inv.txt delete mode 100644 data/items/button-out.npin.txt delete mode 100644 data/items/button-out.pin.txt delete mode 100644 data/items/count-down.txt delete mode 100644 data/items/dallas-temp.txt delete mode 100644 data/items/dht11-hum.txt delete mode 100644 data/items/dht11-temp.txt delete mode 100644 data/items/dht22-hum.txt delete mode 100644 data/items/dht22-temp.txt delete mode 100644 data/items/impuls-out.txt delete mode 100644 data/items/input-digit.txt delete mode 100644 data/items/input-time.txt delete mode 100644 data/items/logging.txt delete mode 100644 data/items/modbus.txt delete mode 100644 data/items/output-text.txt delete mode 100644 data/items/pwm-out.txt delete mode 100644 data/items/uart-button.txt delete mode 100644 data/items/uart-widget.txt delete mode 100644 data/items/ultrasonic-cm.txt delete mode 100644 data/items/uptime.txt delete mode 100644 data/presets/1.c.txt delete mode 100644 data/presets/1.s.txt delete mode 100644 data/presets/2.c.txt delete mode 100644 data/presets/2.s.txt delete mode 100644 data/presets/3.c.txt delete mode 100644 data/presets/3.s.txt delete mode 100644 data/presets/4.c.txt delete mode 100644 data/presets/4.s.txt delete mode 100644 data/presets/5.c.txt delete mode 100644 data/presets/5.s.txt delete mode 100644 data/presets/6.c.txt delete mode 100644 data/presets/6.s.txt delete mode 100644 data/presets/7.c.txt delete mode 100644 data/presets/7.s.txt delete mode 100644 data/presets/8.c.txt delete mode 100644 data/presets/8.s.txt delete mode 100644 data/presets/9.c.txt delete mode 100644 data/presets/9.s.txt diff --git a/data/config.json b/data/config.json index 827b49ed..710b94ac 100644 --- a/data/config.json +++ b/data/config.json @@ -10,8 +10,8 @@ "mqttServer": "wqtt.ru", "mqttPort": 8021, "mqttPrefix": "/iotTest", - "mqttUser": "u_K1CK9Q", - "mqttPass": "YjyCloWS", + "mqttUser": "rise", + "mqttPass": "hostel3333", "scen": "1", "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", diff --git a/data/edit.htm.gz b/data/edit.htm.gz deleted file mode 100644 index 5786121f98a84b5695314c9ad8e5537f57650ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5455 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 z 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 diff --git a/data/items/analog-adc.txt b/data/items/analog-adc.txt deleted file mode 100644 index 8bc0e914..00000000 --- a/data/items/analog-adc.txt +++ /dev/null @@ -1 +0,0 @@ -0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1] \ No newline at end of file diff --git a/data/items/bme280-hum.txt b/data/items/bme280-hum.txt deleted file mode 100644 index c8afd255..00000000 --- a/data/items/bme280-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bme280-press.txt b/data/items/bme280-press.txt deleted file mode 100644 index f79973e2..00000000 --- a/data/items/bme280-press.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bme280-temp.txt b/data/items/bme280-temp.txt deleted file mode 100644 index 6e5e9003..00000000 --- a/data/items/bme280-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bmp280-press.txt b/data/items/bmp280-press.txt deleted file mode 100644 index 44b9e8c9..00000000 --- a/data/items/bmp280-press.txt +++ /dev/null @@ -1 +0,0 @@ -0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bmp280-temp.txt b/data/items/bmp280-temp.txt deleted file mode 100644 index c3cb42eb..00000000 --- a/data/items/bmp280-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/button-in.txt b/data/items/button-in.txt deleted file mode 100644 index 79d79ec9..00000000 --- a/data/items/button-in.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] \ No newline at end of file diff --git a/data/items/button-out.inv.txt b/data/items/button-out.inv.txt deleted file mode 100644 index 5499a108..00000000 --- a/data/items/button-out.inv.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] \ No newline at end of file diff --git a/data/items/button-out.npin.txt b/data/items/button-out.npin.txt deleted file mode 100644 index ecffd881..00000000 --- a/data/items/button-out.npin.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order \ No newline at end of file diff --git a/data/items/button-out.pin.txt b/data/items/button-out.pin.txt deleted file mode 100644 index b0645025..00000000 --- a/data/items/button-out.pin.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin \ No newline at end of file diff --git a/data/items/count-down.txt b/data/items/count-down.txt deleted file mode 100644 index 3300573c..00000000 --- a/data/items/count-down.txt +++ /dev/null @@ -1 +0,0 @@ -0;count-down;id;anydata;Таймер;Обратный#отчет;order \ No newline at end of file diff --git a/data/items/dallas-temp.txt b/data/items/dallas-temp.txt deleted file mode 100644 index 0da8bdca..00000000 --- a/data/items/dallas-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;index[0];int[10] \ No newline at end of file diff --git a/data/items/dht11-hum.txt b/data/items/dht11-hum.txt deleted file mode 100644 index b7d9a820..00000000 --- a/data/items/dht11-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/data/items/dht11-temp.txt b/data/items/dht11-temp.txt deleted file mode 100644 index 39c5e949..00000000 --- a/data/items/dht11-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/data/items/dht22-hum.txt b/data/items/dht22-hum.txt deleted file mode 100644 index ab69cf97..00000000 --- a/data/items/dht22-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/data/items/dht22-temp.txt b/data/items/dht22-temp.txt deleted file mode 100644 index d14a434d..00000000 --- a/data/items/dht22-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/data/items/impuls-out.txt b/data/items/impuls-out.txt deleted file mode 100644 index 99994ae5..00000000 --- a/data/items/impuls-out.txt +++ /dev/null @@ -1 +0,0 @@ -0;impuls-out;id;na;na;na;order;pin \ No newline at end of file diff --git a/data/items/input-digit.txt b/data/items/input-digit.txt deleted file mode 100644 index 7df7260e..00000000 --- a/data/items/input-digit.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file diff --git a/data/items/input-time.txt b/data/items/input-time.txt deleted file mode 100644 index 3fc078b0..00000000 --- a/data/items/input-time.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;inputTime;Ввод;Введите#время;order \ No newline at end of file diff --git a/data/items/logging.txt b/data/items/logging.txt deleted file mode 100644 index 8253774f..00000000 --- a/data/items/logging.txt +++ /dev/null @@ -1 +0,0 @@ -0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] \ No newline at end of file diff --git a/data/items/modbus.txt b/data/items/modbus.txt deleted file mode 100644 index a782395a..00000000 --- a/data/items/modbus.txt +++ /dev/null @@ -1 +0,0 @@ -0;modbus;id;anydata;Modbus;Регистр;order;addr[1];reg[0];c[1] \ No newline at end of file diff --git a/data/items/output-text.txt b/data/items/output-text.txt deleted file mode 100644 index 65d45284..00000000 --- a/data/items/output-text.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;anydata;Вывод;Сигнализация;order \ No newline at end of file diff --git a/data/items/pwm-out.txt b/data/items/pwm-out.txt deleted file mode 100644 index 2acbb459..00000000 --- a/data/items/pwm-out.txt +++ /dev/null @@ -1 +0,0 @@ -0;pwm-out;id;range;Ползунки;Яркость;order;pin \ No newline at end of file diff --git a/data/items/uart-button.txt b/data/items/uart-button.txt deleted file mode 100644 index ee2a6b22..00000000 --- a/data/items/uart-button.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;type[UART] \ No newline at end of file diff --git a/data/items/uart-widget.txt b/data/items/uart-widget.txt deleted file mode 100644 index 469ab016..00000000 --- a/data/items/uart-widget.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;anydata;Вывод;Вывод#uart;order \ No newline at end of file diff --git a/data/items/ultrasonic-cm.txt b/data/items/ultrasonic-cm.txt deleted file mode 100644 index c5d518c3..00000000 --- a/data/items/ultrasonic-cm.txt +++ /dev/null @@ -1 +0,0 @@ -0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] \ No newline at end of file diff --git a/data/items/uptime.txt b/data/items/uptime.txt deleted file mode 100644 index 972241b4..00000000 --- a/data/items/uptime.txt +++ /dev/null @@ -1 +0,0 @@ -0;uptime;id;anydataTime;Системные;%name%#uptime;order \ No newline at end of file diff --git a/data/presets/1.c.txt b/data/presets/1.c.txt deleted file mode 100644 index abcd06dc..00000000 --- a/data/presets/1.c.txt +++ /dev/null @@ -1,5 +0,0 @@ -0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] -0;logging;log;chart;Термостат;История;2;val[temp];int[60];cnt[100] -0;inoutput;inputU;inputDigit;Термостат;Верхний#порог;3 -0;inoutput;inputL;inputDigit;Термостат;Нижний#порог;4 -0;button-out;button;toggle;Термостат;Нагрев;5;pin[12] \ No newline at end of file diff --git a/data/presets/1.s.txt b/data/presets/1.s.txt deleted file mode 100644 index dc4c8c07..00000000 --- a/data/presets/1.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -temp > inputU -button 0 -telegram нагрев#выключен 1 -end -temp < inputL -button 1 -telegram нагрев#включен 1 -end \ No newline at end of file diff --git a/data/presets/2.c.txt b/data/presets/2.c.txt deleted file mode 100644 index d91fa5c7..00000000 --- a/data/presets/2.c.txt +++ /dev/null @@ -1,12 +0,0 @@ -0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[60] -0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] -0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 -0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] -0;inoutput;time1;inputTimeClock;Расписание;Утренний#период;8 -0;inoutput;threshold1;inputDigitTemp;Расписание;Температура;9 -0;inoutput;time2;inputTimeClock;Расписание;Дневной#период;10 -0;inoutput;threshold2;inputDigitTemp;Расписание;Температура;11 -0;inoutput;time3;inputTimeClock;Расписание;Вечерний#период;12 -0;inoutput;threshold3;inputDigitTemp;Расписание;Температура;13 -0;inoutput;time4;inputTimeClock;Расписание;Ночной#период;14 -0;inoutput;threshold4;inputDigitTemp;Расписание;Температура;15 \ No newline at end of file diff --git a/data/presets/2.s.txt b/data/presets/2.s.txt deleted file mode 100644 index 5a6f36f1..00000000 --- a/data/presets/2.s.txt +++ /dev/null @@ -1,18 +0,0 @@ -temp > threshold+-2 -heater 0 -end -temp < threshold+-2 -heater 1 -end -timenow = time1 -threshold threshold1 -end -timenow = time2 -threshold threshold2 -end -timenow = time3 -threshold threshold3 -end -timenow = time4 -threshold threshold4 -end \ No newline at end of file diff --git a/data/presets/3.c.txt b/data/presets/3.c.txt deleted file mode 100644 index fb2c602b..00000000 --- a/data/presets/3.c.txt +++ /dev/null @@ -1,5 +0,0 @@ -0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] -0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] -0;inoutput;inputU;inputDigit;Теплица;Верхний#порог;3 -0;inoutput;inputL;inputDigit;Теплица;Нижний#порог;4 -0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/data/presets/3.s.txt b/data/presets/3.s.txt deleted file mode 100644 index f69cdc54..00000000 --- a/data/presets/3.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -hum > inputU -button 0 -telegram полив#выключен 1 -end -hum < inputL -button 1 -telegram полив#включен 1 -end \ No newline at end of file diff --git a/data/presets/4.c.txt b/data/presets/4.c.txt deleted file mode 100644 index 3f1ccaa1..00000000 --- a/data/presets/4.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;button-out;button1;toggle;Реле;Освещение;1;pin[12] -0;button-out;button2;toggle;Реле;Освещение;2;pin[13] -0;inoutput;T1;inputTime;Реле;Введите#время#включения;3 -0;inoutput;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/data/presets/4.s.txt b/data/presets/4.s.txt deleted file mode 100644 index 844d1c5b..00000000 --- a/data/presets/4.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -timenow = T1 -button1 1 -button2 0 -end -timenow = T2 -button1 0 -button2 1 -end \ No newline at end of file diff --git a/data/presets/5.c.txt b/data/presets/5.c.txt deleted file mode 100644 index 359a7a52..00000000 --- a/data/presets/5.c.txt +++ /dev/null @@ -1,7 +0,0 @@ -0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1 -0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12] -0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13] -0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] -0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] -0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] -0;inoutput;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/data/presets/5.s.txt b/data/presets/5.s.txt deleted file mode 100644 index 0424728a..00000000 --- a/data/presets/5.s.txt +++ /dev/null @@ -1,16 +0,0 @@ -button-out-1 = 1 -button-out-2 1 -button-out-3 1 -button-out-4 1 -pwm-out-5 200 -pwm-out-6 800 -output-text-7 включено -end -button-out-1 = 0 -button-out-2 0 -button-out-3 0 -button-out-4 0 -pwm-out-5 800 -pwm-out-6 200 -output-text-7 выключено -end \ No newline at end of file diff --git a/data/presets/6.c.txt b/data/presets/6.c.txt deleted file mode 100644 index c7fd096a..00000000 --- a/data/presets/6.c.txt +++ /dev/null @@ -1,3 +0,0 @@ -0;button-out;button;toggle;Таймер;Освещение;1;pin[12] -0;count-down;count;anydata;Таймер;Обратный#отчет;2 -0;inoutput;input;inputDigit;Таймер;Введите#цифру;3 \ No newline at end of file diff --git a/data/presets/6.s.txt b/data/presets/6.s.txt deleted file mode 100644 index 06b70a4a..00000000 --- a/data/presets/6.s.txt +++ /dev/null @@ -1,6 +0,0 @@ -button = 1 -count input -end -count = 0 -button 0 -end \ No newline at end of file diff --git a/data/presets/7.c.txt b/data/presets/7.c.txt deleted file mode 100644 index 984c6b8d..00000000 --- a/data/presets/7.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 -0;inoutput;time;anydataTime;Сигнализация;Время:;2 -0;button-in;sensor;na;na;na;3;pin[0];db[20] -0;button-out;reset;toggle;Сигнализация;Сбросить;4 \ No newline at end of file diff --git a/data/presets/7.s.txt b/data/presets/7.s.txt deleted file mode 100644 index 32d28e78..00000000 --- a/data/presets/7.s.txt +++ /dev/null @@ -1,10 +0,0 @@ -sensor = 1 -text обнаружено -time %date% -telegram text обнаружено#движение 1 -end -reset = 1 -text не#обнаружено -time %date% -reset 0 -end \ No newline at end of file diff --git a/data/presets/8.c.txt b/data/presets/8.c.txt deleted file mode 100644 index d18bdd0b..00000000 --- a/data/presets/8.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;button-in;sensor;na;na;na;1;pin[0];db[20] -0;button-out;light;toggle;Освещение;Освещение;2;pin[13] -0;count-down;count;anydata;Освещение;Обратный#отчет;3 -0;inoutput;period;inputDigit;Освещение;Период#включения;4 \ No newline at end of file diff --git a/data/presets/8.s.txt b/data/presets/8.s.txt deleted file mode 100644 index 10d55654..00000000 --- a/data/presets/8.s.txt +++ /dev/null @@ -1,7 +0,0 @@ -sensor = 1 -light 1 -count period -end -count = 0 -light 0 -end \ No newline at end of file diff --git a/data/presets/9.c.txt b/data/presets/9.c.txt deleted file mode 100644 index 502f3ae4..00000000 --- a/data/presets/9.c.txt +++ /dev/null @@ -1,2 +0,0 @@ -0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] -0;button-in;switch;na;na;na;2;pin[0];db[20] \ No newline at end of file diff --git a/data/presets/9.s.txt b/data/presets/9.s.txt deleted file mode 100644 index 6f6d7dcc..00000000 --- a/data/presets/9.s.txt +++ /dev/null @@ -1,3 +0,0 @@ -switch = 1 -light change -end \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index 941df935..ceeb1dd2 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,7 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 272 -//#define FLASH_SIZE_1MB true +#define FLASH_SIZE_1MB true #ifdef ESP8266 #ifdef FLASH_SIZE_1MB #define FIRMWARE_NAME "esp8266-1mb" diff --git a/platformio.ini b/platformio.ini index 7b6ac912..5040336a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266 +default_envs = esp8266_01_1m ;============================================================================================================================================= [common_env_data] lib_deps_external = @@ -36,6 +36,7 @@ extra_scripts = ./tools/littlefsbuilder.py [env:esp8266_01_1m] framework = arduino ;board = esp01_1m +;board = esp12e board = nodemcuv2 board_build.ldscript = eagle.flash.1m256.ld platform = https://github.com/platformio/platform-espressif8266.git From e9820efff9860c900082d00f6e932d5260af2735 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 12 Dec 2020 01:14:31 +0300 Subject: [PATCH 37/94] 1_mb_final --- data/.exclude.files | 2 -- data/index.json | 5 ----- 2 files changed, 7 deletions(-) delete mode 100644 data/.exclude.files diff --git a/data/.exclude.files b/data/.exclude.files deleted file mode 100644 index 955397fa..00000000 --- a/data/.exclude.files +++ /dev/null @@ -1,2 +0,0 @@ -/*.js.gz -/.exclude.files diff --git a/data/index.json b/data/index.json index c0f41268..6141ccdd 100644 --- a/data/index.json +++ b/data/index.json @@ -12,11 +12,6 @@ "title": "{{name}}", "class": "alert-default" }, - { - "type": "text", - "class": "alert alert-light", - "title": "
IoT Manager
" - }, { "type": "link", "title": "Конфигурация устройства", From 9a7f6c5b057ee5406ea5a31f6b71faf69f9f81f2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 13 Dec 2020 00:59:56 +0300 Subject: [PATCH 38/94] 4 mb version --- data/edit.htm.gz | Bin 0 -> 5455 bytes data/items/analog-adc.txt | 1 + data/items/bme280-hum.txt | 1 + data/items/bme280-press.txt | 1 + data/items/bme280-temp.txt | 1 + data/items/bmp280-press.txt | 1 + data/items/bmp280-temp.txt | 1 + data/items/button-in.txt | 1 + data/items/button-out.inv.txt | 1 + data/items/button-out.npin.txt | 1 + data/items/button-out.pin.txt | 1 + data/items/count-down.txt | 1 + data/items/dallas-temp.txt | 1 + data/items/dht11-hum.txt | 1 + data/items/dht11-temp.txt | 1 + data/items/dht22-hum.txt | 1 + data/items/dht22-temp.txt | 1 + data/items/impuls-out.txt | 1 + data/items/input-digit.txt | 1 + data/items/input-time.txt | 1 + data/items/logging.txt | 1 + data/items/modbus.txt | 1 + data/items/output-text.txt | 1 + data/items/pwm-out.txt | 1 + data/items/uart-button.txt | 1 + data/items/uart-widget.txt | 1 + data/items/ultrasonic-cm.txt | 1 + data/items/uptime.txt | 1 + data/presets/1.c.txt | 5 +++ data/presets/1.s.txt | 8 ++++ data/presets/2.c.txt | 12 ++++++ data/presets/2.s.txt | 18 +++++++++ data/presets/3.c.txt | 5 +++ data/presets/3.s.txt | 8 ++++ data/presets/4.c.txt | 4 ++ data/presets/4.s.txt | 8 ++++ data/presets/5.c.txt | 7 ++++ data/presets/5.s.txt | 16 ++++++++ data/presets/6.c.txt | 3 ++ data/presets/6.s.txt | 6 +++ data/presets/7.c.txt | 4 ++ data/presets/7.s.txt | 10 +++++ data/presets/8.c.txt | 4 ++ data/presets/8.s.txt | 7 ++++ data/presets/9.c.txt | 2 + data/presets/9.s.txt | 3 ++ include/Consts.h | 2 +- include/items/vSensorAnalog.h | 41 +++++++++++++++++++ src/ItemsList.cpp | 1 + src/UpgradeFirm.cpp | 4 +- src/items/vSensorAnalog.cpp | 71 +++++++++++++++++++++++++++++++++ 51 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 data/edit.htm.gz create mode 100644 data/items/analog-adc.txt create mode 100644 data/items/bme280-hum.txt create mode 100644 data/items/bme280-press.txt create mode 100644 data/items/bme280-temp.txt create mode 100644 data/items/bmp280-press.txt create mode 100644 data/items/bmp280-temp.txt create mode 100644 data/items/button-in.txt create mode 100644 data/items/button-out.inv.txt create mode 100644 data/items/button-out.npin.txt create mode 100644 data/items/button-out.pin.txt create mode 100644 data/items/count-down.txt create mode 100644 data/items/dallas-temp.txt create mode 100644 data/items/dht11-hum.txt create mode 100644 data/items/dht11-temp.txt create mode 100644 data/items/dht22-hum.txt create mode 100644 data/items/dht22-temp.txt create mode 100644 data/items/impuls-out.txt create mode 100644 data/items/input-digit.txt create mode 100644 data/items/input-time.txt create mode 100644 data/items/logging.txt create mode 100644 data/items/modbus.txt create mode 100644 data/items/output-text.txt create mode 100644 data/items/pwm-out.txt create mode 100644 data/items/uart-button.txt create mode 100644 data/items/uart-widget.txt create mode 100644 data/items/ultrasonic-cm.txt create mode 100644 data/items/uptime.txt create mode 100644 data/presets/1.c.txt create mode 100644 data/presets/1.s.txt create mode 100644 data/presets/2.c.txt create mode 100644 data/presets/2.s.txt create mode 100644 data/presets/3.c.txt create mode 100644 data/presets/3.s.txt create mode 100644 data/presets/4.c.txt create mode 100644 data/presets/4.s.txt create mode 100644 data/presets/5.c.txt create mode 100644 data/presets/5.s.txt create mode 100644 data/presets/6.c.txt create mode 100644 data/presets/6.s.txt create mode 100644 data/presets/7.c.txt create mode 100644 data/presets/7.s.txt create mode 100644 data/presets/8.c.txt create mode 100644 data/presets/8.s.txt create mode 100644 data/presets/9.c.txt create mode 100644 data/presets/9.s.txt create mode 100644 include/items/vSensorAnalog.h create mode 100644 src/items/vSensorAnalog.cpp diff --git a/data/edit.htm.gz b/data/edit.htm.gz new file mode 100644 index 0000000000000000000000000000000000000000..5786121f98a84b5695314c9ad8e5537f57650ba4 GIT binary patch literal 5455 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 z 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 inputU +button 0 +telegram нагрев#выключен 1 +end +temp < inputL +button 1 +telegram нагрев#включен 1 +end \ No newline at end of file diff --git a/data/presets/2.c.txt b/data/presets/2.c.txt new file mode 100644 index 00000000..d91fa5c7 --- /dev/null +++ b/data/presets/2.c.txt @@ -0,0 +1,12 @@ +0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[60] +0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] +0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 +0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] +0;inoutput;time1;inputTimeClock;Расписание;Утренний#период;8 +0;inoutput;threshold1;inputDigitTemp;Расписание;Температура;9 +0;inoutput;time2;inputTimeClock;Расписание;Дневной#период;10 +0;inoutput;threshold2;inputDigitTemp;Расписание;Температура;11 +0;inoutput;time3;inputTimeClock;Расписание;Вечерний#период;12 +0;inoutput;threshold3;inputDigitTemp;Расписание;Температура;13 +0;inoutput;time4;inputTimeClock;Расписание;Ночной#период;14 +0;inoutput;threshold4;inputDigitTemp;Расписание;Температура;15 \ No newline at end of file diff --git a/data/presets/2.s.txt b/data/presets/2.s.txt new file mode 100644 index 00000000..5a6f36f1 --- /dev/null +++ b/data/presets/2.s.txt @@ -0,0 +1,18 @@ +temp > threshold+-2 +heater 0 +end +temp < threshold+-2 +heater 1 +end +timenow = time1 +threshold threshold1 +end +timenow = time2 +threshold threshold2 +end +timenow = time3 +threshold threshold3 +end +timenow = time4 +threshold threshold4 +end \ No newline at end of file diff --git a/data/presets/3.c.txt b/data/presets/3.c.txt new file mode 100644 index 00000000..fb2c602b --- /dev/null +++ b/data/presets/3.c.txt @@ -0,0 +1,5 @@ +0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] +0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] +0;inoutput;inputU;inputDigit;Теплица;Верхний#порог;3 +0;inoutput;inputL;inputDigit;Теплица;Нижний#порог;4 +0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/data/presets/3.s.txt b/data/presets/3.s.txt new file mode 100644 index 00000000..f69cdc54 --- /dev/null +++ b/data/presets/3.s.txt @@ -0,0 +1,8 @@ +hum > inputU +button 0 +telegram полив#выключен 1 +end +hum < inputL +button 1 +telegram полив#включен 1 +end \ No newline at end of file diff --git a/data/presets/4.c.txt b/data/presets/4.c.txt new file mode 100644 index 00000000..3f1ccaa1 --- /dev/null +++ b/data/presets/4.c.txt @@ -0,0 +1,4 @@ +0;button-out;button1;toggle;Реле;Освещение;1;pin[12] +0;button-out;button2;toggle;Реле;Освещение;2;pin[13] +0;inoutput;T1;inputTime;Реле;Введите#время#включения;3 +0;inoutput;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/data/presets/4.s.txt b/data/presets/4.s.txt new file mode 100644 index 00000000..844d1c5b --- /dev/null +++ b/data/presets/4.s.txt @@ -0,0 +1,8 @@ +timenow = T1 +button1 1 +button2 0 +end +timenow = T2 +button1 0 +button2 1 +end \ No newline at end of file diff --git a/data/presets/5.c.txt b/data/presets/5.c.txt new file mode 100644 index 00000000..359a7a52 --- /dev/null +++ b/data/presets/5.c.txt @@ -0,0 +1,7 @@ +0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1 +0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12] +0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13] +0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] +0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] +0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] +0;inoutput;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/data/presets/5.s.txt b/data/presets/5.s.txt new file mode 100644 index 00000000..0424728a --- /dev/null +++ b/data/presets/5.s.txt @@ -0,0 +1,16 @@ +button-out-1 = 1 +button-out-2 1 +button-out-3 1 +button-out-4 1 +pwm-out-5 200 +pwm-out-6 800 +output-text-7 включено +end +button-out-1 = 0 +button-out-2 0 +button-out-3 0 +button-out-4 0 +pwm-out-5 800 +pwm-out-6 200 +output-text-7 выключено +end \ No newline at end of file diff --git a/data/presets/6.c.txt b/data/presets/6.c.txt new file mode 100644 index 00000000..c7fd096a --- /dev/null +++ b/data/presets/6.c.txt @@ -0,0 +1,3 @@ +0;button-out;button;toggle;Таймер;Освещение;1;pin[12] +0;count-down;count;anydata;Таймер;Обратный#отчет;2 +0;inoutput;input;inputDigit;Таймер;Введите#цифру;3 \ No newline at end of file diff --git a/data/presets/6.s.txt b/data/presets/6.s.txt new file mode 100644 index 00000000..06b70a4a --- /dev/null +++ b/data/presets/6.s.txt @@ -0,0 +1,6 @@ +button = 1 +count input +end +count = 0 +button 0 +end \ No newline at end of file diff --git a/data/presets/7.c.txt b/data/presets/7.c.txt new file mode 100644 index 00000000..984c6b8d --- /dev/null +++ b/data/presets/7.c.txt @@ -0,0 +1,4 @@ +0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 +0;inoutput;time;anydataTime;Сигнализация;Время:;2 +0;button-in;sensor;na;na;na;3;pin[0];db[20] +0;button-out;reset;toggle;Сигнализация;Сбросить;4 \ No newline at end of file diff --git a/data/presets/7.s.txt b/data/presets/7.s.txt new file mode 100644 index 00000000..32d28e78 --- /dev/null +++ b/data/presets/7.s.txt @@ -0,0 +1,10 @@ +sensor = 1 +text обнаружено +time %date% +telegram text обнаружено#движение 1 +end +reset = 1 +text не#обнаружено +time %date% +reset 0 +end \ No newline at end of file diff --git a/data/presets/8.c.txt b/data/presets/8.c.txt new file mode 100644 index 00000000..d18bdd0b --- /dev/null +++ b/data/presets/8.c.txt @@ -0,0 +1,4 @@ +0;button-in;sensor;na;na;na;1;pin[0];db[20] +0;button-out;light;toggle;Освещение;Освещение;2;pin[13] +0;count-down;count;anydata;Освещение;Обратный#отчет;3 +0;inoutput;period;inputDigit;Освещение;Период#включения;4 \ No newline at end of file diff --git a/data/presets/8.s.txt b/data/presets/8.s.txt new file mode 100644 index 00000000..10d55654 --- /dev/null +++ b/data/presets/8.s.txt @@ -0,0 +1,7 @@ +sensor = 1 +light 1 +count period +end +count = 0 +light 0 +end \ No newline at end of file diff --git a/data/presets/9.c.txt b/data/presets/9.c.txt new file mode 100644 index 00000000..502f3ae4 --- /dev/null +++ b/data/presets/9.c.txt @@ -0,0 +1,2 @@ +0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] +0;button-in;switch;na;na;na;2;pin[0];db[20] \ No newline at end of file diff --git a/data/presets/9.s.txt b/data/presets/9.s.txt new file mode 100644 index 00000000..6f6d7dcc --- /dev/null +++ b/data/presets/9.s.txt @@ -0,0 +1,3 @@ +switch = 1 +light change +end \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index ceeb1dd2..941df935 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,7 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 272 -#define FLASH_SIZE_1MB true +//#define FLASH_SIZE_1MB true #ifdef ESP8266 #ifdef FLASH_SIZE_1MB #define FIRMWARE_NAME "esp8266-1mb" diff --git a/include/items/vSensorAnalog.h b/include/items/vSensorAnalog.h new file mode 100644 index 00000000..b39c38f3 --- /dev/null +++ b/include/items/vSensorAnalog.h @@ -0,0 +1,41 @@ +#pragma once +#include "Global.h" +#include +#include "GyverFilters.h" + +class SensorAnalog; + +typedef std::vector MySensorAnalogVector; + +class SensorAnalog { +public: + + SensorAnalog(String key, unsigned long interval, unsigned int adcPin, int map1, int map2, int map3, int map4, float c); + ~SensorAnalog(); + + void loop(); + void readAnalog(); + +private: + + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + + unsigned long _interval; + + String _key; + unsigned int _adcPin; + + int _map1; + int _map2; + int _map3; + int _map4; + + float _c; + +}; + +extern MySensorAnalogVector* mySensorAnalog; + +extern void analogAdc(); \ No newline at end of file diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index acab5352..25e45bb6 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -18,6 +18,7 @@ void itemsListInit() { delChoosingItems(); }, nullptr); + #ifdef FLASH_SIZE_1MB myNotAsyncActions->add( do_addItem, [&](void*) { diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index 63e7168f..21e835eb 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -59,8 +59,8 @@ void upgrade_firmware(int type) { String devconfig_ForUpdate; String configSetup_ForUpdate; - scenario_ForUpdate = readFile(String(DEVICE_SCENARIO_FILE), 4000); - devconfig_ForUpdate = readFile(String(DEVICE_CONFIG_FILE), 4000); + scenario_ForUpdate = readFile(String(DEVICE_SCENARIO_FILE), 4096); + devconfig_ForUpdate = readFile(String(DEVICE_CONFIG_FILE), 4096); configSetup_ForUpdate = configSetupJson; if (type == 1) { //only build diff --git a/src/items/vSensorAnalog.cpp b/src/items/vSensorAnalog.cpp new file mode 100644 index 00000000..b449c120 --- /dev/null +++ b/src/items/vSensorAnalog.cpp @@ -0,0 +1,71 @@ +#include "items/vSensorAnalog.h" +#include "Class/LineParsing.h" +#include "Global.h" +#include "BufferExecute.h" +#include + +SensorAnalog::SensorAnalog(String key, unsigned long interval, unsigned int adcPin, int map1, int map2, int map3, int map4, float c) { + _interval = interval * 1000; + _key = key; + _adcPin = _adcPin; + + _map1 = map1; + _map2 = map2; + _map3 = map3; + _map4 = map4; + + _c = c; +} + +SensorAnalog::~SensorAnalog() {} + +void SensorAnalog::loop() { + currentMillis = millis(); + difference = currentMillis - prevMillis; + if (difference >= _interval) { + prevMillis = millis(); + readAnalog(); + } +} + +void SensorAnalog::readAnalog() { + int value; +#ifdef ESP32 + int pinInt = pin.toInt(); + value = analogRead(pinInt); +#endif +#ifdef ESP8266 + value = analogRead(A0); +#endif + + value = map(value, _map1, _map2, _map3, _map4); + float valueFloat = value * _c; + + eventGen2(_key, String(valueFloat)); + jsonWriteStr(configLiveJson, _key, String(valueFloat)); + publishStatus(_key, String(valueFloat)); + SerialPrint("I", "Sensor", "'" + _key + "' data: " + String(valueFloat)); +} + +MySensorAnalogVector* mySensorAnalog = nullptr; + +void analogAdc() { + myLineParsing.update(); + String interval = myLineParsing.gint(); + String pin = myLineParsing.gpin(); + String key = myLineParsing.gkey(); + String map = myLineParsing.gmap(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); + int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); + int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); + int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); + + static bool firstTime = true; + if (firstTime) mySensorAnalog = new MySensorAnalogVector(); + firstTime = false; + mySensorAnalog->push_back(SensorAnalog(key, interval.toInt(), pin.toInt(), map1, map2, map3, map4, c.toFloat())); +} + From 9fd88ffae1c27bac2db3eb59373b6da8c086287b Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 13 Dec 2020 01:06:08 +0300 Subject: [PATCH 39/94] 4mb build version --- include/items/SensorAnalogClass.h | 72 +++++++++++++++---------------- platformio.ini | 2 +- src/items/SensorAnalogClass.cpp | 42 +++++++++--------- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/include/items/SensorAnalogClass.h b/include/items/SensorAnalogClass.h index ccaba263..a4007eb4 100644 --- a/include/items/SensorAnalogClass.h +++ b/include/items/SensorAnalogClass.h @@ -1,36 +1,36 @@ -#pragma once -#include - -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" - -class SensorAnalogClass : public SensorConvertingClass { - public: - SensorAnalogClass() : SensorConvertingClass(){}; - - void SensorAnalogInit() { - jsonWriteStr(configOptionJson, _key + "_pin", _pin); - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - } - - int SensorAnalogRead(String key, String pin) { - int value; -#ifdef ESP32 - int pinInt = pin.toInt(); - value = analogRead(pinInt); -#endif -#ifdef ESP8266 - value = analogRead(A0); -#endif - value = this->mapping(key, value); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - return value; - } -}; -extern SensorAnalogClass mySensorAnalog; \ No newline at end of file +//#pragma once +//#include +// +//#include "Class/LineParsing.h" +//#include "Global.h" +//#include "items/SensorConvertingClass.h" +// +//class SensorAnalogClass : public SensorConvertingClass { +// public: +// SensorAnalogClass() : SensorConvertingClass(){}; +// +// void SensorAnalogInit() { +// jsonWriteStr(configOptionJson, _key + "_pin", _pin); +// jsonWriteStr(configOptionJson, _key + "_map", _map); +// jsonWriteStr(configOptionJson, _key + "_с", _c); +// } +// +// int SensorAnalogRead(String key, String pin) { +// int value; +//#ifdef ESP32 +// int pinInt = pin.toInt(); +// value = analogRead(pinInt); +//#endif +//#ifdef ESP8266 +// value = analogRead(A0); +//#endif +// value = this->mapping(key, value); +// float valueFl = this->correction(key, value); +// eventGen2(key, String(valueFl)); +// jsonWriteStr(configLiveJson, key, String(valueFl)); +// publishStatus(key, String(valueFl)); +// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); +// return value; +// } +//}; +//extern SensorAnalogClass mySensorAnalog; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 5040336a..b2e3463e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266_01_1m +default_envs = esp8266 ;============================================================================================================================================= [common_env_data] lib_deps_external = diff --git a/src/items/SensorAnalogClass.cpp b/src/items/SensorAnalogClass.cpp index 470d31c6..e8dcc9dd 100644 --- a/src/items/SensorAnalogClass.cpp +++ b/src/items/SensorAnalogClass.cpp @@ -1,21 +1,21 @@ -#include "BufferExecute.h" -#include "items/SensorAnalogClass.h" -#ifdef ANALOG_ENABLED -//==============================================Модуль аналогового сенсора=========================================================================================== -//=================================================================================================================================================================== -SensorAnalogClass mySensorAnalog; -void analogAdc() { - mySensorAnalog.update(); - String key = mySensorAnalog.gkey(); - sCmd.addCommand(key.c_str(), analogReading); - sensorReadingMap10sec += key + ","; - mySensorAnalog.SensorAnalogInit(); - mySensorAnalog.clear(); -} - -void analogReading() { - String key = sCmd.order(); - String pin = jsonReadStr(configOptionJson, key + "_pin"); - mySensorAnalog.SensorAnalogRead(key, pin); -} -#endif \ No newline at end of file +//#include "BufferExecute.h" +//#include "items/SensorAnalogClass.h" +//#ifdef ANALOG_ENABLED +////==============================================Модуль аналогового сенсора=========================================================================================== +////=================================================================================================================================================================== +//SensorAnalogClass mySensorAnalog; +//void analogAdc() { +// mySensorAnalog.update(); +// String key = mySensorAnalog.gkey(); +// sCmd.addCommand(key.c_str(), analogReading); +// sensorReadingMap10sec += key + ","; +// mySensorAnalog.SensorAnalogInit(); +// mySensorAnalog.clear(); +//} +// +//void analogReading() { +// String key = sCmd.order(); +// String pin = jsonReadStr(configOptionJson, key + "_pin"); +// mySensorAnalog.SensorAnalogRead(key, pin); +//} +//#endif \ No newline at end of file From b8a9f9b6b3fcd9875d55b4674551a3a401d2f337 Mon Sep 17 00:00:00 2001 From: Yuri Trikoz Date: Sun, 13 Dec 2020 02:23:04 +0300 Subject: [PATCH 40/94] pemz --- include/items/PZEMSensor.cpp | 309 +++++++++++++++++++++++++++++++++++ include/items/PZEMSensor.h | 51 ++++++ include/items/SensorPower.h | 32 ++++ src/items/SensorPower.cpp | 31 ++++ 4 files changed, 423 insertions(+) create mode 100644 include/items/PZEMSensor.cpp create mode 100644 include/items/PZEMSensor.h create mode 100644 include/items/SensorPower.h create mode 100644 src/items/SensorPower.cpp diff --git a/include/items/PZEMSensor.cpp b/include/items/PZEMSensor.cpp new file mode 100644 index 00000000..8a073df5 --- /dev/null +++ b/include/items/PZEMSensor.cpp @@ -0,0 +1,309 @@ +#include "PZEMSensor.h" + +#include + +#define REG_VOLTAGE 0x0000 +#define REG_CURRENT_L 0x0001 +#define REG_CURRENT_H 0X0002 +#define REG_POWER_L 0x0003 +#define REG_POWER_H 0x0004 +#define REG_ENERGY_L 0x0005 +#define REG_ENERGY_H 0x0006 +#define REG_FREQUENCY 0x0007 +#define REG_PF 0x0008 +#define REG_ALARM 0x0009 +#define CMD_RHR 0x03 +#define CMD_RIR 0X04 +#define CMD_WSR 0x06 +#define CMD_CAL 0x41 +#define CMD_REST 0x42 +#define WREG_ALARM_THR 0x0001 +#define WREG_ADDR 0x0002 +#define UPDATE_TIME 200 +#define RESPONSE_SIZE 32 +#define READ_TIMEOUT 100 +#define PZEM_BAUD_RATE 9600 + +#define DEBUG +// Debugging function; +void printBuf(uint8_t *buffer, uint16_t len) { +#ifdef DEBUG + for (uint16_t i = 0; i < len; i++) { + char temp[6]; + sprintf(temp, "%.2x ", buffer[i]); + Serial.print(temp); + } + Serial.println(); +#endif +} + +PZEMSensor::PZEMSensor(SoftwareSerial *port, uint16_t addr) { + _serial = port; + _addr = addr; +} + +PZEM_Info *PZEMSensor::values() { + // Update vales if necessary + if (!refresh()) { + _values = PZEM_Info(); + } + return &_values; +} + +/*! + * PZEM004Tv30::sendCmd8 + * + * Prepares the 8 byte command buffer and sends + * + * @param[in] cmd - Command to send (position 1) + * @param[in] rAddr - Register address (postion 2-3) + * @param[in] val - Register value to write (positon 4-5) + * @param[in] check - perform a simple read check after write + * + * @return success +*/ +bool PZEMSensor::sendCmd8(uint8_t cmd, uint16_t rAddr, uint16_t val, bool check, uint16_t slave_addr) { + uint8_t sendBuffer[8]; // Send buffer + uint8_t respBuffer[8]; // Response buffer (only used when check is true) + + if ((slave_addr == 0xFFFF) || + (slave_addr < 0x01) || + (slave_addr > 0xF7)) { + slave_addr = _addr; + } + + sendBuffer[0] = slave_addr; // Set slave address + sendBuffer[1] = cmd; // Set command + + sendBuffer[2] = (rAddr >> 8) & 0xFF; // Set high byte of register address + sendBuffer[3] = (rAddr)&0xFF; // Set low byte =//= + + sendBuffer[4] = (val >> 8) & 0xFF; // Set high byte of register value + sendBuffer[5] = (val)&0xFF; // Set low byte =//= + + setCRC(sendBuffer, 8); // Set CRC of frame + + _serial->write(sendBuffer, 8); // send frame + + if (check) { + if (!recieve(respBuffer, 8)) { // if check enabled, read the response + return false; + } + + // Check if response is same as send + for (uint8_t i = 0; i < 8; i++) { + if (sendBuffer[i] != respBuffer[i]) + return false; + } + } + return true; +} + +bool PZEMSensor::setAddress(uint8_t addr) { + if (addr < 0x01 || addr > 0xF7) // sanity check + return false; + + // Write the new address to the address register + if (!sendCmd8(CMD_WSR, WREG_ADDR, addr, true)) + return false; + + _addr = addr; // If successful, update the current slave address + + return true; +} + +uint8_t PZEMSensor::getAddress() { + return _addr; +} + +bool PZEMSensor::setPowerAlarm(uint16_t watts) { + if (watts > 25000) { // Sanitych check + watts = 25000; + } + + // Write the watts threshold to the Alarm register + if (!sendCmd8(CMD_WSR, WREG_ALARM_THR, watts, true)) + return false; + + return true; +} + +bool PZEMSensor::getPowerAlarm() { + if (!refresh()) // Update vales if necessary + return NAN; // Update did not work, return NAN + + return _values.alarms != 0x0000; +} + +void PZEMSensor::init() { + if (_addr < 0x01 || _addr > 0xF8) { + // Sanity check of address + _addr = PZEM_DEFAULT_ADDR; + } + // Set initial lastRed time so that we read right away + _lastRead = 0; + _lastRead -= UPDATE_TIME; +} + +bool PZEMSensor::refresh() { + static uint8_t response[25]; + if (_lastRead + UPDATE_TIME > millis()) { + return true; + } + // Read 10 registers starting at 0x00 (no check) + sendCmd8(CMD_RIR, 0x00, 0x0A, false); + if (recieve(response, 25) != 25) { // Something went wrong + return false; + } + // Update the current values + _values.voltage = ((uint32_t)response[3] << 8 | // Raw voltage in 0.1V + (uint32_t)response[4]) / + 10.0; + + _values.current = ((uint32_t)response[5] << 8 | // Raw current in 0.001A + (uint32_t)response[6] | + (uint32_t)response[7] << 24 | + (uint32_t)response[8] << 16) / + 1000.0; + + _values.power = ((uint32_t)response[9] << 8 | // Raw power in 0.1W + (uint32_t)response[10] | + (uint32_t)response[11] << 24 | + (uint32_t)response[12] << 16) / + 10.0; + + _values.energy = ((uint32_t)response[13] << 8 | // Raw Energy in 1Wh + (uint32_t)response[14] | + (uint32_t)response[15] << 24 | + (uint32_t)response[16] << 16) / + 1000.0; + + _values.freq = ((uint32_t)response[17] << 8 | // Raw Frequency in 0.1Hz + (uint32_t)response[18]) / + 10.0; + + _values.pf = ((uint32_t)response[19] << 8 | // Raw pf in 0.01 + (uint32_t)response[20]) / + 100.0; + + _values.alarms = ((uint32_t)response[21] << 8 | // Raw alarm value + (uint32_t)response[22]); + + // Record current time as _lastRead + _lastRead = millis(); + return true; +} + +bool PZEMSensor::reset() { + uint8_t buffer[] = {0x00, CMD_REST, 0x00, 0x00}; + uint8_t reply[5]; + buffer[0] = _addr; + + setCRC(buffer, 4); + _serial->write(buffer, 4); + + uint16_t length = recieve(reply, 5); + + if (length == 0 || length == 5) { + return false; + } + + return true; +} + +uint16_t PZEMSensor::recieve(uint8_t *resp, uint16_t len) { + ((SoftwareSerial *)_serial)->listen(); // Start software serial listen + unsigned long startTime = millis(); // Start time for Timeout + uint8_t index = 0; // Bytes we have read + while ((index < len) && (millis() - startTime < READ_TIMEOUT)) { + if (_serial->available() > 0) { + uint8_t c = (uint8_t)_serial->read(); + resp[index++] = c; + } + } + // Check CRC with the number of bytes read + if (!checkCRC(resp, index)) { + return 0; + } + return index; +} + +bool PZEMSensor::checkCRC(const uint8_t *buf, uint16_t len) { + if (len <= 2) // Sanity check + return false; + + uint16_t crc = CRC16(buf, len - 2); // Compute CRC of data + return ((uint16_t)buf[len - 2] | (uint16_t)buf[len - 1] << 8) == crc; +} + +void PZEMSensor::setCRC(uint8_t *buf, uint16_t len) { + if (len <= 2) // Sanity check + return; + + uint16_t crc = CRC16(buf, len - 2); // CRC of data + + // Write high and low byte to last two positions + buf[len - 2] = crc & 0xFF; // Low byte first + buf[len - 1] = (crc >> 8) & 0xFF; // High byte second +} + +// Pre computed CRC table +static const uint16_t crcTable[] PROGMEM = { + 0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, + 0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, + 0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, + 0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, + 0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, + 0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, + 0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, + 0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, + 0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, + 0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, + 0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, + 0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, + 0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, + 0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, + 0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, + 0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, + 0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, + 0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, + 0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, + 0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, + 0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, + 0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, + 0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, + 0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, + 0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, + 0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, + 0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, + 0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, + 0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, + 0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, + 0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, + 0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040}; + +uint16_t PZEMSensor::CRC16(const uint8_t *data, uint16_t len) { + uint8_t nTemp; // CRC table index + uint16_t crc = 0xFFFF; // Default value + + while (len--) { + nTemp = *data++ ^ crc; + crc >>= 8; + crc ^= (uint16_t)pgm_read_word(&crcTable[nTemp]); + } + return crc; +} + +void PZEMSensor::search() { + static uint8_t response[7]; + for (uint16_t addr = 0x01; addr <= 0xF8; addr++) { + sendCmd8(CMD_RIR, 0x00, 0x01, false, addr); + if (recieve(response, 7) != 7) { + // Something went wrong + continue; + } else { + Serial.print("Device on addr: "); + Serial.print(addr); + } + } +} diff --git a/include/items/PZEMSensor.h b/include/items/PZEMSensor.h new file mode 100644 index 00000000..55650e84 --- /dev/null +++ b/include/items/PZEMSensor.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include + +#define PZEM_DEFAULT_ADDR 0xF8 + +struct PZEM_Info { + float voltage; + float current; + float power; + float energy; + float freq; + float pf; + uint16_t alarms; + PZEM_Info() : voltage{0}, current{0}, power{0}, energy{0}, freq{0}, pf{0}, alarms{0} {}; +}; + +class PZEMSensor { + public: + PZEMSensor(SoftwareSerial *serial, uint16_t addr = PZEM_DEFAULT_ADDR); + + ~PZEMSensor(); + PZEM_Info* values(); + bool setAddress(uint8_t addr); + uint8_t getAddress(); + bool setPowerAlarm(uint16_t watts); + bool getPowerAlarm(); + bool reset(); + void search(); + // Get most up to date values from device registers and cache them + bool refresh(); + + private: + void init(void); + + private: + PZEM_Info _values; // Measured values + Stream *_serial; // Serial interface + bool _isSoft; // Is serial interface software + uint8_t _addr; // Device address + uint64_t _lastRead; // Last time values were updated + + void init(uint8_t addr); // Init common to all constructors + uint16_t recieve(uint8_t *resp, uint16_t len); // Receive len bytes into a buffer + bool sendCmd8(uint8_t cmd, uint16_t rAddr, uint16_t val, bool check = false, uint16_t slave_addr = 0xFFFF); // Send 8 byte command + void setCRC(uint8_t *buf, uint16_t len); // Set the CRC for a buffer + bool checkCRC(const uint8_t *buf, uint16_t len); // Check CRC of buffer + uint16_t CRC16(const uint8_t *data, uint16_t len); // Calculate CRC of buffer +}; diff --git a/include/items/SensorPower.h b/include/items/SensorPower.h new file mode 100644 index 00000000..ed88b33e --- /dev/null +++ b/include/items/SensorPower.h @@ -0,0 +1,32 @@ +#pragma once +#include "Global.h" + +#include "PZEMSensor.h" + +class SensorPZEM { + public: + SensorPZEM(SoftwareSerial* serial, uint32_t addr, uint32_t interval, String key) : _lastUpdate{0}, + _interval{interval}, + _key{key} { + _pzem = new PZEMSensor(serial, addr); + } + + ~SensorPZEM() { + delete _pzem; + } + + void loop(void); + + private: + void readSensor(void); + String getDataKey(const char* param_key); + void post(const char* key, const String& value); + + private: + PZEMSensor* _pzem; + uint32_t _lastUpdate; + uint32_t _interval; + String _key; +}; + +extern SensorPZEM* myPowerSensor; \ No newline at end of file diff --git a/src/items/SensorPower.cpp b/src/items/SensorPower.cpp new file mode 100644 index 00000000..4fae95fe --- /dev/null +++ b/src/items/SensorPower.cpp @@ -0,0 +1,31 @@ +#include "items/SensorPower.h" + +SensorPZEM* myPowerSensor; + +void SensorPZEM::loop() { + uint32_t now = millis(); + if ((_lastUpdate + _interval) < now) { + post("voltage", String(_pzem->values()->voltage, 2)); + post("current", String(_pzem->values()->current, 2)); + post("power", String(_pzem->values()->power, 2)); + post("energy", String(_pzem->values()->energy, 2)); + post("freq", String(_pzem->values()->freq, 0)); + post("pf", String(_pzem->values()->pf, 2)); + _lastUpdate = now; + } +} + +String SensorPZEM::getDataKey(const char* param_key) { + String res = _key; + res += "_"; + res += param_key; + return res; +} + +void SensorPZEM::post(const char* key, const String& value) { + String dataKey = getDataKey(key); + eventGen2(dataKey, value); + jsonWriteStr(configLiveJson, dataKey, value); + publishStatus(dataKey, value); + SerialPrint("I", "Sensor", "'" + dataKey + "' data: " + value); +} From 9934690d0a2d48f8fd0fc4906a99dff293e04eb5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Tue, 15 Dec 2020 22:10:40 +0100 Subject: [PATCH 41/94] some --- data/config.json | 6 +++--- include/Consts.h | 5 ++++- include/FSEditor.h | 2 +- include/Utils/FileHelper.h | 2 +- include/Utils/FileUtils.h | 2 +- platformio.ini | 4 +++- src/Clock.cpp | 2 +- src/Telegram.cpp | 4 +++- src/Utils/TimeUtils.cpp | 3 ++- 9 files changed, 19 insertions(+), 11 deletions(-) diff --git a/data/config.json b/data/config.json index 710b94ac..84768aa7 100644 --- a/data/config.json +++ b/data/config.json @@ -7,11 +7,11 @@ "routerpass": "hostel3333", "timezone": 1, "ntp": "pool.ntp.org", - "mqttServer": "wqtt.ru", - "mqttPort": 8021, + "mqttServer": "91.204.228.124", + "mqttPort": 1883, "mqttPrefix": "/iotTest", "mqttUser": "rise", - "mqttPass": "hostel3333", + "mqttPass": "23ri22se32", "scen": "1", "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", diff --git a/include/Consts.h b/include/Consts.h index 941df935..698d0d00 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -15,7 +15,10 @@ #define FIRMWARE_NAME "esp32" #endif -//===========FSystem============================================================================================================================================== +//===========FileSystem============================================================================================================================================== +#define littlefs_on + +//================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN 2 diff --git a/include/FSEditor.h b/include/FSEditor.h index 69282648..16efe998 100644 --- a/include/FSEditor.h +++ b/include/FSEditor.h @@ -1,7 +1,7 @@ #pragma once #include -#include +//#include #ifdef ESP8266 #include diff --git a/include/Utils/FileHelper.h b/include/Utils/FileHelper.h index daa9186c..542ed423 100644 --- a/include/Utils/FileHelper.h +++ b/include/Utils/FileHelper.h @@ -2,7 +2,7 @@ #include -#include "FS.h" +//#include "FS.h" #ifdef ESP32 #include "LITTLEFS.h" diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h index da2688ac..d630fc39 100644 --- a/include/Utils/FileUtils.h +++ b/include/Utils/FileUtils.h @@ -4,7 +4,7 @@ #include "Consts.h" -#include "FS.h" +//#include "FS.h" #ifdef ESP32 #include "LITTLEFS.h" diff --git a/platformio.ini b/platformio.ini index b2e3463e..5cb44ff7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,10 +47,12 @@ lib_deps = ESPAsyncUDP EspSoftwareSerial CTBot + SPIFFS monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 -board_build.filesystem = littlefs +;board_build.filesystem = littlefs +board_build.filesystem = SPIFFS ;============================================================================================================================================= [env:esp8266] framework = arduino diff --git a/src/Clock.cpp b/src/Clock.cpp index 2ae4132a..0cb26942 100644 --- a/src/Clock.cpp +++ b/src/Clock.cpp @@ -12,5 +12,5 @@ void clockInit() { timeNow->hasSync(); }, nullptr, true); - SerialPrint("I", F("Time"), F("Clock Init")); + SerialPrint("I", F("NTP"), F("Clock Init")); } diff --git a/src/Telegram.cpp b/src/Telegram.cpp index 6a5237ca..679ba5c7 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -76,7 +76,9 @@ void sendTelegramMsg() { static String prevMsg; if (prevMsg != msg) { prevMsg = msg; - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); + if (msg != "na") { + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); + } SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); } } diff --git a/src/Utils/TimeUtils.cpp b/src/Utils/TimeUtils.cpp index b377965a..6d2dcdee 100644 --- a/src/Utils/TimeUtils.cpp +++ b/src/Utils/TimeUtils.cpp @@ -228,8 +228,9 @@ void timeInit() { prevTime = timenow; jsonWriteStr(configLiveJson, "timenow", timenow); eventGen2("timenow", timenow); + SerialPrint("I", F("NTP"), timenow); } }, nullptr, true); - SerialPrint("I", F("Time"), F("Handle time init")); + SerialPrint("I", F("NTP"), F("Handle time init")); } From 078c4389b584c8e89c1e23d29b0f5933bef5dcfd Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 16 Dec 2020 13:59:01 +0100 Subject: [PATCH 42/94] 1 mb compiling version --- include/Clock.h | 2 + include/Consts.h | 36 ++++------------ include/FSEditor.h | 2 - include/SoftUART.h | 5 ++- include/Telegram.h | 4 +- include/Utils/FileHelper.h | 3 -- include/Utils/FileUtils.h | 4 -- include/Utils/TimeUtils.h | 16 +++++++ include/items/SensorBme280Class.h | 6 ++- include/items/SensorBmp280Class.h | 6 ++- include/items/SensorDhtClass.h | 6 ++- include/items/SensorModbusClass.h | 8 +++- include/items/vPwmOut.h | 4 +- platformio.ini | 9 ++-- src/BufferExecute.cpp | 13 +++++- src/Init.cpp | 2 + src/ItemsList.cpp | 41 +----------------- src/MqttClient.cpp | 4 +- src/SoftUART.cpp | 8 ++-- src/Telegram.cpp | 6 +-- src/Web.cpp | 70 +++++++++++++++---------------- src/items/SensorBme280Class.cpp | 6 +-- src/items/SensorBmp280Class.cpp | 6 +-- src/items/SensorDhtClass.cpp | 6 +-- src/items/SensorModbusClass.cpp | 46 ++++++++++---------- src/items/vButtonOut.cpp | 6 ++- src/items/vPwmOut.cpp | 9 +++- src/main.cpp | 8 ++++ 28 files changed, 168 insertions(+), 174 deletions(-) diff --git a/include/Clock.h b/include/Clock.h index fdb55cbe..83ce72dc 100644 --- a/include/Clock.h +++ b/include/Clock.h @@ -11,6 +11,8 @@ extern void clockInit(); #include "sntp.h" #endif + + class Clock { private: Time_t _time_local; diff --git a/include/Consts.h b/include/Consts.h index 698d0d00..d8a40cfb 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -42,37 +42,19 @@ //#define SSDP_ENABLED //=========Sensors enable/disable================================================================================================================================= -#define LEVEL_ENABLED -#define ANALOG_ENABLED -#define DALLAS_ENABLED -#define DHT_ENABLED -#define BMP_ENABLED -#define BME_ENABLED +#define SensorBme280Enabled +#define SensorBmp280Enabled +#define SensorDhtEnabled +#define PwmOutEnable +//#define SensorModbusEnabled + +//=========others================================================================================================================================= +//#define telegram +//#define uartEnable -//=========Gears enable/disable=================================================================================================================================== -#define STEPPER_ENABLED -#define SERVO_ENABLED -//========Other enable/disable==================================================================================================================================== -#define LOGGING_ENABLED -#define SERIAL_ENABLED -#define PUSH_ENABLED - -struct Time_t { - uint8_t second; - uint8_t minute; - uint8_t hour; - uint8_t day_of_week; - uint8_t day_of_month; - uint8_t month; - uint16_t day_of_year; - uint16_t year; - unsigned long days; - unsigned long valid; -}; - //================================================================================================================================================================ enum TimerTask_t { WIFI_SCAN, WIFI_MQTT_CONNECTION_CHECK, diff --git a/include/FSEditor.h b/include/FSEditor.h index 16efe998..c037d008 100644 --- a/include/FSEditor.h +++ b/include/FSEditor.h @@ -1,8 +1,6 @@ #pragma once - #include //#include - #ifdef ESP8266 #include #endif diff --git a/include/SoftUART.h b/include/SoftUART.h index 7af320cc..a566e06a 100644 --- a/include/SoftUART.h +++ b/include/SoftUART.h @@ -1,9 +1,10 @@ #pragma once - +#ifdef uartEnable #include "SoftwareSerial.h" extern SoftwareSerial* myUART; extern void uartInit(); extern void uartHandle(); -extern void parse(String& incStr); \ No newline at end of file +extern void parse(String& incStr); +#endif \ No newline at end of file diff --git a/include/Telegram.h b/include/Telegram.h index 0571fd40..6b04b878 100644 --- a/include/Telegram.h +++ b/include/Telegram.h @@ -1,4 +1,5 @@ #pragma once +#ifdef telegram #include "Global.h" extern void sendTelegramMsg(); @@ -7,4 +8,5 @@ extern void handleTelegram(); extern bool isTelegramEnabled(); extern bool isTelegramInputOn(); extern void telegramMsgParse(String msg); -extern String returnListOfParams(); \ No newline at end of file +extern String returnListOfParams(); +#endif \ No newline at end of file diff --git a/include/Utils/FileHelper.h b/include/Utils/FileHelper.h index 542ed423..d2691706 100644 --- a/include/Utils/FileHelper.h +++ b/include/Utils/FileHelper.h @@ -1,9 +1,6 @@ #pragma once - #include - //#include "FS.h" - #ifdef ESP32 #include "LITTLEFS.h" #define LittleFS LITTLEFS diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h index d630fc39..cd51532f 100644 --- a/include/Utils/FileUtils.h +++ b/include/Utils/FileUtils.h @@ -1,11 +1,7 @@ #pragma once - #include - #include "Consts.h" - //#include "FS.h" - #ifdef ESP32 #include "LITTLEFS.h" #define LittleFS LITTLEFS diff --git a/include/Utils/TimeUtils.h b/include/Utils/TimeUtils.h index 60bf405f..8b4c0739 100644 --- a/include/Utils/TimeUtils.h +++ b/include/Utils/TimeUtils.h @@ -48,6 +48,22 @@ int getOffsetInMinutes(int timezone); /* * Разбивает время на составляющие */ + +struct Time_t { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day_of_week; + uint8_t day_of_month; + uint8_t month; + uint16_t day_of_year; + uint16_t year; + unsigned long days; + unsigned long valid; +}; + void breakEpochToTime(unsigned long epoch, Time_t& tm); + + void timeInit(); \ No newline at end of file diff --git a/include/items/SensorBme280Class.h b/include/items/SensorBme280Class.h index 42b7d532..d3156418 100644 --- a/include/items/SensorBme280Class.h +++ b/include/items/SensorBme280Class.h @@ -1,6 +1,7 @@ #pragma once +#include "Consts.h" +#ifdef SensorBme280Enabled #include - #include "Class/LineParsing.h" #include "Global.h" #include "items/SensorConvertingClass.h" @@ -52,4 +53,5 @@ class SensorBme280Class : public SensorConvertingClass { SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); } }; -extern SensorBme280Class mySensorBme280; \ No newline at end of file +extern SensorBme280Class mySensorBme280; +#endif \ No newline at end of file diff --git a/include/items/SensorBmp280Class.h b/include/items/SensorBmp280Class.h index 934245a1..d775cc87 100644 --- a/include/items/SensorBmp280Class.h +++ b/include/items/SensorBmp280Class.h @@ -1,6 +1,7 @@ #pragma once +#include "Consts.h" +#ifdef SensorBmp280Enabled #include - #include "Class/LineParsing.h" #include "Global.h" #include "items/SensorConvertingClass.h" @@ -45,4 +46,5 @@ class SensorBmp280Class : public SensorConvertingClass { SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); } }; -extern SensorBmp280Class mySensorBmp280; \ No newline at end of file +extern SensorBmp280Class mySensorBmp280; +#endif \ No newline at end of file diff --git a/include/items/SensorDhtClass.h b/include/items/SensorDhtClass.h index 6eb4433b..2242ed4e 100644 --- a/include/items/SensorDhtClass.h +++ b/include/items/SensorDhtClass.h @@ -1,6 +1,7 @@ #pragma once +#include "Consts.h" +#ifdef SensorDhtEnabled #include - #include "Class/LineParsing.h" #include "Global.h" #include "items/SensorConvertingClass.h" @@ -75,4 +76,5 @@ class SensorDhtClass : public SensorConvertingClass { } } }; -extern SensorDhtClass mySensorDht; \ No newline at end of file +extern SensorDhtClass mySensorDht; +#endif \ No newline at end of file diff --git a/include/items/SensorModbusClass.h b/include/items/SensorModbusClass.h index 494540ea..9f13e166 100644 --- a/include/items/SensorModbusClass.h +++ b/include/items/SensorModbusClass.h @@ -1,9 +1,9 @@ -#ifdef modbus #pragma once +#include "Consts.h" +#ifdef SensorModbusEnabled #include #include #include - #include "Class/LineParsing.h" #include "Global.h" #include "items/SensorConvertingClass.h" @@ -82,4 +82,8 @@ class SensorModbusClass : public SensorConvertingClass { } }; extern SensorModbusClass mySensorModbus; + +extern void modbus(); +extern void modbusReading(); + #endif \ No newline at end of file diff --git a/include/items/vPwmOut.h b/include/items/vPwmOut.h index 2b9ba669..0c27d196 100644 --- a/include/items/vPwmOut.h +++ b/include/items/vPwmOut.h @@ -1,6 +1,7 @@ #pragma once +#include "Consts.h" +#ifdef PwmOutEnable #include - #include "Global.h" class PwmOut; @@ -27,3 +28,4 @@ extern MyPwmOutVector* myPwmOut; extern void pwmOut(); extern void pwmOutExecute(); +#endif diff --git a/platformio.ini b/platformio.ini index 5cb44ff7..98ed5b66 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266 +default_envs = esp8266_01_1m ;============================================================================================================================================= [common_env_data] lib_deps_external = @@ -38,7 +38,8 @@ framework = arduino ;board = esp01_1m ;board = esp12e board = nodemcuv2 -board_build.ldscript = eagle.flash.1m256.ld +;board_build.ldscript = eagle.flash.1m256.ld +board_build.ldscript = eagle.flash.1m512.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} @@ -47,12 +48,10 @@ lib_deps = ESPAsyncUDP EspSoftwareSerial CTBot - SPIFFS monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 -;board_build.filesystem = littlefs -board_build.filesystem = SPIFFS +board_build.filesystem = littlefs ;============================================================================================================================================= [env:esp8266] framework = arduino diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 4af187a8..1bff0d34 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -9,6 +9,7 @@ #include "items/vLogging.h" #include "items/vImpulsOut.h" #include "items/vCountDown.h" +#include "items/SensorModbusClass.h" void loopCmdAdd(const String& cmdStr) { orderBuf += cmdStr; @@ -41,9 +42,11 @@ void csvCmdExecute(String& cmdStr) { if (order == F("button-out")) { sCmd.addCommand(order.c_str(), buttonOut); } +#ifdef PwmOutEnable else if (order == F("pwm-out")) { sCmd.addCommand(order.c_str(), pwmOut); } +#endif else if (order == F("button-in")) { sCmd.addCommand(order.c_str(), buttonIn); } @@ -59,12 +62,15 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("dallas-temp")) { sCmd.addCommand(order.c_str(), dallas); } +#ifdef SensorDhtEnabled else if (order == F("dht-temp")) { sCmd.addCommand(order.c_str(), dhtTemp); } else if (order == F("dht-hum")) { sCmd.addCommand(order.c_str(), dhtHum); } +#endif +#ifdef SensorBme280Enabled else if (order == F("bme280-temp")) { sCmd.addCommand(order.c_str(), bme280Temp); } @@ -74,15 +80,20 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("bme280-press")) { sCmd.addCommand(order.c_str(), bme280Press); } +#endif +#ifdef SensorBmp280Enabled else if (order == F("bmp280-temp")) { sCmd.addCommand(order.c_str(), bmp280Temp); } else if (order == F("bmp280-press")) { sCmd.addCommand(order.c_str(), bmp280Press); } +#endif +#ifdef SensorModbusEnabled else if (order == F("modbus")) { - //sCmd.addCommand(order.c_str(), modbus); + sCmd.addCommand(order.c_str(), modbus); } +#endif else if (order == F("uptime")) { sCmd.addCommand(order.c_str(), sysUptime); } diff --git a/src/Init.cpp b/src/Init.cpp index 25885189..fc0fa3ae 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -76,11 +76,13 @@ void deviceInit() { inOutput_KeyList = ""; inOutput_EnterCounter = -1; //======clear pwm params======= + #ifdef PwmOutEnable if (myPwmOut != nullptr) { myPwmOut->clear(); } pwmOut_KeyList = ""; pwmOut_EnterCounter = -1; + #endif //=================================== if (myCountDown != nullptr) { myCountDown->clear(); diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 25e45bb6..13937acf 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -19,34 +19,13 @@ void itemsListInit() { }, nullptr); -#ifdef FLASH_SIZE_1MB - myNotAsyncActions->add( - do_addItem, [&](void*) { - addItem(itemName); - itemName = ""; - }, - nullptr); - myNotAsyncActions->add( - do_addPreset, [&](void*) { - addPreset(presetName); - presetName = ""; - }, - nullptr); -#endif SerialPrint("I", F("Items"), F("Items Init")); } void addItem(String name) { -#ifdef FLASH_SIZE_1MB - String url = serverIP + F("/projects/iotmanager/config/items/") + name + ".txt"; - String item = getURL(url); - Serial.println(url); - if (item == "error") return; -#endif -#ifndef FLASH_SIZE_1MB + String item = readFile("items/" + name + ".txt", 1024); -#endif name = selectToMarker(name, "-"); @@ -81,34 +60,16 @@ void addItem(String name) { } void addPreset(String name) { -#ifdef FLASH_SIZE_1MB - String url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; - String preset = getURL(url); - Serial.println(url); - if (preset == "error") return; -#endif -#ifndef FLASH_SIZE_1MB String preset = readFile("presets/" + name + ".txt", 4048); -#endif - addFile(DEVICE_CONFIG_FILE, "\n" + preset); Serial.println(preset); name.replace(".c", ".s"); -#ifdef FLASH_SIZE_1MB - url = serverIP + F("/projects/iotmanager/config/presets/") + name + ".txt"; - String scenario = getURL(url); - Serial.println(url); - if (scenario == "error") return; -#endif -#ifndef FLASH_SIZE_1MB String scenario = readFile("presets/" + name + ".txt", 4048); -#endif removeFile(DEVICE_SCENARIO_FILE); - addFile(DEVICE_SCENARIO_FILE, scenario); loadScenario(); Serial.println(scenario); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index d5ce4107..951d16c4 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -124,10 +124,8 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { SerialPrint("I", "MQTT", "Full update"); publishWidgets(); publishState(); -#ifdef LOGGING_ENABLED - choose_log_date_and_send(); -#endif + choose_log_date_and_send(); } else if (topicStr.indexOf("control") != -1) { diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 91ba781d..8a7dc57e 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -1,3 +1,4 @@ +#ifdef uartEnable #include "SoftUART.h" #include "Global.h" @@ -8,7 +9,7 @@ HardwareSerial* myUART = nullptr; #endif void uartInit() { - if (!jsonReadBool(configSetupJson, "uart")) { + if (!jsonReadBool(configSetupJson, "uartEnable")) { return; } if (!myUART) { @@ -25,7 +26,7 @@ void uartInit() { void uartHandle() { if (myUART) { - if (!jsonReadBool(configSetupJson, "uart")) { + if (!jsonReadBool(configSetupJson, "uartEnable")) { return; } static String incStr; @@ -50,4 +51,5 @@ void parse(String& incStr) { orderBuf += incStr; SerialPrint("I", "=>UART", incStr); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Telegram.cpp b/src/Telegram.cpp index 679ba5c7..2ab73d32 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -1,6 +1,5 @@ +#ifdef telegram #include "Telegram.h" - - CTBot* myBot{ nullptr }; void telegramInit() { @@ -119,4 +118,5 @@ String returnListOfParams() { cmdStr = deleteBeforeDelimiter(cmdStr, "\n"); } return out; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index 6be0c2cc..c673a79a 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -20,24 +20,12 @@ void web_init() { server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { //==============================set.device.json==================================================================================================== if (request->hasArg("addItem")) { -#ifdef FLASH_SIZE_1MB - itemName = request->getParam("addItem")->value(); - myNotAsyncActions->make(do_addItem); -#endif -#ifndef FLASH_SIZE_1MB addItem(request->getParam("addItem")->value()); -#endif request->redirect("/?set.device"); } if (request->hasArg("addPreset")) { -#ifdef FLASH_SIZE_1MB - presetName = request->getParam("addPreset")->value(); - myNotAsyncActions->make(do_addPreset); -#endif -#ifndef FLASH_SIZE_1MB addPreset(request->getParam("addPreset")->value()); -#endif jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); request->redirect("/?set.device"); } @@ -109,12 +97,11 @@ void web_init() { request->send(200); } -#ifdef LOGGING_ENABLED + if (request->hasArg("cleanlog")) { cleanLogAndData(); request->send(200); } -#endif //==============================wifi settings============================================= if (request->hasArg("devname")) { @@ -279,29 +266,37 @@ void web_init() { myNotAsyncActions->make(do_BUSSCAN); request->redirect("/?set.utilities"); } - if (request->hasArg("uart")) { - bool value = request->getParam("uart")->value().toInt(); - jsonWriteBool(configSetupJson, "uart", value); + if (request->hasArg("uartEnable")) { + bool value = request->getParam("uartEnable")->value().toInt(); + jsonWriteBool(configSetupJson, "uartEnable", value); saveConfig(); +#ifdef uartEnable uartInit(); +#endif request->send(200); } if (request->hasArg("uartS")) { jsonWriteStr(configSetupJson, "uartS", request->getParam("uartS")->value()); saveConfig(); +#ifdef uartEnable uartInit(); +#endif request->send(200); } if (request->hasArg("uartTX")) { jsonWriteStr(configSetupJson, "uartTX", request->getParam("uartTX")->value()); saveConfig(); +#ifdef uartEnable uartInit(); +#endif request->send(200); } if (request->hasArg("uartRX")) { jsonWriteStr(configSetupJson, "uartRX", request->getParam("uartRX")->value()); saveConfig(); +#ifdef uartEnable uartInit(); +#endif request->send(200); } @@ -335,26 +330,31 @@ void web_init() { SerialPrint("I", "Update", "firmware version: " + String(lastVersion)); String msg = ""; - //if (FLASH_SIZE_1MB) { - // msg = F("Обновление невозможно, память устройства 1 мб"); - //} - //else { - if (lastVersion == FIRMWARE_VERSION) { - msg = F("Актуальная версия прошивки уже установлена."); +#ifdef FLASH_SIZE_1MB + if (FLASH_SIZE_1MB) { + msg = F("Обновление невозможно, память устройства 1 мб"); } - else if (lastVersion > FIRMWARE_VERSION) { - msg = F("Новая версия прошивкиИдет обновление прошивки, после обновления страница перезагрузится автоматически...')\">Установить"); + else { +#endif + + if (lastVersion == FIRMWARE_VERSION) { + msg = F("Актуальная версия прошивки уже установлена."); + } + else if (lastVersion > FIRMWARE_VERSION) { + msg = F("Новая версия прошивкиИдет обновление прошивки, после обновления страница перезагрузится автоматически...')\">Установить"); + } + else if (lastVersion == -1) { + msg = F("Cервер не найден. Попробуйте повторить позже..."); + } + else if (lastVersion == -2) { + msg = F("Устройство не подключено к роутеру!"); + } + else if (lastVersion < FIRMWARE_VERSION) { + msg = F("Ошибка версии. Попробуйте повторить позже..."); + } +#ifdef FLASH_SIZE_1MB } - else if (lastVersion == -1) { - msg = F("Cервер не найден. Попробуйте повторить позже..."); - } - else if (lastVersion == -2) { - msg = F("Устройство не подключено к роутеру!"); - } - else if (lastVersion < FIRMWARE_VERSION) { - msg = F("Ошибка версии. Попробуйте повторить позже..."); - } - //} +#endif // else if (lastVersion == "") { //msg = F("Нажмите на кнопку \"обновить прошивку\" повторно..."); diff --git a/src/items/SensorBme280Class.cpp b/src/items/SensorBme280Class.cpp index 21a2f476..086c44f4 100644 --- a/src/items/SensorBme280Class.cpp +++ b/src/items/SensorBme280Class.cpp @@ -1,7 +1,7 @@ +#include "Consts.h" +#ifdef SensorBme280Enabled #include "items/SensorBme280Class.h" - #include "BufferExecute.h" -//#ifdef SensorBme280Enabled //=========================================Модуль ультрозвукового дальномера================================================================== //bme280-temp;id;anydata;Сенсоры;Температура;order;c[1] //bme280-hum;id;anydata;Сенсоры;Температура;order;c[1] @@ -44,4 +44,4 @@ void bme280ReadingPress() { String key = sCmd.order(); mySensorBme280.SensorBme280ReadPress(key); } -//#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/items/SensorBmp280Class.cpp b/src/items/SensorBmp280Class.cpp index e6a2e7ed..f8e5b06f 100644 --- a/src/items/SensorBmp280Class.cpp +++ b/src/items/SensorBmp280Class.cpp @@ -1,7 +1,7 @@ +#include "Consts.h" +#ifdef SensorBmp280Enabled #include "items/SensorBmp280Class.h" - #include "BufferExecute.h" -//#ifdef SensorBmp280Enabled //=========================================Модуль ультрозвукового дальномера================================================================== //bmp280-temp;id;anydata;Сенсоры;Температура;order;c[1] //bmp280-hum;id;anydata;Сенсоры;Температура;order;c[1] @@ -32,4 +32,4 @@ void bmp280ReadingPress() { String key = sCmd.order(); mySensorBmp280.SensorBmp280ReadPress(key); } -//#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/items/SensorDhtClass.cpp b/src/items/SensorDhtClass.cpp index 035a4eb2..d0c2e41c 100644 --- a/src/items/SensorDhtClass.cpp +++ b/src/items/SensorDhtClass.cpp @@ -1,7 +1,7 @@ +#include "Consts.h" +#ifdef SensorDhtEnabled #include "items/SensorDhtClass.h" - #include "BufferExecute.h" -//#ifdef SensorDhtEnabled //=========================================DHT Sensor================================================================== //dht-temp;id;anydata;Сенсоры;Температура;order;pin;type[dht11];c[1] //dht-hum;id;anydata;Сенсоры;Влажность;order;pin;type[dht11];c[1] @@ -32,4 +32,4 @@ void dhtReadingHum() { String key = sCmd.order(); mySensorDht.SensorDhtReadHum(key); } -//#endif \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/items/SensorModbusClass.cpp b/src/items/SensorModbusClass.cpp index 368fe916..a4e84157 100644 --- a/src/items/SensorModbusClass.cpp +++ b/src/items/SensorModbusClass.cpp @@ -1,23 +1,23 @@ -//#include "items/SensorModbusClass.h" -// -//#include "BufferExecute.h" -////#ifdef SensorModbusEnabled -////=========================================Модуль modbus=================================================================================== -////modbus;id;anydata;Сенсоры;Температура;order;addr[1];regaddr[0];c[1] -////========================================================================================================================================= -//SensorModbusClass mySensorModbus; -// -//void modbus() { -// mySensorModbus.update(); -// String key = mySensorModbus.gkey(); -// sCmd.addCommand(key.c_str(), modbusReading); -// mySensorModbus.SensorModbusInit(); -// mySensorModbus.clear(); -//} -//void modbusReading() { -// String key = sCmd.order(); -// String addr = sCmd.next(); -// String regaddr = sCmd.next(); -// mySensorModbus.SensorModbusRead(key, addr.toInt(), regaddr.toInt()); -//} -////#endif \ No newline at end of file +#include "Consts.h" +#ifdef SensorModbusEnabled +#include "items/SensorModbusClass.h" +#include "BufferExecute.h" +//=========================================Модуль modbus=================================================================================== +//modbus;id;anydata;Сенсоры;Температура;order;addr[1];regaddr[0];c[1] +//========================================================================================================================================= +SensorModbusClass mySensorModbus; + +void modbus() { + mySensorModbus.update(); + String key = mySensorModbus.gkey(); + sCmd.addCommand(key.c_str(), modbusReading); + mySensorModbus.SensorModbusInit(); + mySensorModbus.clear(); +} +void modbusReading() { + String key = sCmd.order(); + String addr = sCmd.next(); + String regaddr = sCmd.next(); + mySensorModbus.SensorModbusRead(key, addr.toInt(), regaddr.toInt()); +} +#endif \ No newline at end of file diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index ff98e2ee..3ef137b0 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -21,12 +21,14 @@ ButtonOut::~ButtonOut() {} void ButtonOut::execute(String state) { if (_type == "UART") { - if (jsonReadBool(configSetupJson, "uart")) { + if (jsonReadBool(configSetupJson, "uartEnable")) { +#ifdef uartEnable if (myUART) { String msg = _key + " " + state; myUART->print(msg); SerialPrint("I", "<=UART", msg); - } + } +#endif } } if (state != "" && _pin != "") { diff --git a/src/items/vPwmOut.cpp b/src/items/vPwmOut.cpp index f2346c27..06c39916 100644 --- a/src/items/vPwmOut.cpp +++ b/src/items/vPwmOut.cpp @@ -1,9 +1,13 @@ + +#include "Consts.h" +#ifdef PwmOutEnable #include "items/vPwmOut.h" #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" - #include + + //this class save data to flash PwmOut::PwmOut(unsigned int pin, String key) { _pin = pin; @@ -52,4 +56,5 @@ void pwmOutExecute() { myPwmOut->at(number).execute(state); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 41936b07..60edbce8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,14 +41,18 @@ void setup() { setChipId(); fileSystemInit(); loadConfig(); +#ifdef uartEnable uartInit(); +#endif clockInit(); timeInit(); sensorsInit(); //Will be remooved itemsListInit(); espInit(); routerConnect(); +#ifdef telegram telegramInit(); +#endif uptime_init(); upgradeInit(); HttpServer::init(); @@ -91,9 +95,13 @@ void loop() { myNotAsyncActions->loop(); ts.update(); +#ifdef telegram handleTelegram(); +#endif +#ifdef uartEnable uartHandle(); +#endif if (myLogging != nullptr) { for (unsigned int i = 0; i < myLogging->size(); i++) { From 8f08459a5a71efd1b5cf1c03caf7c773eabf950c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 16 Dec 2020 19:28:44 +0100 Subject: [PATCH 43/94] 1mb --- data/items/analog-adc.txt | 1 - data/items/bme280-hum.txt | 1 - data/items/bme280-press.txt | 1 - data/items/bme280-temp.txt | 1 - data/items/bmp280-press.txt | 1 - data/items/bmp280-temp.txt | 1 - data/items/button-in.txt | 1 - data/items/button-out.inv.txt | 1 - data/items/button-out.npin.txt | 1 - data/items/button-out.pin.txt | 1 - data/items/count-down.txt | 1 - data/items/dallas-temp.txt | 1 - data/items/dht11-hum.txt | 1 - data/items/dht11-temp.txt | 1 - data/items/dht22-hum.txt | 1 - data/items/dht22-temp.txt | 1 - data/items/impuls-out.txt | 1 - data/items/input-digit.txt | 1 - data/items/input-time.txt | 1 - data/items/logging.txt | 1 - data/items/modbus.txt | 1 - data/items/output-text.txt | 1 - data/items/pwm-out.txt | 1 - data/items/uart-button.txt | 1 - data/items/uart-widget.txt | 1 - data/items/ultrasonic-cm.txt | 1 - data/items/uptime.txt | 1 - data/presets/1.c.txt | 5 - data/presets/1.s.txt | 8 - data/presets/2.c.txt | 12 - data/presets/2.s.txt | 18 - data/presets/3.c.txt | 5 - data/presets/3.s.txt | 8 - data/presets/4.c.txt | 4 - data/presets/4.s.txt | 8 - data/presets/5.c.txt | 7 - data/presets/5.s.txt | 16 - data/presets/6.c.txt | 3 - data/presets/6.s.txt | 6 - data/presets/7.c.txt | 4 - data/presets/7.s.txt | 10 - data/presets/8.c.txt | 4 - data/presets/8.s.txt | 7 - data/presets/9.c.txt | 2 - data/presets/9.s.txt | 3 - include/Consts.h | 15 +- include/Global.h | 4 +- include/SoftUART.h | 1 + include/Telegram.h | 3 +- include/items/SensorModbusClass.h | 89 -- lib/ModbusMaster/.github/ISSUE_TEMPLATE.md | 66 -- .../.github/PULL_REQUEST_TEMPLATE.md | 37 - lib/ModbusMaster/.github_changelog_generator | 8 - lib/ModbusMaster/.gitignore | 56 -- lib/ModbusMaster/.ruby-gemset | 1 - lib/ModbusMaster/.ruby-version | 1 - lib/ModbusMaster/.travis.yml | 30 - lib/ModbusMaster/CHANGELOG.md | 141 --- lib/ModbusMaster/CODE_OF_CONDUCT.md | 78 -- lib/ModbusMaster/CONTRIBUTING.md | 58 -- lib/ModbusMaster/Gemfile | 30 - lib/ModbusMaster/Gemfile.lock | 47 - lib/ModbusMaster/LICENSE | 201 ---- lib/ModbusMaster/Makefile | 19 - lib/ModbusMaster/README.md | 167 ---- lib/ModbusMaster/Rakefile | 218 ----- lib/ModbusMaster/STYLE.md | 372 -------- lib/ModbusMaster/VERSION | 1 - lib/ModbusMaster/examples/Basic/Basic.pde | 69 -- .../PhoenixContact_nanoLC.pde | 143 --- .../RS485_HalfDuplex/RS485_HalfDuplex.ino | 98 -- .../extras/ModbusMaster reference-2.0.1.pdf | Bin 206477 -> 0 bytes lib/ModbusMaster/extras/README.txt | 6 - lib/ModbusMaster/keywords.txt | 50 - lib/ModbusMaster/library.properties | 10 - lib/ModbusMaster/src/ModbusMaster.cpp | 876 ------------------ lib/ModbusMaster/src/ModbusMaster.h | 270 ------ lib/ModbusMaster/src/util/crc16.h | 88 -- lib/ModbusMaster/src/util/word.h | 64 -- platformio.ini | 3 +- src/BufferExecute.cpp | 6 - src/SoftUART.cpp | 1 + src/Telegram.cpp | 5 +- src/Utils/TimeUtils.cpp | 3 +- src/items/SensorModbusClass.cpp | 23 - src/main.cpp | 4 +- 86 files changed, 17 insertions(+), 3502 deletions(-) delete mode 100644 data/items/analog-adc.txt delete mode 100644 data/items/bme280-hum.txt delete mode 100644 data/items/bme280-press.txt delete mode 100644 data/items/bme280-temp.txt delete mode 100644 data/items/bmp280-press.txt delete mode 100644 data/items/bmp280-temp.txt delete mode 100644 data/items/button-in.txt delete mode 100644 data/items/button-out.inv.txt delete mode 100644 data/items/button-out.npin.txt delete mode 100644 data/items/button-out.pin.txt delete mode 100644 data/items/count-down.txt delete mode 100644 data/items/dallas-temp.txt delete mode 100644 data/items/dht11-hum.txt delete mode 100644 data/items/dht11-temp.txt delete mode 100644 data/items/dht22-hum.txt delete mode 100644 data/items/dht22-temp.txt delete mode 100644 data/items/impuls-out.txt delete mode 100644 data/items/input-digit.txt delete mode 100644 data/items/input-time.txt delete mode 100644 data/items/logging.txt delete mode 100644 data/items/modbus.txt delete mode 100644 data/items/output-text.txt delete mode 100644 data/items/pwm-out.txt delete mode 100644 data/items/uart-button.txt delete mode 100644 data/items/uart-widget.txt delete mode 100644 data/items/ultrasonic-cm.txt delete mode 100644 data/items/uptime.txt delete mode 100644 data/presets/1.c.txt delete mode 100644 data/presets/1.s.txt delete mode 100644 data/presets/2.c.txt delete mode 100644 data/presets/2.s.txt delete mode 100644 data/presets/3.c.txt delete mode 100644 data/presets/3.s.txt delete mode 100644 data/presets/4.c.txt delete mode 100644 data/presets/4.s.txt delete mode 100644 data/presets/5.c.txt delete mode 100644 data/presets/5.s.txt delete mode 100644 data/presets/6.c.txt delete mode 100644 data/presets/6.s.txt delete mode 100644 data/presets/7.c.txt delete mode 100644 data/presets/7.s.txt delete mode 100644 data/presets/8.c.txt delete mode 100644 data/presets/8.s.txt delete mode 100644 data/presets/9.c.txt delete mode 100644 data/presets/9.s.txt delete mode 100644 include/items/SensorModbusClass.h delete mode 100644 lib/ModbusMaster/.github/ISSUE_TEMPLATE.md delete mode 100644 lib/ModbusMaster/.github/PULL_REQUEST_TEMPLATE.md delete mode 100644 lib/ModbusMaster/.github_changelog_generator delete mode 100644 lib/ModbusMaster/.gitignore delete mode 100644 lib/ModbusMaster/.ruby-gemset delete mode 100644 lib/ModbusMaster/.ruby-version delete mode 100644 lib/ModbusMaster/.travis.yml delete mode 100644 lib/ModbusMaster/CHANGELOG.md delete mode 100644 lib/ModbusMaster/CODE_OF_CONDUCT.md delete mode 100644 lib/ModbusMaster/CONTRIBUTING.md delete mode 100644 lib/ModbusMaster/Gemfile delete mode 100644 lib/ModbusMaster/Gemfile.lock delete mode 100644 lib/ModbusMaster/LICENSE delete mode 100644 lib/ModbusMaster/Makefile delete mode 100644 lib/ModbusMaster/README.md delete mode 100644 lib/ModbusMaster/Rakefile delete mode 100644 lib/ModbusMaster/STYLE.md delete mode 100644 lib/ModbusMaster/VERSION delete mode 100644 lib/ModbusMaster/examples/Basic/Basic.pde delete mode 100644 lib/ModbusMaster/examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde delete mode 100644 lib/ModbusMaster/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino delete mode 100644 lib/ModbusMaster/extras/ModbusMaster reference-2.0.1.pdf delete mode 100644 lib/ModbusMaster/extras/README.txt delete mode 100644 lib/ModbusMaster/keywords.txt delete mode 100644 lib/ModbusMaster/library.properties delete mode 100644 lib/ModbusMaster/src/ModbusMaster.cpp delete mode 100644 lib/ModbusMaster/src/ModbusMaster.h delete mode 100644 lib/ModbusMaster/src/util/crc16.h delete mode 100644 lib/ModbusMaster/src/util/word.h delete mode 100644 src/items/SensorModbusClass.cpp diff --git a/data/items/analog-adc.txt b/data/items/analog-adc.txt deleted file mode 100644 index 8bc0e914..00000000 --- a/data/items/analog-adc.txt +++ /dev/null @@ -1 +0,0 @@ -0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1] \ No newline at end of file diff --git a/data/items/bme280-hum.txt b/data/items/bme280-hum.txt deleted file mode 100644 index c8afd255..00000000 --- a/data/items/bme280-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bme280-press.txt b/data/items/bme280-press.txt deleted file mode 100644 index f79973e2..00000000 --- a/data/items/bme280-press.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bme280-temp.txt b/data/items/bme280-temp.txt deleted file mode 100644 index 6e5e9003..00000000 --- a/data/items/bme280-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bmp280-press.txt b/data/items/bmp280-press.txt deleted file mode 100644 index 44b9e8c9..00000000 --- a/data/items/bmp280-press.txt +++ /dev/null @@ -1 +0,0 @@ -0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/bmp280-temp.txt b/data/items/bmp280-temp.txt deleted file mode 100644 index c3cb42eb..00000000 --- a/data/items/bmp280-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/data/items/button-in.txt b/data/items/button-in.txt deleted file mode 100644 index 79d79ec9..00000000 --- a/data/items/button-in.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] \ No newline at end of file diff --git a/data/items/button-out.inv.txt b/data/items/button-out.inv.txt deleted file mode 100644 index 5499a108..00000000 --- a/data/items/button-out.inv.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] \ No newline at end of file diff --git a/data/items/button-out.npin.txt b/data/items/button-out.npin.txt deleted file mode 100644 index ecffd881..00000000 --- a/data/items/button-out.npin.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order \ No newline at end of file diff --git a/data/items/button-out.pin.txt b/data/items/button-out.pin.txt deleted file mode 100644 index b0645025..00000000 --- a/data/items/button-out.pin.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;pin \ No newline at end of file diff --git a/data/items/count-down.txt b/data/items/count-down.txt deleted file mode 100644 index 3300573c..00000000 --- a/data/items/count-down.txt +++ /dev/null @@ -1 +0,0 @@ -0;count-down;id;anydata;Таймер;Обратный#отчет;order \ No newline at end of file diff --git a/data/items/dallas-temp.txt b/data/items/dallas-temp.txt deleted file mode 100644 index 0da8bdca..00000000 --- a/data/items/dallas-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;index[0];int[10] \ No newline at end of file diff --git a/data/items/dht11-hum.txt b/data/items/dht11-hum.txt deleted file mode 100644 index b7d9a820..00000000 --- a/data/items/dht11-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/data/items/dht11-temp.txt b/data/items/dht11-temp.txt deleted file mode 100644 index 39c5e949..00000000 --- a/data/items/dht11-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/data/items/dht22-hum.txt b/data/items/dht22-hum.txt deleted file mode 100644 index ab69cf97..00000000 --- a/data/items/dht22-hum.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/data/items/dht22-temp.txt b/data/items/dht22-temp.txt deleted file mode 100644 index d14a434d..00000000 --- a/data/items/dht22-temp.txt +++ /dev/null @@ -1 +0,0 @@ -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/data/items/impuls-out.txt b/data/items/impuls-out.txt deleted file mode 100644 index 99994ae5..00000000 --- a/data/items/impuls-out.txt +++ /dev/null @@ -1 +0,0 @@ -0;impuls-out;id;na;na;na;order;pin \ No newline at end of file diff --git a/data/items/input-digit.txt b/data/items/input-digit.txt deleted file mode 100644 index 7df7260e..00000000 --- a/data/items/input-digit.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file diff --git a/data/items/input-time.txt b/data/items/input-time.txt deleted file mode 100644 index 3fc078b0..00000000 --- a/data/items/input-time.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;inputTime;Ввод;Введите#время;order \ No newline at end of file diff --git a/data/items/logging.txt b/data/items/logging.txt deleted file mode 100644 index 8253774f..00000000 --- a/data/items/logging.txt +++ /dev/null @@ -1 +0,0 @@ -0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] \ No newline at end of file diff --git a/data/items/modbus.txt b/data/items/modbus.txt deleted file mode 100644 index a782395a..00000000 --- a/data/items/modbus.txt +++ /dev/null @@ -1 +0,0 @@ -0;modbus;id;anydata;Modbus;Регистр;order;addr[1];reg[0];c[1] \ No newline at end of file diff --git a/data/items/output-text.txt b/data/items/output-text.txt deleted file mode 100644 index 65d45284..00000000 --- a/data/items/output-text.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;anydata;Вывод;Сигнализация;order \ No newline at end of file diff --git a/data/items/pwm-out.txt b/data/items/pwm-out.txt deleted file mode 100644 index 2acbb459..00000000 --- a/data/items/pwm-out.txt +++ /dev/null @@ -1 +0,0 @@ -0;pwm-out;id;range;Ползунки;Яркость;order;pin \ No newline at end of file diff --git a/data/items/uart-button.txt b/data/items/uart-button.txt deleted file mode 100644 index ee2a6b22..00000000 --- a/data/items/uart-button.txt +++ /dev/null @@ -1 +0,0 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;type[UART] \ No newline at end of file diff --git a/data/items/uart-widget.txt b/data/items/uart-widget.txt deleted file mode 100644 index 469ab016..00000000 --- a/data/items/uart-widget.txt +++ /dev/null @@ -1 +0,0 @@ -0;inoutput;id;anydata;Вывод;Вывод#uart;order \ No newline at end of file diff --git a/data/items/ultrasonic-cm.txt b/data/items/ultrasonic-cm.txt deleted file mode 100644 index c5d518c3..00000000 --- a/data/items/ultrasonic-cm.txt +++ /dev/null @@ -1 +0,0 @@ -0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] \ No newline at end of file diff --git a/data/items/uptime.txt b/data/items/uptime.txt deleted file mode 100644 index 972241b4..00000000 --- a/data/items/uptime.txt +++ /dev/null @@ -1 +0,0 @@ -0;uptime;id;anydataTime;Системные;%name%#uptime;order \ No newline at end of file diff --git a/data/presets/1.c.txt b/data/presets/1.c.txt deleted file mode 100644 index abcd06dc..00000000 --- a/data/presets/1.c.txt +++ /dev/null @@ -1,5 +0,0 @@ -0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] -0;logging;log;chart;Термостат;История;2;val[temp];int[60];cnt[100] -0;inoutput;inputU;inputDigit;Термостат;Верхний#порог;3 -0;inoutput;inputL;inputDigit;Термостат;Нижний#порог;4 -0;button-out;button;toggle;Термостат;Нагрев;5;pin[12] \ No newline at end of file diff --git a/data/presets/1.s.txt b/data/presets/1.s.txt deleted file mode 100644 index dc4c8c07..00000000 --- a/data/presets/1.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -temp > inputU -button 0 -telegram нагрев#выключен 1 -end -temp < inputL -button 1 -telegram нагрев#включен 1 -end \ No newline at end of file diff --git a/data/presets/2.c.txt b/data/presets/2.c.txt deleted file mode 100644 index d91fa5c7..00000000 --- a/data/presets/2.c.txt +++ /dev/null @@ -1,12 +0,0 @@ -0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[60] -0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] -0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 -0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] -0;inoutput;time1;inputTimeClock;Расписание;Утренний#период;8 -0;inoutput;threshold1;inputDigitTemp;Расписание;Температура;9 -0;inoutput;time2;inputTimeClock;Расписание;Дневной#период;10 -0;inoutput;threshold2;inputDigitTemp;Расписание;Температура;11 -0;inoutput;time3;inputTimeClock;Расписание;Вечерний#период;12 -0;inoutput;threshold3;inputDigitTemp;Расписание;Температура;13 -0;inoutput;time4;inputTimeClock;Расписание;Ночной#период;14 -0;inoutput;threshold4;inputDigitTemp;Расписание;Температура;15 \ No newline at end of file diff --git a/data/presets/2.s.txt b/data/presets/2.s.txt deleted file mode 100644 index 5a6f36f1..00000000 --- a/data/presets/2.s.txt +++ /dev/null @@ -1,18 +0,0 @@ -temp > threshold+-2 -heater 0 -end -temp < threshold+-2 -heater 1 -end -timenow = time1 -threshold threshold1 -end -timenow = time2 -threshold threshold2 -end -timenow = time3 -threshold threshold3 -end -timenow = time4 -threshold threshold4 -end \ No newline at end of file diff --git a/data/presets/3.c.txt b/data/presets/3.c.txt deleted file mode 100644 index fb2c602b..00000000 --- a/data/presets/3.c.txt +++ /dev/null @@ -1,5 +0,0 @@ -0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] -0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] -0;inoutput;inputU;inputDigit;Теплица;Верхний#порог;3 -0;inoutput;inputL;inputDigit;Теплица;Нижний#порог;4 -0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/data/presets/3.s.txt b/data/presets/3.s.txt deleted file mode 100644 index f69cdc54..00000000 --- a/data/presets/3.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -hum > inputU -button 0 -telegram полив#выключен 1 -end -hum < inputL -button 1 -telegram полив#включен 1 -end \ No newline at end of file diff --git a/data/presets/4.c.txt b/data/presets/4.c.txt deleted file mode 100644 index 3f1ccaa1..00000000 --- a/data/presets/4.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;button-out;button1;toggle;Реле;Освещение;1;pin[12] -0;button-out;button2;toggle;Реле;Освещение;2;pin[13] -0;inoutput;T1;inputTime;Реле;Введите#время#включения;3 -0;inoutput;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/data/presets/4.s.txt b/data/presets/4.s.txt deleted file mode 100644 index 844d1c5b..00000000 --- a/data/presets/4.s.txt +++ /dev/null @@ -1,8 +0,0 @@ -timenow = T1 -button1 1 -button2 0 -end -timenow = T2 -button1 0 -button2 1 -end \ No newline at end of file diff --git a/data/presets/5.c.txt b/data/presets/5.c.txt deleted file mode 100644 index 359a7a52..00000000 --- a/data/presets/5.c.txt +++ /dev/null @@ -1,7 +0,0 @@ -0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1 -0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12] -0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13] -0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] -0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] -0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] -0;inoutput;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/data/presets/5.s.txt b/data/presets/5.s.txt deleted file mode 100644 index 0424728a..00000000 --- a/data/presets/5.s.txt +++ /dev/null @@ -1,16 +0,0 @@ -button-out-1 = 1 -button-out-2 1 -button-out-3 1 -button-out-4 1 -pwm-out-5 200 -pwm-out-6 800 -output-text-7 включено -end -button-out-1 = 0 -button-out-2 0 -button-out-3 0 -button-out-4 0 -pwm-out-5 800 -pwm-out-6 200 -output-text-7 выключено -end \ No newline at end of file diff --git a/data/presets/6.c.txt b/data/presets/6.c.txt deleted file mode 100644 index c7fd096a..00000000 --- a/data/presets/6.c.txt +++ /dev/null @@ -1,3 +0,0 @@ -0;button-out;button;toggle;Таймер;Освещение;1;pin[12] -0;count-down;count;anydata;Таймер;Обратный#отчет;2 -0;inoutput;input;inputDigit;Таймер;Введите#цифру;3 \ No newline at end of file diff --git a/data/presets/6.s.txt b/data/presets/6.s.txt deleted file mode 100644 index 06b70a4a..00000000 --- a/data/presets/6.s.txt +++ /dev/null @@ -1,6 +0,0 @@ -button = 1 -count input -end -count = 0 -button 0 -end \ No newline at end of file diff --git a/data/presets/7.c.txt b/data/presets/7.c.txt deleted file mode 100644 index 984c6b8d..00000000 --- a/data/presets/7.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 -0;inoutput;time;anydataTime;Сигнализация;Время:;2 -0;button-in;sensor;na;na;na;3;pin[0];db[20] -0;button-out;reset;toggle;Сигнализация;Сбросить;4 \ No newline at end of file diff --git a/data/presets/7.s.txt b/data/presets/7.s.txt deleted file mode 100644 index 32d28e78..00000000 --- a/data/presets/7.s.txt +++ /dev/null @@ -1,10 +0,0 @@ -sensor = 1 -text обнаружено -time %date% -telegram text обнаружено#движение 1 -end -reset = 1 -text не#обнаружено -time %date% -reset 0 -end \ No newline at end of file diff --git a/data/presets/8.c.txt b/data/presets/8.c.txt deleted file mode 100644 index d18bdd0b..00000000 --- a/data/presets/8.c.txt +++ /dev/null @@ -1,4 +0,0 @@ -0;button-in;sensor;na;na;na;1;pin[0];db[20] -0;button-out;light;toggle;Освещение;Освещение;2;pin[13] -0;count-down;count;anydata;Освещение;Обратный#отчет;3 -0;inoutput;period;inputDigit;Освещение;Период#включения;4 \ No newline at end of file diff --git a/data/presets/8.s.txt b/data/presets/8.s.txt deleted file mode 100644 index 10d55654..00000000 --- a/data/presets/8.s.txt +++ /dev/null @@ -1,7 +0,0 @@ -sensor = 1 -light 1 -count period -end -count = 0 -light 0 -end \ No newline at end of file diff --git a/data/presets/9.c.txt b/data/presets/9.c.txt deleted file mode 100644 index 502f3ae4..00000000 --- a/data/presets/9.c.txt +++ /dev/null @@ -1,2 +0,0 @@ -0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] -0;button-in;switch;na;na;na;2;pin[0];db[20] \ No newline at end of file diff --git a/data/presets/9.s.txt b/data/presets/9.s.txt deleted file mode 100644 index 6f6d7dcc..00000000 --- a/data/presets/9.s.txt +++ /dev/null @@ -1,3 +0,0 @@ -switch = 1 -light change -end \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index d8a40cfb..0bce4c82 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -17,22 +17,16 @@ //===========FileSystem============================================================================================================================================== #define littlefs_on - //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN 2 - //===========MQTT================================================================================================================================================= #define MQTT_RECONNECT_INTERVAL 20000 - //==========Telemetry============================================================================================================================================= #define TELEMETRY_UPDATE_INTERVAL_MIN 60 - //=========Configuration========================================================================================================================================== #define DEVICE_CONFIG_FILE "s.conf.csv" #define DEVICE_SCENARIO_FILE "s.scen.txt" - - //=========System parts=========================================================================================================================================== //#define OTA_UPDATES_ENABLED //#define MDNS_ENABLED @@ -40,17 +34,14 @@ //#define LAYOUT_IN_RAM //#define UDP_ENABLED //#define SSDP_ENABLED - //=========Sensors enable/disable================================================================================================================================= #define SensorBme280Enabled #define SensorBmp280Enabled #define SensorDhtEnabled #define PwmOutEnable -//#define SensorModbusEnabled - -//=========others================================================================================================================================= -//#define telegram -//#define uartEnable +//=========Features================================================================================================================================= +//#define telegramEnable +#define uartEnable diff --git a/include/Global.h b/include/Global.h index 4be48511..b4ca622a 100644 --- a/include/Global.h +++ b/include/Global.h @@ -1,5 +1,6 @@ #pragma once //===================Libraries=================================================================================================================================================== +#include "Consts.h" #include #include #include @@ -15,14 +16,13 @@ #include #include #include - #include "Clock.h" -#include "Consts.h" #include "ESP32.h" #include "ESP8266.h" #include "GyverFilters.h" #include "MqttClient.h" #include "Upgrade.h" + #include "Utils/FileUtils.h" #include "Utils/JsonUtils.h" #include "Utils/SerialPrint.h" diff --git a/include/SoftUART.h b/include/SoftUART.h index a566e06a..8b928a36 100644 --- a/include/SoftUART.h +++ b/include/SoftUART.h @@ -1,4 +1,5 @@ #pragma once +#include "Consts.h" #ifdef uartEnable #include "SoftwareSerial.h" diff --git a/include/Telegram.h b/include/Telegram.h index 6b04b878..7b3c656d 100644 --- a/include/Telegram.h +++ b/include/Telegram.h @@ -1,5 +1,6 @@ #pragma once -#ifdef telegram +#include "Consts.h" +#ifdef telegramEnable #include "Global.h" extern void sendTelegramMsg(); diff --git a/include/items/SensorModbusClass.h b/include/items/SensorModbusClass.h deleted file mode 100644 index 9f13e166..00000000 --- a/include/items/SensorModbusClass.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once -#include "Consts.h" -#ifdef SensorModbusEnabled -#include -#include -#include -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" - -ModbusMaster modbus1; -SoftwareSerial uart(13, 12); // RX, TX - -class SensorModbusClass : public SensorConvertingClass { - public: - SensorModbusClass() : SensorConvertingClass(){}; - - void SensorModbusInit() { - uart.begin(9600); - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - sensorReadingMap10sec += _key + " " + _addr + " " + _reg + ","; - Serial.println(sensorReadingMap10sec); - } - - void SensorModbusRead(String key, uint8_t slaveAddress, uint16_t regAddress) { - int value; - - modbus1.begin(slaveAddress, uart); - uint16_t reqisterValue = modbus1.readHoldingRegisters(regAddress, 1); - if (getResultMsg(&modbus1, reqisterValue)) { - reqisterValue = modbus1.getResponseBuffer(0); - value = reqisterValue; - } else { - value = NULL; - } - - int valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl) + ", Slave dev addr: " + String(slaveAddress) + ", Register: " + String(regAddress)); - } - - bool getResultMsg(ModbusMaster* modbus1, uint16_t result) { - String tmpstr; - switch (result) { - case modbus1->ku8MBSuccess: - return true; - tmpstr += "Ok"; - break; - case modbus1->ku8MBIllegalFunction: - tmpstr += "Illegal Function"; - break; - case modbus1->ku8MBIllegalDataAddress: - tmpstr += "Illegal Data Address"; - break; - case modbus1->ku8MBIllegalDataValue: - tmpstr += "Illegal Data Value"; - break; - case modbus1->ku8MBSlaveDeviceFailure: - tmpstr += "Slave Device Failure"; - break; - case modbus1->ku8MBInvalidSlaveID: - tmpstr += "Invalid Slave ID"; - break; - case modbus1->ku8MBInvalidFunction: - tmpstr += "Invalid Function"; - break; - case modbus1->ku8MBResponseTimedOut: - tmpstr += "Response Timed Out"; - break; - case modbus1->ku8MBInvalidCRC: - tmpstr += "Invalid CRC"; - break; - default: - tmpstr += "Unknown error: " + String(result); - break; - } - SerialPrint("I", "Modbus", tmpstr); - return false; - } -}; -extern SensorModbusClass mySensorModbus; - -extern void modbus(); -extern void modbusReading(); - -#endif \ No newline at end of file diff --git a/lib/ModbusMaster/.github/ISSUE_TEMPLATE.md b/lib/ModbusMaster/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 7ae83e53..00000000 --- a/lib/ModbusMaster/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,66 +0,0 @@ - - - -### ModbusMaster version -[Version of the project where you are encountering the issue] - -### Arduino IDE version -[Version of Arduino IDE in your environment] - -### Arduino Hardware -[Hardware information, including board and processor] - -### Platform Details -[Operating system distribution and release version] - ---- - -### Scenario: -[What you are trying to achieve and you can't?] - -### Steps to Reproduce: -[If you are filing an issue what are the things we need to do in order to repro your problem? How are you using this project or any resources it includes?] - -### Expected Result: -[What are you expecting to happen as the consequence of above reproduction steps?] - -### Actual Result: -[What actually happens after the reproduction steps? Include the error output or a link to a gist if possible.] - ---- - -### Feature Request - -#### Narrative: - -```` text -As a [role] -I want [feature] -So that [benefit] -```` - -#### Acceptance Criteria: - -```` text -Scenario 1: Title -Given [context] - And [some more context]... -When [event] -Then [outcome] - And [another outcome]... -```` diff --git a/lib/ModbusMaster/.github/PULL_REQUEST_TEMPLATE.md b/lib/ModbusMaster/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 1039f974..00000000 --- a/lib/ModbusMaster/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,37 +0,0 @@ - -### Description -[Describe what this change achieves] - -### Issues Resolved -[List any existing issues this PR resolves; include Fixes #xxx or Closes #xxx (where xxx is issue number)] - -### Check List - -General - -- [ ] Code follows coding style defined in STYLE.md -- [ ] Doxygen comments are included inline with code -- [ ] No unnecessary whitespace; check with `git diff --check` before committing. - -The following have been modified to reflect new features, if warranted - -- [ ] README.md -- [ ] keywords.txt (use tabs as whitespace separators) -- [ ] library.properties -- [ ] examples/ - update or create new ones, as warranted - -The following have **NOT** been modified - -- [ ] doc/ - will be updated upon versioned release -- [ ] .ruby-gemset -- [ ] .ruby-version -- [ ] CHANGELOG.md - will be updated upon versioned release (HISTORY.md is deprecated) -- [ ] Gemfile -- [ ] LICENSE -- [ ] VERSION - will be updated upon versioned release diff --git a/lib/ModbusMaster/.github_changelog_generator b/lib/ModbusMaster/.github_changelog_generator deleted file mode 100644 index 1a846259..00000000 --- a/lib/ModbusMaster/.github_changelog_generator +++ /dev/null @@ -1,8 +0,0 @@ -add_issues_wo_labels=false -add_pr_wo_labels=false -enhancement-labels=Type: Enhancement -bug-labels=Type: Bug -exclude-labels=Type: Question -header=# ModbusMaster CHANGELOG -include-labels=Type: Bug,Type: Enhancement,Type: Feature Request,Type: Maintenance -future-release=Unreleased diff --git a/lib/ModbusMaster/.gitignore b/lib/ModbusMaster/.gitignore deleted file mode 100644 index d52f89eb..00000000 --- a/lib/ModbusMaster/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -#---------------------------------------------------------------- ModbusMaster -doc/html/ -doc/latex/ - - -#-------------- https://github.com/github/gitignore/blob/master/Ruby.gitignore -*.gem -*.rbc -/.config -/coverage/ -/InstalledFiles -/pkg/ -/spec/reports/ -/spec/examples.txt -/test/tmp/ -/test/version_tmp/ -/tmp/ - -# Used by dotenv library to load environment variables. -# .env - -## Specific to RubyMotion: -.dat* -.repl_history -build/ -*.bridgesupport -build-iPhoneOS/ -build-iPhoneSimulator/ - -## Specific to RubyMotion (use of CocoaPods): -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# vendor/Pods/ - -## Documentation cache and generated files: -/.yardoc/ -/_yardoc/ -/doc/ -/rdoc/ - -## Environment normalization: -/.bundle/ -/vendor/bundle -/lib/bundler/man/ - -# for a library or gem, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# Gemfile.lock -# .ruby-version -# .ruby-gemset - -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: -.rvmrc diff --git a/lib/ModbusMaster/.ruby-gemset b/lib/ModbusMaster/.ruby-gemset deleted file mode 100644 index b47d0eb8..00000000 --- a/lib/ModbusMaster/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -global diff --git a/lib/ModbusMaster/.ruby-version b/lib/ModbusMaster/.ruby-version deleted file mode 100644 index 2bf1c1cc..00000000 --- a/lib/ModbusMaster/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.1 diff --git a/lib/ModbusMaster/.travis.yml b/lib/ModbusMaster/.travis.yml deleted file mode 100644 index a9cbf868..00000000 --- a/lib/ModbusMaster/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: python - -python: - - 2.7 - -sudo: false - -cache: - directories: - - ~/.platformio - -# update Makefile if target boards added -env: - - PLATFORMIO_BOARD=uno - - PLATFORMIO_BOARD=due - - PLATFORMIO_BOARD=huzzah - - PLATFORMIO_BOARD=genuino101 - - PLATFORMIO_BOARD=teensy31 - -install: - - pip install -U platformio - -before_script: - - env - - echo $HOME - - echo $TRAVIS_BUILD_DIR - - ls -al $PWD - -script: - - make build diff --git a/lib/ModbusMaster/CHANGELOG.md b/lib/ModbusMaster/CHANGELOG.md deleted file mode 100644 index c5d1fe02..00000000 --- a/lib/ModbusMaster/CHANGELOG.md +++ /dev/null @@ -1,141 +0,0 @@ -# ModbusMaster CHANGELOG - -## [v2.0.0](https://github.com/4-20ma/ModbusMaster/tree/v2.0.0) (2016-09-24) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v1.0.0...v2.0.0) - -**Implemented enhancements:** - -- BREAK: Update library to match IDE 1.5 spec v2.1 [\#81](https://github.com/4-20ma/ModbusMaster/pull/81) ([4-20ma](https://github.com/4-20ma)) -- Use platformio to build multiple boards [\#79](https://github.com/4-20ma/ModbusMaster/pull/79) ([4-20ma](https://github.com/4-20ma)) - -**Closed issues:** - -- Use platformio to build against multiple boards [\#78](https://github.com/4-20ma/ModbusMaster/issues/78) -- Add ruby files to .gitignore [\#75](https://github.com/4-20ma/ModbusMaster/issues/75) -- Reorder Installation section of README [\#73](https://github.com/4-20ma/ModbusMaster/issues/73) -- Update README [\#71](https://github.com/4-20ma/ModbusMaster/issues/71) -- Rename HISTORY to CHANGELOG [\#47](https://github.com/4-20ma/ModbusMaster/issues/47) -- Update library to match 1.5 specification [\#14](https://github.com/4-20ma/ModbusMaster/issues/14) - -**Merged pull requests:** - -- Use relative path to examples [\#80](https://github.com/4-20ma/ModbusMaster/pull/80) ([4-20ma](https://github.com/4-20ma)) -- Add files to .gitignore [\#76](https://github.com/4-20ma/ModbusMaster/pull/76) ([4-20ma](https://github.com/4-20ma)) -- Reorder installation section of README [\#74](https://github.com/4-20ma/ModbusMaster/pull/74) ([4-20ma](https://github.com/4-20ma)) -- Update README [\#72](https://github.com/4-20ma/ModbusMaster/pull/72) ([4-20ma](https://github.com/4-20ma)) -- Automate CHANGELOG generation [\#68](https://github.com/4-20ma/ModbusMaster/pull/68) ([4-20ma](https://github.com/4-20ma)) - -## [v1.0.0](https://github.com/4-20ma/ModbusMaster/tree/v1.0.0) (2016-09-12) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.11.0...v1.0.0) - -**Implemented enhancements:** - -- Add LICENSE, convert project to Apache 2.0 [\#67](https://github.com/4-20ma/ModbusMaster/pull/67) ([4-20ma](https://github.com/4-20ma)) -- Add example sketch for half-duplex RS485 [\#66](https://github.com/4-20ma/ModbusMaster/pull/66) ([kintel](https://github.com/kintel)) -- Add continuous integration testing with Travis CI [\#63](https://github.com/4-20ma/ModbusMaster/pull/63) ([4-20ma](https://github.com/4-20ma)) -- Disable \_\_MODBUSMASTER\_DEBUG\_\_ mode by default [\#43](https://github.com/4-20ma/ModbusMaster/pull/43) ([kintel](https://github.com/kintel)) - -**Closed issues:** - -- Fix documentation references in ModbusMaster.h, .cpp [\#69](https://github.com/4-20ma/ModbusMaster/issues/69) -- Clean up template wording [\#64](https://github.com/4-20ma/ModbusMaster/issues/64) -- Add Label section to CONTRIBUTING [\#60](https://github.com/4-20ma/ModbusMaster/issues/60) -- Update README contact information [\#58](https://github.com/4-20ma/ModbusMaster/issues/58) -- Add continuous integration testing with travis [\#55](https://github.com/4-20ma/ModbusMaster/issues/55) -- Add Code of Conduct [\#54](https://github.com/4-20ma/ModbusMaster/issues/54) -- Create PULL\_REQUEST\_TEMPLATE [\#49](https://github.com/4-20ma/ModbusMaster/issues/49) -- Create ISSUE\_TEMPLATE [\#48](https://github.com/4-20ma/ModbusMaster/issues/48) -- Change license to Apache 2.0 [\#45](https://github.com/4-20ma/ModbusMaster/issues/45) -- Set \_\_MODBUSMASTER\_DEBUG\_\_ to 0 by default [\#35](https://github.com/4-20ma/ModbusMaster/issues/35) -- Pass Stream object instead of integer reference [\#17](https://github.com/4-20ma/ModbusMaster/issues/17) - -**Merged pull requests:** - -- Add documentation cross-references [\#70](https://github.com/4-20ma/ModbusMaster/pull/70) ([4-20ma](https://github.com/4-20ma)) -- Clean up ISSUE/PULL\_REQUEST templates [\#65](https://github.com/4-20ma/ModbusMaster/pull/65) ([4-20ma](https://github.com/4-20ma)) -- Add initial .travis.yml configuration [\#62](https://github.com/4-20ma/ModbusMaster/pull/62) ([4-20ma](https://github.com/4-20ma)) -- Add label guidance to CONTRIBUTING [\#61](https://github.com/4-20ma/ModbusMaster/pull/61) ([4-20ma](https://github.com/4-20ma)) -- Update README contact information [\#59](https://github.com/4-20ma/ModbusMaster/pull/59) ([4-20ma](https://github.com/4-20ma)) -- Add email address to CODE\_OF\_CONDUCT [\#57](https://github.com/4-20ma/ModbusMaster/pull/57) ([4-20ma](https://github.com/4-20ma)) -- Add CODE\_OF\_CONDUCT [\#56](https://github.com/4-20ma/ModbusMaster/pull/56) ([4-20ma](https://github.com/4-20ma)) -- Add initial PULL\_REQUEST\_TEMPLATE [\#53](https://github.com/4-20ma/ModbusMaster/pull/53) ([4-20ma](https://github.com/4-20ma)) -- Clarify instructions in ISSUE\_TEMPLATE [\#52](https://github.com/4-20ma/ModbusMaster/pull/52) ([4-20ma](https://github.com/4-20ma)) -- Add ISSUE\_TEMPLATE title reqs, separator lines [\#51](https://github.com/4-20ma/ModbusMaster/pull/51) ([4-20ma](https://github.com/4-20ma)) -- Add initial ISSUE\_TEMPLATE [\#50](https://github.com/4-20ma/ModbusMaster/pull/50) ([4-20ma](https://github.com/4-20ma)) -- Add preTransmission\(\), postTransmission\(\) for half-duplex [\#44](https://github.com/4-20ma/ModbusMaster/pull/44) ([kintel](https://github.com/kintel)) -- Add STYLE coding style guide [\#29](https://github.com/4-20ma/ModbusMaster/pull/29) ([4-20ma](https://github.com/4-20ma)) - -## [v0.11.0](https://github.com/4-20ma/ModbusMaster/tree/v0.11.0) (2015-05-22) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.10.3...v0.11.0) - -**Implemented enhancements:** - -- Update architecture switch [\#28](https://github.com/4-20ma/ModbusMaster/pull/28) ([4-20ma](https://github.com/4-20ma)) - -**Closed issues:** - -- Update architecture switch to match Arduino convention [\#27](https://github.com/4-20ma/ModbusMaster/issues/27) -- Request timeout is impatient [\#3](https://github.com/4-20ma/ModbusMaster/issues/3) - -## [v0.10.3](https://github.com/4-20ma/ModbusMaster/tree/v0.10.3) (2015-05-22) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.10.2...v0.10.3) - -**Closed issues:** - -- Inconsistent Doxygen comments [\#25](https://github.com/4-20ma/ModbusMaster/issues/25) -- Replace C macros with inline functions [\#18](https://github.com/4-20ma/ModbusMaster/issues/18) - -**Merged pull requests:** - -- Adjust doxygen comments to be consistent [\#26](https://github.com/4-20ma/ModbusMaster/pull/26) ([4-20ma](https://github.com/4-20ma)) -- Replace C macros w/inline functions [\#24](https://github.com/4-20ma/ModbusMaster/pull/24) ([4-20ma](https://github.com/4-20ma)) - -## [v0.10.2](https://github.com/4-20ma/ModbusMaster/tree/v0.10.2) (2015-05-22) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.9.1...v0.10.2) - -**Implemented enhancements:** - -- Implement CRC16 for SAM3X8E microprocessor [\#11](https://github.com/4-20ma/ModbusMaster/pull/11) ([4-20ma](https://github.com/4-20ma)) -- Add rx flush, change response timeout to 2000 ms [\#10](https://github.com/4-20ma/ModbusMaster/pull/10) ([agprimatic](https://github.com/agprimatic)) - -**Fixed bugs:** - -- Fix documentation build error [\#23](https://github.com/4-20ma/ModbusMaster/pull/23) ([4-20ma](https://github.com/4-20ma)) -- Work around HardwareSerial for SAM3 micro [\#12](https://github.com/4-20ma/ModbusMaster/pull/12) ([4-20ma](https://github.com/4-20ma)) - -**Merged pull requests:** - -- Update pointers to match C++ convention [\#22](https://github.com/4-20ma/ModbusMaster/pull/22) ([4-20ma](https://github.com/4-20ma)) -- Rename markdown file extensions [\#21](https://github.com/4-20ma/ModbusMaster/pull/21) ([4-20ma](https://github.com/4-20ma)) - -## [v0.9.1](https://github.com/4-20ma/ModbusMaster/tree/v0.9.1) (2013-01-02) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.9...v0.9.1) - -## [v0.9](https://github.com/4-20ma/ModbusMaster/tree/v0.9) (2011-12-27) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.8...v0.9) - -## [v0.8](https://github.com/4-20ma/ModbusMaster/tree/v0.8) (2011-11-09) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.7...v0.8) - -## [v0.7](https://github.com/4-20ma/ModbusMaster/tree/v0.7) (2010-02-10) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.6...v0.7) - -## [v0.6](https://github.com/4-20ma/ModbusMaster/tree/v0.6) (2010-02-05) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.5...v0.6) - -## [v0.5](https://github.com/4-20ma/ModbusMaster/tree/v0.5) (2010-01-30) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.4...v0.5) - -## [v0.4](https://github.com/4-20ma/ModbusMaster/tree/v0.4) (2010-01-30) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.3...v0.4) - -## [v0.3](https://github.com/4-20ma/ModbusMaster/tree/v0.3) (2010-01-29) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.2...v0.3) - -## [v0.2](https://github.com/4-20ma/ModbusMaster/tree/v0.2) (2010-01-26) -[Full Changelog](https://github.com/4-20ma/ModbusMaster/compare/v0.1...v0.2) - -## [v0.1](https://github.com/4-20ma/ModbusMaster/tree/v0.1) (2010-01-25) - - -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/lib/ModbusMaster/CODE_OF_CONDUCT.md b/lib/ModbusMaster/CODE_OF_CONDUCT.md deleted file mode 100644 index eca5ac5b..00000000 --- a/lib/ModbusMaster/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,78 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project owner at 4-20ma@wvfans.net. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Project Maintainers - -- Doc Walker <<4-20ma@wvfans.net>> - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/lib/ModbusMaster/CONTRIBUTING.md b/lib/ModbusMaster/CONTRIBUTING.md deleted file mode 100644 index 0bcf721a..00000000 --- a/lib/ModbusMaster/CONTRIBUTING.md +++ /dev/null @@ -1,58 +0,0 @@ -Contributing -============ - -- Fork, then clone the repo: - ```` - git clone git@github.com:your_username/ModbusMaster.git - ```` - -- Create a topic branch from where you want to base your work - - This is usually the master branch - - Only target release branches if you are certain your fix must be on that branch - - To quickly create a topic branch based on master; `git checkout -b fix/master/my_contribution master`. Please avoid working directly on the `master` branch. - -- Follow the [style guide](https://github.com/4-20ma/ModbusMaster/blob/master/STYLE.md) - -- Test your change - - ```` bash - $ make - ```` - - Project must build successfully using `make` in order for contribution to be considered. - -- Make commits of logical units - - Check for unnecessary whitespace with `git diff --check` before committing - - Each commit should represent one atomic change and should stand on its own - - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - -- Push to your fork and [submit a pull request](https://github.com/4-20ma/ModbusMaster/compare/) -- [Code of conduct](https://github.com/4-20ma/ModbusMaster/blob/master/CODE_OF_CONDUCT.md) - -### Labels - -Project maintainers assign labels to Issues and Pull Requests (PRs) to categorize, prioritize, and provide status. The following guidelines and conventions are used in this project: - -#### Type - -- `Bug` - existing code does not behave as described in the project documentation; _requires_ clear test case and be _reproducible_ by project maintainer -- `Enhancement` - improvement to an existing feature (Issue or Pull Request) -- `Feature Requst` - new functionality; _requires_ a well-written, clear user story (Issue) -- `Maintenance` - minor administrative change that does not provide enhancement or introduce new feature -- `Question` - self-explanatory - -#### Priority - -- `Low` - default priority; new issues generally start here -- `Medium` - issues are escalated, depending on severity of the issue -- `High` - issues are escalated, depending on severity of the issue -- `Critical` - these issues are to be resolved ahead of any other - -#### Status - -- `Abandoned` - issue/PR closed due to inactivity -- `Blocked` - issue/PR will not be resolved/merged (some projects label these items as `wontfix`; include explanation in issue/PR) -- `In Progress` - issue has been assigned and is actively being addressed; re-label issue `On Hold` with explanation if there will be a significant delay -- `Maintainer Review Needed` - last step prior to merge; PR passes continuous integration tests and is able to be cleanly merged - awaiting review for style, code cleanliness, etc. -- `On Hold` - implementation delayed; provide explanation in issue/PR -- `Pending Contributor Response` - issue/PR closed after 14 days of inactivity (re-label `Abandoned` at closure) diff --git a/lib/ModbusMaster/Gemfile b/lib/ModbusMaster/Gemfile deleted file mode 100644 index 9ed64664..00000000 --- a/lib/ModbusMaster/Gemfile +++ /dev/null @@ -1,30 +0,0 @@ -# encoding: utf-8 -# Gemfile style guide derived from: -# http://mcdowall.info/posts/gemfile-best-practices-and-discourse/ - -# Use `bundle install` after changing this file -# `bundle update [gemname]` to force update of gem -# `bundle show [gemname]` to see where a bundled gem is installed -# `bundle open [gemname]` to edit a bundled gem -# `bundle package` to add gem to vendor/cache - -source 'https://rubygems.org' - - -# place gems sourced from github.com in this section _________________________ - - -# place gems sourced from a project path in this section _____________________ - - -# place general project gems in this section (alphabetical order) ____________ -gem 'git', '~> 1.3.0' # git management -gem 'github_changelog_generator', '~> 1.13.1' -gem 'rake', '~> 11.2.2' -gem 'version', '~> 1.0.0' # version management gem - - -# place gems related to test/specs in this section (alphabetical order) ______ - - -# place gems related to development in this section (alphabetical order) _____ diff --git a/lib/ModbusMaster/Gemfile.lock b/lib/ModbusMaster/Gemfile.lock deleted file mode 100644 index cdec1471..00000000 --- a/lib/ModbusMaster/Gemfile.lock +++ /dev/null @@ -1,47 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.4.0) - colorize (0.8.1) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) - faraday (0.9.2) - multipart-post (>= 1.2, < 3) - git (1.3.0) - github_api (0.14.5) - addressable (~> 2.4.0) - descendants_tracker (~> 0.0.4) - faraday (~> 0.8, < 0.10) - hashie (>= 3.4) - oauth2 (~> 1.0) - github_changelog_generator (1.13.1) - colorize (~> 0.7) - github_api (~> 0.12) - rake (>= 10.0) - hashie (3.4.4) - jwt (1.5.4) - multi_json (1.12.1) - multi_xml (0.5.5) - multipart-post (2.0.0) - oauth2 (1.2.0) - faraday (>= 0.8, < 0.10) - jwt (~> 1.0) - multi_json (~> 1.3) - multi_xml (~> 0.5) - rack (>= 1.2, < 3) - rack (2.0.1) - rake (11.2.2) - thread_safe (0.3.5) - version (1.0.0) - -PLATFORMS - ruby - -DEPENDENCIES - git (~> 1.3.0) - github_changelog_generator (~> 1.13.1) - rake (~> 11.2.2) - version (~> 1.0.0) - -BUNDLED WITH - 1.12.1 diff --git a/lib/ModbusMaster/LICENSE b/lib/ModbusMaster/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/lib/ModbusMaster/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/lib/ModbusMaster/Makefile b/lib/ModbusMaster/Makefile deleted file mode 100644 index fba3392d..00000000 --- a/lib/ModbusMaster/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -#-------------------------------------------------------------------- settings -FIND := find -DIR := examples -CRITERIA := \( -name "*.ino" -o -name "*.pde" \) -EACH_EXAMPLE := $(FIND) $(DIR) $(CRITERIA) -exec -BUILD := platformio ci -LIB := src - -#--------------------------------------------------------------------- targets -# update .travis.yml if target boards added -all: uno due huzzah genuino101 teensy31 - -uno due huzzah genuino101 teensy31: - PLATFORMIO_BOARD=$@ $(MAKE) build - -build: - $(EACH_EXAMPLE) $(BUILD) --board=$(PLATFORMIO_BOARD) --lib=$(LIB) {} \; - -.PHONY: all uno due huzzah genuino101 teensy31 build diff --git a/lib/ModbusMaster/README.md b/lib/ModbusMaster/README.md deleted file mode 100644 index 5c5719e4..00000000 --- a/lib/ModbusMaster/README.md +++ /dev/null @@ -1,167 +0,0 @@ -# ModbusMaster -[![GitHub release](https://img.shields.io/github/release/4-20ma/ModbusMaster.svg?maxAge=3600)][GitHub release] -[![Travis](https://img.shields.io/travis/4-20ma/ModbusMaster.svg?maxAge=3600)][Travis] -[![license](https://img.shields.io/github/license/4-20ma/ModbusMaster.svg?maxAge=3600)][license] -[![code of conduct](https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?maxAge=3600)][code of conduct] - -[GitHub release]: https://github.com/4-20ma/ModbusMaster -[Travis]: https://travis-ci.org/4-20ma/ModbusMaster -[license]: LICENSE -[code of conduct]: CODE_OF_CONDUCT.md - - -## Overview -This is an Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol). - - -## Features -The following Modbus functions are available: - -Discrete Coils/Flags - - - 0x01 - Read Coils - - 0x02 - Read Discrete Inputs - - 0x05 - Write Single Coil - - 0x0F - Write Multiple Coils - -Registers - - - 0x03 - Read Holding Registers - - 0x04 - Read Input Registers - - 0x06 - Write Single Register - - 0x10 - Write Multiple Registers - - 0x16 - Mask Write Register - - 0x17 - Read Write Multiple Registers - -Both full-duplex and half-duplex RS232/485 transceivers are supported. Callback functions are provided to toggle Data Enable (DE) and Receiver Enable (/RE) pins. - - -## Installation - -#### Library Manager -Install the library into your Arduino IDE using the Library Manager (available from IDE version 1.6.2). Open the IDE and click Sketch > Include Library > Manage Libraries… - -Scroll or search for `ModbusMaster`, then select the version of the library you want to install. Quit/re-launch the IDE to refresh the list; new versions are automatically added to the list, once released on GitHub. - -Refer to Arduino Tutorials > Libraries [Using the Library Manager](https://www.arduino.cc/en/Guide/Libraries#toc3). - -#### Zip Library -Refer to Arduino Tutorials > Libraries [Importing a .zip Library](https://www.arduino.cc/en/Guide/Libraries#toc4). - -#### Manual -Refer to Arduino Tutorials > Libraries [Manual Installation](https://www.arduino.cc/en/Guide/Libraries#toc5). - - -## Hardware -This library has been tested with an Arduino [Duemilanove](http://www.arduino.cc/en/Main/ArduinoBoardDuemilanove), PHOENIX CONTACT [nanoLine](https://www.phoenixcontact.com/online/portal/us?1dmy&urile=wcm%3apath%3a/usen/web/main/products/subcategory_pages/standard_logic_modules_p-21-03-03/3329dd38-7c6a-46e1-8260-b9208235d6fe/3329dd38-7c6a-46e1-8260-b9208235d6fe) controller, connected via RS485 using a Maxim [MAX488EPA](http://www.maxim-ic.com/quick_view2.cfm/qv_pk/1111) transceiver. - - -## Caveats -Conforms to Arduino IDE 1.5 Library Specification v2.1 which requires Arduino IDE >= 1.5. - -Arduinos prior to the Mega have one serial port which must be connected to USB (FTDI) for uploading sketches and to the RS232/485 device/network for running sketches. You will need to disconnect pin 0 (RX) while uploading sketches. After a successful upload, you can reconnect pin 0. - - -## Support -Please [submit an issue](https://github.com/4-20ma/ModbusMaster/issues) for all questions, bug reports, and feature requests. Email requests will be politely redirected to the issue tracker so others may contribute to the discussion and requestors get a more timely response. - - -## Example -The library contains a few sketches that demonstrate use of the `ModbusMaster` library. You can find these in the [examples](https://github.com/4-20ma/ModbusMaster/tree/master/examples) folder. - -``` cpp -/* - - Basic.pde - example using ModbusMaster library - - Library:: ModbusMaster - Author:: Doc Walker <4-20ma@wvfans.net> - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#include - - -// instantiate ModbusMaster object -ModbusMaster node; - - -void setup() -{ - // use Serial (port 0); initialize Modbus communication baud rate - Serial.begin(19200); - - // communicate with Modbus slave ID 2 over Serial (port 0) - node.begin(2, Serial); -} - - -void loop() -{ - static uint32_t i; - uint8_t j, result; - uint16_t data[6]; - - i++; - - // set word 0 of TX buffer to least-significant word of counter (bits 15..0) - node.setTransmitBuffer(0, lowWord(i)); - - // set word 1 of TX buffer to most-significant word of counter (bits 31..16) - node.setTransmitBuffer(1, highWord(i)); - - // slave: write TX buffer to (2) 16-bit registers starting at register 0 - result = node.writeMultipleRegisters(0, 2); - - // slave: read (6) 16-bit registers starting at register 2 to RX buffer - result = node.readHoldingRegisters(2, 6); - - // do something with data if read is successful - if (result == node.ku8MBSuccess) - { - for (j = 0; j < 6; j++) - { - data[j] = node.getResponseBuffer(j); - } - } -} -``` - -_Project inspired by [Arduino Modbus Master](http://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-master)._ - - -## License & Authors - -- Author:: Doc Walker ([4-20ma@wvfans.net](mailto:4-20ma@wvfans.net)) -- Author:: Ag Primatic ([agprimatic@gmail.com](mailto:agprimatic@gmail.com)) -- Author:: Marius Kintel ([marius@kintel.net](mailto:marius@kintel.net)) - -``` -Copyright:: 2009-2016 Doc Walker - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -``` diff --git a/lib/ModbusMaster/Rakefile b/lib/ModbusMaster/Rakefile deleted file mode 100644 index b0619918..00000000 --- a/lib/ModbusMaster/Rakefile +++ /dev/null @@ -1,218 +0,0 @@ -# encoding: utf-8 -# -# Copyright:: 2009-2016 Doc Walker -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'git' -require 'github_changelog_generator/task' -require 'rake' -require 'rubygems' -require 'rake/version_task' # gem install version -require 'version' - -# requires additional packages on MacOS (including Homebrew): -# $ /usr/bin/ruby -e "$(curl -fsSL \ -# https://raw.githubusercontent.com/Homebrew/install/master/install)" -# $ brew install doxygen # generates documentation from source code -# $ brew cask install mactex # MacTeX - -Rake::VersionTask.new do |task| - # prevent auto-commit on version bump - task.with_git = false -end - -# adjust as appropriate -CWD = File.expand_path(File.dirname(__FILE__)) -DOXYFILE = 'Doxyfile' -GITHUB_USERNAME = '4-20ma' -GITHUB_REPO = 'ModbusMaster' -HEADER_FILE = "#{GITHUB_REPO}.h" -CHANGELOG_FILE = 'CHANGELOG.md' -PROPERTIES_FILE = 'library.properties' -VERSION_FILE = Version.version_file('').basename.to_s - - -task :default => :info - -desc 'Display instructions for public release' -task :info do - puts <<-EOF.gsub(/^\s{2}/, '') - - Instructions for public release - - - Update version, as appropriate: - - $ rake version:bump # or - $ rake version:bump:minor # or - $ rake version:bump:major # or - edit 'VERSION' file directly - - - Prepare release date, 'CHANGELOG.md' file, documentation: - - $ rake prepare - - - Review changes to 'CHANGELOG.md' file - This file is assembled using git commit messages; review for completeness. - - - Review html documentation files - These files are assembled using source code Doxygen tags; review for - for completeness. - - - Add & commit source files, tag, push to origin/master; - add & commit documentation files, push to origin/gh-pages: - - $ rake release - - EOF -end # task :info - - -desc "Prepare #{CHANGELOG_FILE} for release" -task :prepare => 'prepare:default' - -namespace :prepare do - task :default => [ - :release_date, - :library_properties, - :changelog, - :documentation - ] - - desc 'Prepare documentation' - task :documentation do - version = Version.current.to_s - - # update parameters in Doxyfile - file = File.join(CWD, 'doc', DOXYFILE) - - contents = IO.read(file) - contents.sub!(/(^PROJECT_NUMBER\s*=)(.*)$/) do |match| - "#{$1} v#{version}" - end # contents.sub!(...) - IO.write(file, contents) - - # chdir to doc/ and call doxygen to update documentation - Dir.chdir(to = File.join(CWD, 'doc')) - system('doxygen', DOXYFILE) - - # chdir to doc/latex and call doxygen to update documentation - Dir.chdir(from = File.join(CWD, 'doc', 'latex')) - system('make') - - # move/rename file to 'extras/GITHUB_REPO reference-x.y.pdf' - to = File.join(CWD, 'extras') - FileUtils.mv(File.join(from, 'refman.pdf'), - File.join(to, "#{GITHUB_REPO} reference-#{version}.pdf")) - end # task :documentation - - desc 'Prepare release history' - GitHubChangelogGenerator::RakeTask.new(:changelog) do |config| - config.add_issues_wo_labels = false - config.add_pr_wo_labels = false - config.enhancement_labels = [ - 'Type: Enhancement' - ] - config.bug_labels = ['Type: Bug'] - config.exclude_labels = ['Type: Question'] - config.header = '# ModbusMaster CHANGELOG' - config.include_labels = [ - 'Type: Bug', - 'Type: Enhancement', - 'Type: Feature Request', - 'Type: Maintenance' - ] - # config.since_tag = '0.1.0' - config.future_release = "v#{Version.current.to_s}" - config.user = GITHUB_USERNAME - config.project = GITHUB_REPO - end # GitHubChangelogGenerator::RakeTask.new - - desc 'Update version in library properties file' - task :library_properties do - version = Version.current.to_s - - file = File.join(CWD, PROPERTIES_FILE) - - contents = IO.read(file) - contents.sub!(/(version=\s*)(.*)$/) do |match| - "#{$1}#{version}" - end # contents.sub!(...) - IO.write(file, contents) - end # task :library_properties - - desc 'Update release date in header file' - task :release_date do - file = File.join(CWD, 'src', HEADER_FILE) - - contents = IO.read(file) - contents.sub!(/(\\date\s*)(.*)$/) do |match| - "#{$1}#{Time.now.strftime('%-d %b %Y')}" - end # contents.sub!(...) - IO.write(file, contents) - end # task :release_date - -end # namespace :prepare - - -desc 'Release source & documentation' -task :release => 'release:default' - -namespace :release do - task :default => [:source, :documentation] - - desc 'Commit documentation changes related to version bump' - task :documentation do - version = Version.current.to_s - cwd = File.expand_path(File.join(File.dirname(__FILE__), 'doc', 'html')) - g = Git.open(cwd) - - # `git add .` - g.add - - # remove each deleted item - g.status.deleted.each do |item| - g.remove(item[0]) - end # g.status.deleted.each - - # commit changes if items added, changed, or deleted - if g.status.added.size > 0 || g.status.changed.size > 0 || - g.status.deleted.size > 0 then - message = "Update documentation for v#{version}" - puts g.commit(message) - else - puts "No changes to commit v#{version}" - end # if g.status.added.size > 0 || g.status.changed.size > 0... - - g.push('origin', 'gh-pages') - end # task :documentation - - desc 'Commit source changes related to version bump' - task :source do - version = Version.current.to_s - `git add \ - doc/#{DOXYFILE} \ - "extras/#{GITHUB_REPO} reference-#{version}.pdf" \ - src/#{HEADER_FILE} \ - #{CHANGELOG_FILE} \ - #{PROPERTIES_FILE} \ - #{VERSION_FILE} \ - ` - `git commit -m 'Version bump to v#{version}'` - `git tag -a -f -m 'Version v#{version}' v#{version}` - `git push origin master` - `git push --tags` - end # task :source - -end # namespace :release diff --git a/lib/ModbusMaster/STYLE.md b/lib/ModbusMaster/STYLE.md deleted file mode 100644 index 00516e0f..00000000 --- a/lib/ModbusMaster/STYLE.md +++ /dev/null @@ -1,372 +0,0 @@ -ModbusMaster Style Guide -======================== - -The following references provide sound guidance for writing C/C++ code for the Arduino platform. - -- [Arduino API Style Guide (AASG)](http://www.arduino.cc/en/Reference/APIStyleGuide) -- [Bjarne Stroustrup's C++ Style Guide](http://www.stroustrup.com/bs_faq2.html) -- [JSF Air Vehicle C++ Coding Standards (JSFAV)](http://www.stroustrup.com/JSF-AV-rules.pdf) - -Opinions about style and generally accepted usage patterns may vary widely. I've carefully chosen a few key items to emphasize and enforce for this library in order to promote readability, usability, and safe coding practices. **Pull requests will follow these guidelines in order to be considered**. - - -General \[AASG\] -------- - -Use the established Arduino core libraries and styles. - -- Use `read()` to read inputs, and `write()` to write to outputs, e.g. `digitalRead()`, `analogWrite()`, etc. -- Use the `Stream.h` and `Print.h` libraries when dealing with byte streams. If it’s not appropriate, at least try to use its API as a model. For more on this, see below. -- For network applications, use the Client and Server libraries as the basis. -- Use `begin()` to initialize a library instance, usually with some settings. Use `end()` to stop it. -- Use camelCase function names, not underscore. For example, `analogRead`, not `analog_read`. Or `myNewFunction`, not `my_new_function`. We've adopted this from Processing.org for readability's sake. Refer to AV Rule 45 and AV Rule 51. -- When using serial communication, allow the user to specify any `Stream` object, rather than hard-coding `Serial`. This will make the library compatible with all serial ports on Mega and the Due, and can also use alternate interfaces like `SoftwareSerial`. The `Stream` object can be passed to the library's constructor or to a `begin()` function (as a reference, not a pointer). See Firmata 2.3 or XBee 0.4 for examples of each approach. - - -Rules \[JSFAV 4.2\] ------ - -#### Should, Will, and Shall Rules - -There are three types of rules: **should**, **will**, and **shall** rules. Each rule contains either a **"should"**, **"will"** or a **"shall"** in bold letters indicating its type. - -- **Should** rules are advisory rules. They strongly suggest the recommended way of doing things. -- **Will** rules are intended to be mandatory requirements. It is expected that they will be followed, but they do not require verification. They are limited to non-safety-critical requirements that cannot be easily verified (e.g., naming conventions). -- **Shall** rules are mandatory requirements. They must be followed and they require verification (either automatic or manual). - - -Pre-Processing Directives \[JSFAV 4.6\] -------------------------- -Since the pre-processor knows nothing about C++, it should not be used to do what can otherwise be done in C++. - -- AV Rule 26 - - Only the following pre-processor directives shall be used: - - 1. `#ifndef` - 1. `#define` - 1. `#endif` - 1. `#include` - - **Rationale**: Limit the use of the pre-processor to those cases where it is necessary. - -#### \#ifndef and \#endif Pre-Processing Directives - -- AV Rule 27 - - `#ifndef`, `#define` and `#endif` **will** be used to prevent multiple inclusions of the same header file. Other techniques to prevent the multiple inclusions of header files **will not** be used. - - **Rationale**: Eliminate multiple inclusions of the same header file in a standard way. - -- AV Rule 28 - - The `#ifndef` and `#endif` pre-processor directives **will** only be used as defined in AV Rule 27 to prevent multiple inclusions of the same header file. - - **Rationale**: Conditional code compilation should be kept to a minimum as it can significantly obscure testing and maintenance efforts. - -#### \#define Pre-Processing Directive - -- AV Rule 29 - - The `#define` pre-processor directive **shall not** be used to create inline macros. Inline functions **shall** be used instead. - - **Rationale**: Inline functions do not require text substitutions and behave well when called with arguments (e.g. type checking is performed). - -- AV Rule 30 - - The `#define` pre-processor directive **shall not** be used to define constant values. Instead, the `const` qualifier shall be applied to variable declarations to specify constant values. - - **Rationale**: `const` variables follow scope rules, are subject to type checking and do not require text substitutions (which can be confusing or misleading). - -- AV Rule 31 - - The `#define` pre-processor directive **will** only be used as part of the technique to prevent multiple inclusions of the same header file. - - **Rationale**: `#define` can be used to specify conditional compilation (AV Rule 27 and AV Rule 28), inline macros (AV Rule 29) and constants (AV Rule 30). This rule specifies that the only allowable use of `#define` is to prevent multiple includes of the same header file (AV Rule 27). - -#### \#include Pre-Processing Directive - -- AV Rule 32 - - The `#include` pre-processor directive **will** only be used to include header (\*.h) files. - - **Rationale**: Clarity. The only files included in a .cpp file should be the relevant header (\*.h) files. - - -Header Files \[JSFAV 4.7\] ------------- - -- AV Rule 33 - - The `#include` directive **shall** use the `` notation to include header files. - - **Rationale**: The include form `"filename.h"` is typically used to include local header files. However, due to the unfortunate divergence in vendor implementations, only the `` form will be used. - -- AV Rule 35 - - A header file **will** contain a mechanism that prevents multiple inclusions of itself. - - **Rationale**: Avoid accidental header file recursion. Note AV Rule 27 specifies the mechanism by which multiple inclusions are to be eliminated whereas this rule (AV Rule 35) specifies that each header file must use that mechanism. - -- AV Rule 37 - - Header (include) files **should** include only those header files that are required for them to successfully compile. Files that are only used by the associated .cpp file should be placed in the .cpp file—not the .h file. - - **Rationale**: The `#include` statements in a header file define the dependencies of the file. Fewer dependencies imply looser couplings and hence a smaller ripple-effect when the header file is required to change. - - -Style \[JSFAV 4.9\] ------ -Imposing constraints on the format of syntactic elements makes source code easier to read due to consistency in form and appearance. - -- AV Rule 41 (modified) - - Source lines **will** be kept to a length of 78 characters or less. - - **Rationale**: Readability and style. Very long source lines can be difficult to read and understand. - -- AV Rule 42 - - Each expression-statement **will** be on a separate line. - - **Rationale**: Simplicity, readability, and style. - -- AV Rule 43 (modified) - - Tabs **will** be avoided. - - **Rationale**: Tabs are interpreted differently across various editors and printers. - -- AV Rule 44 - - All indentations **will** be at least two spaces and be consistent within the same source file. - - **Rationale**: Readability and style. - -#### Naming Identifiers - -The choice of identifier names should: - -- Suggest the usage of the identifier. -- Consist of a descriptive name that is short yet meaningful. -- Be long enough to avoid name conflicts, but not excessive in length. -- Include abbreviations that are generally accepted. - -Note: In general, the above guidelines should be followed. However, conventional usage of simple identifiers (i, x, y, p, etc.) in small scopes can lead to cleaner code and will therefore be permitted. - -Additionally, the term ‘word’ in the following naming convention rules may be used to refer to a word, an acronym, an abbreviation, or a number. - -- AV Rule 45 - - All words in an identifier **will** be separated by the '\_' character. - - **Exception**: Function names follow the camelCase convention according to the Arduino API Style Guide. Refer to AV Rule 51. - - **Rationale**: Readability and Style. - -- AV Rule 47 (modified) - - Identifiers **should not** begin with the underscore character '\_'. - - **Exception**: Currently, private members of the `ModbusMaster` class begin with '\_'. - - **Rationale**: '\_' is often used as the first character in the name of library functions (e.g. `_main`, `_exit`, etc.) In order to avoid name collisions, identifiers should not begin with '\_'. - -- AV Rule 49 - - All acronyms in an identifier **will** be composed of uppercase letters. - - Note: An acronym will always be in uppercase, even if the acronym is located in a portion of an identifier that is specified to be lowercase by other rules. - - **Rationale**: Readability. - -- AV Rule 50 - - The first word of the name of a class, structure, namespace, enumeration, or type created with typedef **will** begin with an uppercase letter. All others letters will be lowercase. - - **Rationale**: Style. - - **Exception**: The first letter of a typedef name may be in lowercase in order to conform to a standard library interface or when used as a replacement for fundamental types. - -- AV Rule 51 (modified) - - All letters contained in *function* names **will** be composed of a mix of lowercase and uppercase letters in camelCase format (e.g. first letter of first word is lowercase and first letter of each subsequent word is uppercase). This is to maintain consistency with the Arduion API Style Guide. Refer to AV Rule 45. - - All letters contained in *variable* names **will** be composed entirely of lowercase letters. - - **Rationale**: Style. - -- AV Rule 52 - - Identifiers for constant and enumerator values **shall** be lowercase. - - **Rationale**: Although it is an accepted convention to use uppercase letters for constants and enumerators, it is possible for third party libraries to replace constant/enumerator names as part of the macro substitution process (macros are also typically represented with uppercase letters). - -#### Classes - -- AV Rule 57 - - The public, protected, and private sections of a class **will** be declared in that order (the public section is declared before the protected section which is declared before the private section). - - **Rationale**: By placing the public section first, everything that is of interest to a user is gathered in the beginning of the class definition. The protected section may be of interest to designers when considering inheriting from the class. The private section contains details that should be of the least general interest. - -#### Functions - -- AV Rule 58 - - When declaring and defining functions with more than two parameters, the leading parenthesis and the first argument **will** be written on the same line as the function name. Each additional argument will be written on a separate line (with the closing parenthesis directly after the last argument). - - **Rationale**: Readability and style. - -#### Blocks - -- AV Rule 59 - - The statements forming the body of an `if`, `else if`, `else`, `while`, `do...while` or `for` statement **shall** always be enclosed in braces, even if the braces form an empty block. - - **Rationale**: Readability. It can be difficult to see ';' when it appears by itself. - -- AV Rule 60 - - Braces ('{}') which enclose a block will be placed in the same column, on separate lines directly before and after the block. - - **Example**: - ``` - if (var_name == true) - { - } - else - { - } - ``` - -- AV Rule 61 - - Braces ('{}') which enclose a block **will** have nothing else on the line except comments (if necessary). - -#### Pointers and References - -- AV Rule 62 - - The dereference operator '\*' and the address-of operator '&' **will** be directly connected with the type-specifier. - - **Rationale**: The `int32* p;` form emphasizes type over syntax while the `int32 *p;` form emphasizes syntax over type. Although both forms are equally valid C++, the heavy emphasis on types in C++ suggests that `int32* p;` is the preferable form. - - **Examples**: - ``` - int32* p; // correct - int32 *p; // incorrect - int32* p, q; // probable error - ``` - -#### Miscellaneous - -- AV Rule 63 - - Spaces **will not** be used around '.' or '->', nor between unary operators and operands. - - **Rationale**: Readability and style. - - -Classes \[JSFAV 4.10\] -------- - -#### const Member Functions - -- AV Rule 69 - - A member function that does not affect the state of an object (its instance variables) **will** be declared `const`. - - Member functions should be `const` by default. Only when there is a clear, explicit reason should the `const` modifier on member functions be omitted. - - **Rationale**: Declaring a member function `const` is a means of ensuring that objects will not be modified when they should not. Furthermore, C++ allows member functions to be overloaded on their `const`-ness. - - -Initialization \[JSFAV 4.16\] --------------- - -- AV Rule 142 - - All variables **shall** be initialized before use. - - **Rationale**: Prevent the use of variables before they have been properly initialized. - -- AV Rule 143 - - Variables **will not** be introduced until they can be initialized with meaningful values. - - **Rationale**: Prevent clients from accessing variables without meaningful values. - - -Constants \[JSFAV 4.18\] ---------- - -- AV Rule 149 - - Octal constants (other than zero) **shall not** be used. - - **Rationale**: Any integer constant beginning with a zero ('0') is defined by the C++ standard to be an octal constant. Due to the confusion this causes, octal constants should be avoided. - - Note: Hexadecimal numbers and zero (which is also an octal constant) are allowed. - -- AV Rule 150 - - Hexadecimal constants **will** be represented using all uppercase letters. - - -Variables \[JSFAV 4.19\] ---------- - -- AV Rule 152 - - Multiple variable declarations **shall not** be allowed on the same line. - - **Rationale**: Increases readability and prevents confusion (see also AV Rule 62). - - **Examples**: - ``` - int32 p; // correct - int32 q; // correct - int32 p, q; // probable error - int32 first_button_on_the_left_box, i; // incorrect; easy to overlook i - ``` - - -Flow Control Structures \[JSFAV 4.24\] ------------------------ - -- AV Rule 188 - - Labels **will** not be used, except in switch statements. - - **Rationale**: Labels are typically either used in switch statements or are as the targets for goto statements. See exception given in AV Rule 189. - -- AV Rule 189 - - The `goto` statement **shall not** be used. - - **Rationale**: Frequent use of the `goto` statement tends to lead to code that is both difficult to read and maintain. - -- AV Rule 190 - - The `continue` statement **shall not** be used. - -- AV Rule 191 - - The `break` statement **shall not** be used (except to terminate the cases of a switch statement). - - **Exception**: The `break` statement may be used to "break" out of a single loop provided the alternative would obscure or otherwise significantly complicate the control logic. - -- AV Rule 192 - - All `if`, `else if` constructs **will** contain either a final `else` clause or a comment indicating why a final `else` clause is not necessary. - - **Rationale**: Provide a defensive strategy to ensure that all cases are handled by an `else if` series. - - Note: This rule only applies when an `if` statement is followed by one or more `else if`’s. - -- AV Rule 193 - - Every non-empty `case` clause in a `switch` statement **shall** be terminated with a `break` statement. - - **Rationale**: Eliminates potentially confusing behavior since execution will fall through to the code of the next `case` clause if a `break` statement does not terminate the previous `case` clause. diff --git a/lib/ModbusMaster/VERSION b/lib/ModbusMaster/VERSION deleted file mode 100644 index 38f77a65..00000000 --- a/lib/ModbusMaster/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.0.1 diff --git a/lib/ModbusMaster/examples/Basic/Basic.pde b/lib/ModbusMaster/examples/Basic/Basic.pde deleted file mode 100644 index 74c199b1..00000000 --- a/lib/ModbusMaster/examples/Basic/Basic.pde +++ /dev/null @@ -1,69 +0,0 @@ -/* - - Basic.pde - example using ModbusMaster library - - Library:: ModbusMaster - Author:: Doc Walker <4-20ma@wvfans.net> - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#include - - -// instantiate ModbusMaster object -ModbusMaster node; - - -void setup() -{ - // use Serial (port 0); initialize Modbus communication baud rate - Serial.begin(19200); - - // communicate with Modbus slave ID 2 over Serial (port 0) - node.begin(2, Serial); -} - - -void loop() -{ - static uint32_t i; - uint8_t j, result; - uint16_t data[6]; - - i++; - - // set word 0 of TX buffer to least-significant word of counter (bits 15..0) - node.setTransmitBuffer(0, lowWord(i)); - - // set word 1 of TX buffer to most-significant word of counter (bits 31..16) - node.setTransmitBuffer(1, highWord(i)); - - // slave: write TX buffer to (2) 16-bit registers starting at register 0 - result = node.writeMultipleRegisters(0, 2); - - // slave: read (6) 16-bit registers starting at register 2 to RX buffer - result = node.readHoldingRegisters(2, 6); - - // do something with data if read is successful - if (result == node.ku8MBSuccess) - { - for (j = 0; j < 6; j++) - { - data[j] = node.getResponseBuffer(j); - } - } -} diff --git a/lib/ModbusMaster/examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde b/lib/ModbusMaster/examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde deleted file mode 100644 index e91fb338..00000000 --- a/lib/ModbusMaster/examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde +++ /dev/null @@ -1,143 +0,0 @@ -/* - - PhoenixContact_nanoLC.pde - example using ModbusMaster library - to communicate with PHOENIX CONTACT nanoLine controller. - - Library:: ModbusMaster - Author:: Doc Walker <4-20ma@wvfans.net> - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#include - -// discrete coils -#define NANO_DO(n) (0x0000 + n) ///< returns nanoLC discrete output address -#define NANO_FLAG(n) (0x1000 + n) ///< returns nanoLC flag address - -// discrete inputs -#define NANO_DI(n) (0x0000 + n) ///< returns nanoLC discrete input address - -// analog holding registers -#define NANO_REG(n) (0x0000 + 2 * n) ///< returns nanoLC holding register address -#define NANO_AO(n) (0x1000 + 2 * n) ///< returns nanoLC analog output address -#define NANO_TCP(n) (0x2000 + 2 * n) ///< returns nanoLC timer/counter preset address -#define NANO_OTP(n) (0x3000 + 2 * n) ///< returns nanoLC discrete output preset address -#define NANO_HSP(n) (0x4000 + 2 * n) ///< returns nanoLC high-speed counter preset address -#define NANO_TCA(n) (0x5000 + 2 * n) ///< returns nanoLC timer/counter accumulator address -#define NANO_OTA(n) (0x6000 + 2 * n) ///< returns nanoLC discrete output accumulator address -#define NANO_HSA(n) (0x7000 + 2 * n) ///< returns nanoLC high-speed counter accumulator address - -// analog input registers -#define NANO_AI(n) (0x0000 + 2 * n) ///< returns nanoLC analog input address - - -// instantiate ModbusMaster object -ModbusMaster nanoLC; - - -void setup() -{ - // use Serial (port 0); initialize Modbus communication baud rate - Serial.begin(19200); - - // communicate with Modbus slave ID 1 over Serial (port 0) - nanoLC.begin(1, Serial); -} - - -void loop() -{ - static uint32_t u32ShiftRegister; - static uint32_t i; - uint8_t u8Status; - - u32ShiftRegister = ((u32ShiftRegister < 0x01000000) ? (u32ShiftRegister << 4) : 1); - if (u32ShiftRegister == 0) u32ShiftRegister = 1; - i++; - - // set word 0 of TX buffer to least-significant word of u32ShiftRegister (bits 15..0) - nanoLC.setTransmitBuffer(0, lowWord(u32ShiftRegister)); - - // set word 1 of TX buffer to most-significant word of u32ShiftRegister (bits 31..16) - nanoLC.setTransmitBuffer(1, highWord(u32ShiftRegister)); - - // set word 2 of TX buffer to least-significant word of i (bits 15..0) - nanoLC.setTransmitBuffer(2, lowWord(i)); - - // set word 3 of TX buffer to most-significant word of i (bits 31..16) - nanoLC.setTransmitBuffer(3, highWord(i)); - - // write TX buffer to (4) 16-bit registers starting at NANO_REG(1) - // read (4) 16-bit registers starting at NANO_REG(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..3) - nanoLC.readWriteMultipleRegisters(NANO_REG(0), 4, NANO_REG(1), 4); - - // write lowWord(u32ShiftRegister) to single 16-bit register starting at NANO_REG(3) - nanoLC.writeSingleRegister(NANO_REG(3), lowWord(u32ShiftRegister)); - - // write highWord(u32ShiftRegister) to single 16-bit register starting at NANO_REG(3) + 1 - nanoLC.writeSingleRegister(NANO_REG(3) + 1, highWord(u32ShiftRegister)); - - // set word 0 of TX buffer to nanoLC.getResponseBuffer(0) (bits 15..0) - nanoLC.setTransmitBuffer(0, nanoLC.getResponseBuffer(0)); - - // set word 1 of TX buffer to nanoLC.getResponseBuffer(1) (bits 31..16) - nanoLC.setTransmitBuffer(1, nanoLC.getResponseBuffer(1)); - - // write TX buffer to (2) 16-bit registers starting at NANO_REG(4) - nanoLC.writeMultipleRegisters(NANO_REG(4), 2); - - // read 17 coils starting at NANO_FLAG(0) to RX buffer - // bits 15..0 are available via nanoLC.getResponseBuffer(0) - // bit 16 is available via zero-padded nanoLC.getResponseBuffer(1) - nanoLC.readCoils(NANO_FLAG(0), 17); - - // read (66) 16-bit registers starting at NANO_REG(0) to RX buffer - // generates Modbus exception ku8MBIllegalDataAddress (0x02) - u8Status = nanoLC.readHoldingRegisters(NANO_REG(0), 66); - if (u8Status == nanoLC.ku8MBIllegalDataAddress) - { - // read (64) 16-bit registers starting at NANO_REG(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..63) - u8Status = nanoLC.readHoldingRegisters(NANO_REG(0), 64); - } - - // read (8) 16-bit registers starting at NANO_AO(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..7) - nanoLC.readHoldingRegisters(NANO_AO(0), 8); - - // read (64) 16-bit registers starting at NANO_TCP(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..63) - nanoLC.readHoldingRegisters(NANO_TCP(0), 64); - - // read (64) 16-bit registers starting at NANO_OTP(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..63) - nanoLC.readHoldingRegisters(NANO_OTP(0), 64); - - // read (64) 16-bit registers starting at NANO_TCA(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..63) - nanoLC.readHoldingRegisters(NANO_TCA(0), 64); - - // read (64) 16-bit registers starting at NANO_OTA(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..63) - nanoLC.readHoldingRegisters(NANO_OTA(0), 64); - - // read (8) 16-bit registers starting at NANO_AI(0) to RX buffer - // data is available via nanoLC.getResponseBuffer(0..7) - nanoLC.readInputRegisters(NANO_AI(0), 8); -} - diff --git a/lib/ModbusMaster/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino b/lib/ModbusMaster/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino deleted file mode 100644 index c19607f0..00000000 --- a/lib/ModbusMaster/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino +++ /dev/null @@ -1,98 +0,0 @@ -/* - - RS485_HalfDuplex.pde - example using ModbusMaster library to communicate - with EPSolar LS2024B controller using a half-duplex RS485 transceiver. - - This example is tested against an EPSolar LS2024B solar charge controller. - See here for protocol specs: - http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf - - Library:: ModbusMaster - Author:: Marius Kintel - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#include - -/*! - We're using a MAX485-compatible RS485 Transceiver. - Rx/Tx is hooked up to the hardware serial port at 'Serial'. - The Data Enable and Receiver Enable pins are hooked up as follows: -*/ -#define MAX485_DE 3 -#define MAX485_RE_NEG 2 - -// instantiate ModbusMaster object -ModbusMaster node; - -void preTransmission() -{ - digitalWrite(MAX485_RE_NEG, 1); - digitalWrite(MAX485_DE, 1); -} - -void postTransmission() -{ - digitalWrite(MAX485_RE_NEG, 0); - digitalWrite(MAX485_DE, 0); -} - -void setup() -{ - pinMode(MAX485_RE_NEG, OUTPUT); - pinMode(MAX485_DE, OUTPUT); - // Init in receive mode - digitalWrite(MAX485_RE_NEG, 0); - digitalWrite(MAX485_DE, 0); - - // Modbus communication runs at 115200 baud - Serial.begin(115200); - - // Modbus slave ID 1 - node.begin(1, Serial); - // Callbacks allow us to configure the RS485 transceiver correctly - node.preTransmission(preTransmission); - node.postTransmission(postTransmission); -} - -bool state = true; - -void loop() -{ - uint8_t result; - uint16_t data[6]; - - // Toggle the coil at address 0x0002 (Manual Load Control) - result = node.writeSingleCoil(0x0002, state); - state = !state; - - // Read 16 registers starting at 0x3100) - result = node.readInputRegisters(0x3100, 16); - if (result == node.ku8MBSuccess) - { - Serial.print("Vbatt: "); - Serial.println(node.getResponseBuffer(0x04)/100.0f); - Serial.print("Vload: "); - Serial.println(node.getResponseBuffer(0xC0)/100.0f); - Serial.print("Pload: "); - Serial.println((node.getResponseBuffer(0x0D) + - node.getResponseBuffer(0x0E) << 16)/100.0f); - } - - delay(1000); -} - diff --git a/lib/ModbusMaster/extras/ModbusMaster reference-2.0.1.pdf b/lib/ModbusMaster/extras/ModbusMaster reference-2.0.1.pdf deleted file mode 100644 index cce1e04ec2da2f55d720c2d2d55a38e576b14122..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206477 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 diff --git a/lib/ModbusMaster/extras/README.txt b/lib/ModbusMaster/extras/README.txt deleted file mode 100644 index a5720f68..00000000 --- a/lib/ModbusMaster/extras/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -Documentation is available at: - http://4-20ma.github.com/ModbusMaster - -Alternatively, you can download the HTML files at: - [tarball] https://github.com/4-20ma/ModbusMaster/tarball/gh-pages - [zipball] https://github.com/4-20ma/ModbusMaster/zipball/gh-pages diff --git a/lib/ModbusMaster/keywords.txt b/lib/ModbusMaster/keywords.txt deleted file mode 100644 index fd92fdb4..00000000 --- a/lib/ModbusMaster/keywords.txt +++ /dev/null @@ -1,50 +0,0 @@ -####################################### -# Syntax Coloring Map For ModbusMaster -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -ModbusMaster KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -lowWord KEYWORD2 -highWord KEYWORD2 -LONG KEYWORD2 - -begin KEYWORD2 - -getResponseBuffer KEYWORD2 -clearResponseBuffer KEYWORD2 -setTransmitBuffer KEYWORD2 -clearTransmitBuffer KEYWORD2 - -readCoils KEYWORD2 -readDiscreteInputs KEYWORD2 -readHoldingRegisters KEYWORD2 -readInputRegisters KEYWORD2 -writeSingleCoil KEYWORD2 -writeSingleRegister KEYWORD2 -writeMultipleCoils KEYWORD2 -writeMultipleRegisters KEYWORD2 -maskWriteRegister KEYWORD2 -readWriteMultipleRegisters KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - -ku8MBIllegalFunction LITERAL1 -ku8MBIllegalDataAddress LITERAL1 -ku8MBIllegalDataValue LITERAL1 -ku8MBSlaveDeviceFailure LITERAL1 - -ku8MBSuccess LITERAL1 -ku8MBInvalidSlaveID LITERAL1 -ku8MBInvalidFunction LITERAL1 -ku8MBResponseTimedOut LITERAL1 -ku8MBInvalidCRC LITERAL1 diff --git a/lib/ModbusMaster/library.properties b/lib/ModbusMaster/library.properties deleted file mode 100644 index a2be2fa7..00000000 --- a/lib/ModbusMaster/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=ModbusMaster -version=2.0.1 -author=Doc Walker -maintainer=Doc Walker <4-20ma@wvfans.net> -sentence=Enlighten your Arduino to be a Modbus master. -paragraph=Enables communication with Modbus slaves over RS232/485 (via RTU protocol). Requires an RS232/485 transceiver. -category=Communication -url=https://github.com/4-20ma/ModbusMaster -architectures=* -includes=ModbusMaster.h diff --git a/lib/ModbusMaster/src/ModbusMaster.cpp b/lib/ModbusMaster/src/ModbusMaster.cpp deleted file mode 100644 index 4169e585..00000000 --- a/lib/ModbusMaster/src/ModbusMaster.cpp +++ /dev/null @@ -1,876 +0,0 @@ -/** -@file -Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol). -*/ -/* - - ModbusMaster.cpp - Arduino library for communicating with Modbus slaves - over RS232/485 (via RTU protocol). - - Library:: ModbusMaster - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - - -/* _____PROJECT INCLUDES_____________________________________________________ */ -#include "ModbusMaster.h" - - -/* _____GLOBAL VARIABLES_____________________________________________________ */ - - -/* _____PUBLIC FUNCTIONS_____________________________________________________ */ -/** -Constructor. - -Creates class object; initialize it using ModbusMaster::begin(). - -@ingroup setup -*/ -ModbusMaster::ModbusMaster(void) -{ - _idle = 0; - _preTransmission = 0; - _postTransmission = 0; -} - -/** -Initialize class object. - -Assigns the Modbus slave ID and serial port. -Call once class has been instantiated, typically within setup(). - -@param slave Modbus slave ID (1..255) -@param &serial reference to serial port object (Serial, Serial1, ... Serial3) -@ingroup setup -*/ -void ModbusMaster::begin(uint8_t slave, Stream &serial) -{ -// txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t)); - _u8MBSlave = slave; - _serial = &serial; - _u8TransmitBufferIndex = 0; - u16TransmitBufferLength = 0; - -#if __MODBUSMASTER_DEBUG__ - pinMode(__MODBUSMASTER_DEBUG_PIN_A__, OUTPUT); - pinMode(__MODBUSMASTER_DEBUG_PIN_B__, OUTPUT); -#endif -} - - -void ModbusMaster::beginTransmission(uint16_t u16Address) -{ - _u16WriteAddress = u16Address; - _u8TransmitBufferIndex = 0; - u16TransmitBufferLength = 0; -} - -// eliminate this function in favor of using existing MB request functions -uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity) -{ - uint8_t read; - // clamp to buffer length - if (quantity > ku8MaxBufferSize) - { - quantity = ku8MaxBufferSize; - } - // set rx buffer iterator vars - _u8ResponseBufferIndex = 0; - _u8ResponseBufferLength = read; - - return read; -} - - -void ModbusMaster::sendBit(bool data) -{ - uint8_t txBitIndex = u16TransmitBufferLength % 16; - if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize) - { - if (0 == txBitIndex) - { - _u16TransmitBuffer[_u8TransmitBufferIndex] = 0; - } - bitWrite(_u16TransmitBuffer[_u8TransmitBufferIndex], txBitIndex, data); - u16TransmitBufferLength++; - _u8TransmitBufferIndex = u16TransmitBufferLength >> 4; - } -} - - -void ModbusMaster::send(uint16_t data) -{ - if (_u8TransmitBufferIndex < ku8MaxBufferSize) - { - _u16TransmitBuffer[_u8TransmitBufferIndex++] = data; - u16TransmitBufferLength = _u8TransmitBufferIndex << 4; - } -} - - -void ModbusMaster::send(uint32_t data) -{ - send(lowWord(data)); - send(highWord(data)); -} - - -void ModbusMaster::send(uint8_t data) -{ - send(word(data)); -} - - - - - - - - - -uint8_t ModbusMaster::available(void) -{ - return _u8ResponseBufferLength - _u8ResponseBufferIndex; -} - - -uint16_t ModbusMaster::receive(void) -{ - if (_u8ResponseBufferIndex < _u8ResponseBufferLength) - { - return _u16ResponseBuffer[_u8ResponseBufferIndex++]; - } - else - { - return 0xFFFF; - } -} - - - - - - - - -/** -Set idle time callback function (cooperative multitasking). - -This function gets called in the idle time between transmission of data -and response from slave. Do not call functions that read from the serial -buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other -serial ports, etc. is permitted within callback function. - -@see ModbusMaster::ModbusMasterTransaction() -*/ -void ModbusMaster::idle(void (*idle)()) -{ - _idle = idle; -} - -/** -Set pre-transmission callback function. - -This function gets called just before a Modbus message is sent over serial. -Typical usage of this callback is to enable an RS485 transceiver's -Driver Enable pin, and optionally disable its Receiver Enable pin. - -@see ModbusMaster::ModbusMasterTransaction() -@see ModbusMaster::postTransmission() -*/ -void ModbusMaster::preTransmission(void (*preTransmission)()) -{ - _preTransmission = preTransmission; -} - -/** -Set post-transmission callback function. - -This function gets called after a Modbus message has finished sending -(i.e. after all data has been physically transmitted onto the serial -bus). - -Typical usage of this callback is to enable an RS485 transceiver's -Receiver Enable pin, and disable its Driver Enable pin. - -@see ModbusMaster::ModbusMasterTransaction() -@see ModbusMaster::preTransmission() -*/ -void ModbusMaster::postTransmission(void (*postTransmission)()) -{ - _postTransmission = postTransmission; -} - - -/** -Retrieve data from response buffer. - -@see ModbusMaster::clearResponseBuffer() -@param u8Index index of response buffer array (0x00..0x3F) -@return value in position u8Index of response buffer (0x0000..0xFFFF) -@ingroup buffer -*/ -uint16_t ModbusMaster::getResponseBuffer(uint8_t u8Index) -{ - if (u8Index < ku8MaxBufferSize) - { - return _u16ResponseBuffer[u8Index]; - } - else - { - return 0xFFFF; - } -} - - -/** -Clear Modbus response buffer. - -@see ModbusMaster::getResponseBuffer(uint8_t u8Index) -@ingroup buffer -*/ -void ModbusMaster::clearResponseBuffer() -{ - uint8_t i; - - for (i = 0; i < ku8MaxBufferSize; i++) - { - _u16ResponseBuffer[i] = 0; - } -} - - -/** -Place data in transmit buffer. - -@see ModbusMaster::clearTransmitBuffer() -@param u8Index index of transmit buffer array (0x00..0x3F) -@param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF) -@return 0 on success; exception number on failure -@ingroup buffer -*/ -uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) -{ - if (u8Index < ku8MaxBufferSize) - { - _u16TransmitBuffer[u8Index] = u16Value; - return ku8MBSuccess; - } - else - { - return ku8MBIllegalDataAddress; - } -} - - -/** -Clear Modbus transmit buffer. - -@see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) -@ingroup buffer -*/ -void ModbusMaster::clearTransmitBuffer() -{ - uint8_t i; - - for (i = 0; i < ku8MaxBufferSize; i++) - { - _u16TransmitBuffer[i] = 0; - } -} - - -/** -Modbus function 0x01 Read Coils. - -This function code is used to read from 1 to 2000 contiguous status of -coils in a remote device. The request specifies the starting address, -i.e. the address of the first coil specified, and the number of coils. -Coils are addressed starting at zero. - -The coils in the response buffer are packed as one coil per bit of the -data field. Status is indicated as 1=ON and 0=OFF. The LSB of the first -data word contains the output addressed in the query. The other coils -follow toward the high order end of this word and from low order to high -order in subsequent words. - -If the returned quantity is not a multiple of sixteen, the remaining -bits in the final data word will be padded with zeros (toward the high -order end of the word). - -@param u16ReadAddress address of first coil (0x0000..0xFFFF) -@param u16BitQty quantity of coils to read (1..2000, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup discrete -*/ -uint8_t ModbusMaster::readCoils(uint16_t u16ReadAddress, uint16_t u16BitQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16BitQty; - return ModbusMasterTransaction(ku8MBReadCoils); -} - - -/** -Modbus function 0x02 Read Discrete Inputs. - -This function code is used to read from 1 to 2000 contiguous status of -discrete inputs in a remote device. The request specifies the starting -address, i.e. the address of the first input specified, and the number -of inputs. Discrete inputs are addressed starting at zero. - -The discrete inputs in the response buffer are packed as one input per -bit of the data field. Status is indicated as 1=ON; 0=OFF. The LSB of -the first data word contains the input addressed in the query. The other -inputs follow toward the high order end of this word, and from low order -to high order in subsequent words. - -If the returned quantity is not a multiple of sixteen, the remaining -bits in the final data word will be padded with zeros (toward the high -order end of the word). - -@param u16ReadAddress address of first discrete input (0x0000..0xFFFF) -@param u16BitQty quantity of discrete inputs to read (1..2000, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup discrete -*/ -uint8_t ModbusMaster::readDiscreteInputs(uint16_t u16ReadAddress, - uint16_t u16BitQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16BitQty; - return ModbusMasterTransaction(ku8MBReadDiscreteInputs); -} - - -/** -Modbus function 0x03 Read Holding Registers. - -This function code is used to read the contents of a contiguous block of -holding registers in a remote device. The request specifies the starting -register address and the number of registers. Registers are addressed -starting at zero. - -The register data in the response buffer is packed as one word per -register. - -@param u16ReadAddress address of the first holding register (0x0000..0xFFFF) -@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::readHoldingRegisters(uint16_t u16ReadAddress, - uint16_t u16ReadQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16ReadQty; - return ModbusMasterTransaction(ku8MBReadHoldingRegisters); -} - - -/** -Modbus function 0x04 Read Input Registers. - -This function code is used to read from 1 to 125 contiguous input -registers in a remote device. The request specifies the starting -register address and the number of registers. Registers are addressed -starting at zero. - -The register data in the response buffer is packed as one word per -register. - -@param u16ReadAddress address of the first input register (0x0000..0xFFFF) -@param u16ReadQty quantity of input registers to read (1..125, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::readInputRegisters(uint16_t u16ReadAddress, - uint8_t u16ReadQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16ReadQty; - return ModbusMasterTransaction(ku8MBReadInputRegisters); -} - - -/** -Modbus function 0x05 Write Single Coil. - -This function code is used to write a single output to either ON or OFF -in a remote device. The requested ON/OFF state is specified by a -constant in the state field. A non-zero value requests the output to be -ON and a value of 0 requests it to be OFF. The request specifies the -address of the coil to be forced. Coils are addressed starting at zero. - -@param u16WriteAddress address of the coil (0x0000..0xFFFF) -@param u8State 0=OFF, non-zero=ON (0x00..0xFF) -@return 0 on success; exception number on failure -@ingroup discrete -*/ -uint8_t ModbusMaster::writeSingleCoil(uint16_t u16WriteAddress, uint8_t u8State) -{ - _u16WriteAddress = u16WriteAddress; - _u16WriteQty = (u8State ? 0xFF00 : 0x0000); - return ModbusMasterTransaction(ku8MBWriteSingleCoil); -} - - -/** -Modbus function 0x06 Write Single Register. - -This function code is used to write a single holding register in a -remote device. The request specifies the address of the register to be -written. Registers are addressed starting at zero. - -@param u16WriteAddress address of the holding register (0x0000..0xFFFF) -@param u16WriteValue value to be written to holding register (0x0000..0xFFFF) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::writeSingleRegister(uint16_t u16WriteAddress, - uint16_t u16WriteValue) -{ - _u16WriteAddress = u16WriteAddress; - _u16WriteQty = 0; - _u16TransmitBuffer[0] = u16WriteValue; - return ModbusMasterTransaction(ku8MBWriteSingleRegister); -} - - -/** -Modbus function 0x0F Write Multiple Coils. - -This function code is used to force each coil in a sequence of coils to -either ON or OFF in a remote device. The request specifies the coil -references to be forced. Coils are addressed starting at zero. - -The requested ON/OFF states are specified by contents of the transmit -buffer. A logical '1' in a bit position of the buffer requests the -corresponding output to be ON. A logical '0' requests it to be OFF. - -@param u16WriteAddress address of the first coil (0x0000..0xFFFF) -@param u16BitQty quantity of coils to write (1..2000, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup discrete -*/ -uint8_t ModbusMaster::writeMultipleCoils(uint16_t u16WriteAddress, - uint16_t u16BitQty) -{ - _u16WriteAddress = u16WriteAddress; - _u16WriteQty = u16BitQty; - return ModbusMasterTransaction(ku8MBWriteMultipleCoils); -} -uint8_t ModbusMaster::writeMultipleCoils() -{ - _u16WriteQty = u16TransmitBufferLength; - return ModbusMasterTransaction(ku8MBWriteMultipleCoils); -} - - -/** -Modbus function 0x10 Write Multiple Registers. - -This function code is used to write a block of contiguous registers (1 -to 123 registers) in a remote device. - -The requested written values are specified in the transmit buffer. Data -is packed as one word per register. - -@param u16WriteAddress address of the holding register (0x0000..0xFFFF) -@param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::writeMultipleRegisters(uint16_t u16WriteAddress, - uint16_t u16WriteQty) -{ - _u16WriteAddress = u16WriteAddress; - _u16WriteQty = u16WriteQty; - return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); -} - -// new version based on Wire.h -uint8_t ModbusMaster::writeMultipleRegisters() -{ - _u16WriteQty = _u8TransmitBufferIndex; - return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); -} - - -/** -Modbus function 0x16 Mask Write Register. - -This function code is used to modify the contents of a specified holding -register using a combination of an AND mask, an OR mask, and the -register's current contents. The function can be used to set or clear -individual bits in the register. - -The request specifies the holding register to be written, the data to be -used as the AND mask, and the data to be used as the OR mask. Registers -are addressed starting at zero. - -The function's algorithm is: - -Result = (Current Contents && And_Mask) || (Or_Mask && (~And_Mask)) - -@param u16WriteAddress address of the holding register (0x0000..0xFFFF) -@param u16AndMask AND mask (0x0000..0xFFFF) -@param u16OrMask OR mask (0x0000..0xFFFF) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::maskWriteRegister(uint16_t u16WriteAddress, - uint16_t u16AndMask, uint16_t u16OrMask) -{ - _u16WriteAddress = u16WriteAddress; - _u16TransmitBuffer[0] = u16AndMask; - _u16TransmitBuffer[1] = u16OrMask; - return ModbusMasterTransaction(ku8MBMaskWriteRegister); -} - - -/** -Modbus function 0x17 Read Write Multiple Registers. - -This function code performs a combination of one read operation and one -write operation in a single MODBUS transaction. The write operation is -performed before the read. Holding registers are addressed starting at -zero. - -The request specifies the starting address and number of holding -registers to be read as well as the starting address, and the number of -holding registers. The data to be written is specified in the transmit -buffer. - -@param u16ReadAddress address of the first holding register (0x0000..0xFFFF) -@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) -@param u16WriteAddress address of the first holding register (0x0000..0xFFFF) -@param u16WriteQty quantity of holding registers to write (1..121, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ -uint8_t ModbusMaster::readWriteMultipleRegisters(uint16_t u16ReadAddress, - uint16_t u16ReadQty, uint16_t u16WriteAddress, uint16_t u16WriteQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16ReadQty; - _u16WriteAddress = u16WriteAddress; - _u16WriteQty = u16WriteQty; - return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters); -} -uint8_t ModbusMaster::readWriteMultipleRegisters(uint16_t u16ReadAddress, - uint16_t u16ReadQty) -{ - _u16ReadAddress = u16ReadAddress; - _u16ReadQty = u16ReadQty; - _u16WriteQty = _u8TransmitBufferIndex; - return ModbusMasterTransaction(ku8MBReadWriteMultipleRegisters); -} - - -/* _____PRIVATE FUNCTIONS____________________________________________________ */ -/** -Modbus transaction engine. -Sequence: - - assemble Modbus Request Application Data Unit (ADU), - based on particular function called - - transmit request over selected serial port - - wait for/retrieve response - - evaluate/disassemble response - - return status (success/exception) - -@param u8MBFunction Modbus function (0x01..0xFF) -@return 0 on success; exception number on failure -*/ -uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) -{ - uint8_t u8ModbusADU[256]; - uint8_t u8ModbusADUSize = 0; - uint8_t i, u8Qty; - uint16_t u16CRC; - uint32_t u32StartTime; - uint8_t u8BytesLeft = 8; - uint8_t u8MBStatus = ku8MBSuccess; - - // assemble Modbus Request Application Data Unit - u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave; - u8ModbusADU[u8ModbusADUSize++] = u8MBFunction; - - switch(u8MBFunction) - { - case ku8MBReadCoils: - case ku8MBReadDiscreteInputs: - case ku8MBReadInputRegisters: - case ku8MBReadHoldingRegisters: - case ku8MBReadWriteMultipleRegisters: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress); - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty); - break; - } - - switch(u8MBFunction) - { - case ku8MBWriteSingleCoil: - case ku8MBMaskWriteRegister: - case ku8MBWriteMultipleCoils: - case ku8MBWriteSingleRegister: - case ku8MBWriteMultipleRegisters: - case ku8MBReadWriteMultipleRegisters: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress); - break; - } - - switch(u8MBFunction) - { - case ku8MBWriteSingleCoil: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); - break; - - case ku8MBWriteSingleRegister: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]); - break; - - case ku8MBWriteMultipleCoils: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); - u8Qty = (_u16WriteQty % 8) ? ((_u16WriteQty >> 3) + 1) : (_u16WriteQty >> 3); - u8ModbusADU[u8ModbusADUSize++] = u8Qty; - for (i = 0; i < u8Qty; i++) - { - switch(i % 2) - { - case 0: // i is even - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]); - break; - - case 1: // i is odd - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]); - break; - } - } - break; - - case ku8MBWriteMultipleRegisters: - case ku8MBReadWriteMultipleRegisters: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1); - - for (i = 0; i < lowByte(_u16WriteQty); i++) - { - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]); - } - break; - - case ku8MBMaskWriteRegister: - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]); - u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[1]); - u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]); - break; - } - - // append CRC - u16CRC = 0xFFFF; - for (i = 0; i < u8ModbusADUSize; i++) - { - u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); - } - u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC); - u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC); - u8ModbusADU[u8ModbusADUSize] = 0; - - // flush receive buffer before transmitting request - while (_serial->read() != -1); - - // transmit request - if (_preTransmission) - { - _preTransmission(); - } - for (i = 0; i < u8ModbusADUSize; i++) - { - _serial->write(u8ModbusADU[i]); - } - - u8ModbusADUSize = 0; - _serial->flush(); // flush transmit buffer - if (_postTransmission) - { - _postTransmission(); - } - - // loop until we run out of time or bytes, or an error occurs - u32StartTime = millis(); - while (u8BytesLeft && !u8MBStatus) - { - if (_serial->available()) - { -#if __MODBUSMASTER_DEBUG__ - digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true); -#endif - u8ModbusADU[u8ModbusADUSize++] = _serial->read(); - u8BytesLeft--; -#if __MODBUSMASTER_DEBUG__ - digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false); -#endif - } - else - { -#if __MODBUSMASTER_DEBUG__ - digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, true); -#endif - if (_idle) - { - _idle(); - } -#if __MODBUSMASTER_DEBUG__ - digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false); -#endif - } - - // evaluate slave ID, function code once enough bytes have been read - if (u8ModbusADUSize == 5) - { - // verify response is for correct Modbus slave - if (u8ModbusADU[0] != _u8MBSlave) - { - u8MBStatus = ku8MBInvalidSlaveID; - break; - } - - // verify response is for correct Modbus function code (mask exception bit 7) - if ((u8ModbusADU[1] & 0x7F) != u8MBFunction) - { - u8MBStatus = ku8MBInvalidFunction; - break; - } - - // check whether Modbus exception occurred; return Modbus Exception Code - if (bitRead(u8ModbusADU[1], 7)) - { - u8MBStatus = u8ModbusADU[2]; - break; - } - - // evaluate returned Modbus function code - switch(u8ModbusADU[1]) - { - case ku8MBReadCoils: - case ku8MBReadDiscreteInputs: - case ku8MBReadInputRegisters: - case ku8MBReadHoldingRegisters: - case ku8MBReadWriteMultipleRegisters: - u8BytesLeft = u8ModbusADU[2]; - break; - - case ku8MBWriteSingleCoil: - case ku8MBWriteMultipleCoils: - case ku8MBWriteSingleRegister: - case ku8MBWriteMultipleRegisters: - u8BytesLeft = 3; - break; - - case ku8MBMaskWriteRegister: - u8BytesLeft = 5; - break; - } - } - if ((millis() - u32StartTime) > ku16MBResponseTimeout) - { - u8MBStatus = ku8MBResponseTimedOut; - } - } - - // verify response is large enough to inspect further - if (!u8MBStatus && u8ModbusADUSize >= 5) - { - // calculate CRC - u16CRC = 0xFFFF; - for (i = 0; i < (u8ModbusADUSize - 2); i++) - { - u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); - } - - // verify CRC - if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] || - highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1])) - { - u8MBStatus = ku8MBInvalidCRC; - } - } - - // disassemble ADU into words - if (!u8MBStatus) - { - // evaluate returned Modbus function code - switch(u8ModbusADU[1]) - { - case ku8MBReadCoils: - case ku8MBReadDiscreteInputs: - // load bytes into word; response bytes are ordered L, H, L, H, ... - for (i = 0; i < (u8ModbusADU[2] >> 1); i++) - { - if (i < ku8MaxBufferSize) - { - _u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 4], u8ModbusADU[2 * i + 3]); - } - - _u8ResponseBufferLength = i; - } - - // in the event of an odd number of bytes, load last byte into zero-padded word - if (u8ModbusADU[2] % 2) - { - if (i < ku8MaxBufferSize) - { - _u16ResponseBuffer[i] = word(0, u8ModbusADU[2 * i + 3]); - } - - _u8ResponseBufferLength = i + 1; - } - break; - - case ku8MBReadInputRegisters: - case ku8MBReadHoldingRegisters: - case ku8MBReadWriteMultipleRegisters: - // load bytes into word; response bytes are ordered H, L, H, L, ... - for (i = 0; i < (u8ModbusADU[2] >> 1); i++) - { - if (i < ku8MaxBufferSize) - { - _u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]); - } - - _u8ResponseBufferLength = i; - } - break; - } - } - - _u8TransmitBufferIndex = 0; - u16TransmitBufferLength = 0; - _u8ResponseBufferIndex = 0; - return u8MBStatus; -} diff --git a/lib/ModbusMaster/src/ModbusMaster.h b/lib/ModbusMaster/src/ModbusMaster.h deleted file mode 100644 index b8c566a4..00000000 --- a/lib/ModbusMaster/src/ModbusMaster.h +++ /dev/null @@ -1,270 +0,0 @@ -/** -@file -Arduino library for communicating with Modbus slaves over RS232/485 (via RTU protocol). - -@defgroup setup ModbusMaster Object Instantiation/Initialization -@defgroup buffer ModbusMaster Buffer Management -@defgroup discrete Modbus Function Codes for Discrete Coils/Inputs -@defgroup register Modbus Function Codes for Holding/Input Registers -@defgroup constant Modbus Function Codes, Exception Codes -*/ -/* - - ModbusMaster.h - Arduino library for communicating with Modbus slaves - over RS232/485 (via RTU protocol). - - Library:: ModbusMaster - - Copyright:: 2009-2016 Doc Walker - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - - -#ifndef ModbusMaster_h -#define ModbusMaster_h - - -/** -@def __MODBUSMASTER_DEBUG__ (0) -Set to 1 to enable debugging features within class: - - PIN A cycles for each byte read in the Modbus response - - PIN B cycles for each millisecond timeout during the Modbus response -*/ -#define __MODBUSMASTER_DEBUG__ (0) -#define __MODBUSMASTER_DEBUG_PIN_A__ 4 -#define __MODBUSMASTER_DEBUG_PIN_B__ 5 - -/* _____STANDARD INCLUDES____________________________________________________ */ -// include types & constants of Wiring core API -#include "Arduino.h" - -/* _____UTILITY MACROS_______________________________________________________ */ - - -/* _____PROJECT INCLUDES_____________________________________________________ */ -// functions to calculate Modbus Application Data Unit CRC -#include "util/crc16.h" - -// functions to manipulate words -#include "util/word.h" - - -/* _____CLASS DEFINITIONS____________________________________________________ */ -/** -Arduino class library for communicating with Modbus slaves over -RS232/485 (via RTU protocol). -*/ -class ModbusMaster -{ - public: - ModbusMaster(); - - void begin(uint8_t, Stream &serial); - void idle(void (*)()); - void preTransmission(void (*)()); - void postTransmission(void (*)()); - - // Modbus exception codes - /** - Modbus protocol illegal function exception. - - The function code received in the query is not an allowable action for - the server (or slave). This may be because the function code is only - applicable to newer devices, and was not implemented in the unit - selected. It could also indicate that the server (or slave) is in the - wrong state to process a request of this type, for example because it is - unconfigured and is being asked to return register values. - - @ingroup constant - */ - static const uint8_t ku8MBIllegalFunction = 0x01; - - /** - Modbus protocol illegal data address exception. - - The data address received in the query is not an allowable address for - the server (or slave). More specifically, the combination of reference - number and transfer length is invalid. For a controller with 100 - registers, the ADU addresses the first register as 0, and the last one - as 99. If a request is submitted with a starting register address of 96 - and a quantity of registers of 4, then this request will successfully - operate (address-wise at least) on registers 96, 97, 98, 99. If a - request is submitted with a starting register address of 96 and a - quantity of registers of 5, then this request will fail with Exception - Code 0x02 "Illegal Data Address" since it attempts to operate on - registers 96, 97, 98, 99 and 100, and there is no register with address - 100. - - @ingroup constant - */ - static const uint8_t ku8MBIllegalDataAddress = 0x02; - - /** - Modbus protocol illegal data value exception. - - A value contained in the query data field is not an allowable value for - server (or slave). This indicates a fault in the structure of the - remainder of a complex request, such as that the implied length is - incorrect. It specifically does NOT mean that a data item submitted for - storage in a register has a value outside the expectation of the - application program, since the MODBUS protocol is unaware of the - significance of any particular value of any particular register. - - @ingroup constant - */ - static const uint8_t ku8MBIllegalDataValue = 0x03; - - /** - Modbus protocol slave device failure exception. - - An unrecoverable error occurred while the server (or slave) was - attempting to perform the requested action. - - @ingroup constant - */ - static const uint8_t ku8MBSlaveDeviceFailure = 0x04; - - // Class-defined success/exception codes - /** - ModbusMaster success. - - Modbus transaction was successful; the following checks were valid: - - slave ID - - function code - - response code - - data - - CRC - - @ingroup constant - */ - static const uint8_t ku8MBSuccess = 0x00; - - /** - ModbusMaster invalid response slave ID exception. - - The slave ID in the response does not match that of the request. - - @ingroup constant - */ - static const uint8_t ku8MBInvalidSlaveID = 0xE0; - - /** - ModbusMaster invalid response function exception. - - The function code in the response does not match that of the request. - - @ingroup constant - */ - static const uint8_t ku8MBInvalidFunction = 0xE1; - - /** - ModbusMaster response timed out exception. - - The entire response was not received within the timeout period, - ModbusMaster::ku8MBResponseTimeout. - - @ingroup constant - */ - static const uint8_t ku8MBResponseTimedOut = 0xE2; - - /** - ModbusMaster invalid response CRC exception. - - The CRC in the response does not match the one calculated. - - @ingroup constant - */ - static const uint8_t ku8MBInvalidCRC = 0xE3; - - uint16_t getResponseBuffer(uint8_t); - void clearResponseBuffer(); - uint8_t setTransmitBuffer(uint8_t, uint16_t); - void clearTransmitBuffer(); - - void beginTransmission(uint16_t); - uint8_t requestFrom(uint16_t, uint16_t); - void sendBit(bool); - void send(uint8_t); - void send(uint16_t); - void send(uint32_t); - uint8_t available(void); - uint16_t receive(void); - - - uint8_t readCoils(uint16_t, uint16_t); - uint8_t readDiscreteInputs(uint16_t, uint16_t); - uint8_t readHoldingRegisters(uint16_t, uint16_t); - uint8_t readInputRegisters(uint16_t, uint8_t); - uint8_t writeSingleCoil(uint16_t, uint8_t); - uint8_t writeSingleRegister(uint16_t, uint16_t); - uint8_t writeMultipleCoils(uint16_t, uint16_t); - uint8_t writeMultipleCoils(); - uint8_t writeMultipleRegisters(uint16_t, uint16_t); - uint8_t writeMultipleRegisters(); - uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t); - uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t); - uint8_t readWriteMultipleRegisters(uint16_t, uint16_t); - - private: - Stream* _serial; ///< reference to serial port object - uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in begin() - static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers - uint16_t _u16ReadAddress; ///< slave register from which to read - uint16_t _u16ReadQty; ///< quantity of words to read - uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer() - uint16_t _u16WriteAddress; ///< slave register to which to write - uint16_t _u16WriteQty; ///< quantity of words to write - uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer() - uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx - uint8_t _u8TransmitBufferIndex; - uint16_t u16TransmitBufferLength; - uint16_t* rxBuffer; // from Wire.h -- need to clean this up Rx - uint8_t _u8ResponseBufferIndex; - uint8_t _u8ResponseBufferLength; - - // Modbus function codes for bit access - static const uint8_t ku8MBReadCoils = 0x01; ///< Modbus function 0x01 Read Coils - static const uint8_t ku8MBReadDiscreteInputs = 0x02; ///< Modbus function 0x02 Read Discrete Inputs - static const uint8_t ku8MBWriteSingleCoil = 0x05; ///< Modbus function 0x05 Write Single Coil - static const uint8_t ku8MBWriteMultipleCoils = 0x0F; ///< Modbus function 0x0F Write Multiple Coils - - // Modbus function codes for 16 bit access - static const uint8_t ku8MBReadHoldingRegisters = 0x03; ///< Modbus function 0x03 Read Holding Registers - static const uint8_t ku8MBReadInputRegisters = 0x04; ///< Modbus function 0x04 Read Input Registers - static const uint8_t ku8MBWriteSingleRegister = 0x06; ///< Modbus function 0x06 Write Single Register - static const uint8_t ku8MBWriteMultipleRegisters = 0x10; ///< Modbus function 0x10 Write Multiple Registers - static const uint8_t ku8MBMaskWriteRegister = 0x16; ///< Modbus function 0x16 Mask Write Register - static const uint8_t ku8MBReadWriteMultipleRegisters = 0x17; ///< Modbus function 0x17 Read Write Multiple Registers - - // Modbus timeout [milliseconds] - static const uint16_t ku16MBResponseTimeout = 150; ///< Modbus timeout [milliseconds] - - // master function that conducts Modbus transactions - uint8_t ModbusMasterTransaction(uint8_t u8MBFunction); - - // idle callback function; gets called during idle time between TX and RX - void (*_idle)(); - // preTransmission callback function; gets called before writing a Modbus message - void (*_preTransmission)(); - // postTransmission callback function; gets called after a Modbus message has been sent - void (*_postTransmission)(); -}; -#endif - -/** -@example examples/Basic/Basic.pde -@example examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde -@example examples/RS485_HalfDuplex/RS485_HalfDuplex.ino -*/ diff --git a/lib/ModbusMaster/src/util/crc16.h b/lib/ModbusMaster/src/util/crc16.h deleted file mode 100644 index 27ea89ec..00000000 --- a/lib/ModbusMaster/src/util/crc16.h +++ /dev/null @@ -1,88 +0,0 @@ -/** -@file -CRC Computations - -@defgroup util_crc16 "util/crc16.h": CRC Computations -@code#include "util/crc16.h"@endcode - -This header file provides functions for calculating -cyclic redundancy checks (CRC) using common polynomials. -Modified by Doc Walker to be processor-independent (removed inline -assembler to allow it to compile on SAM3X8E processors). - -@par References: -Jack Crenshaw's "Implementing CRCs" article in the January 1992 issue of @e -Embedded @e Systems @e Programming. This may be difficult to find, but it -explains CRC's in very clear and concise terms. Well worth the effort to -obtain a copy. - -*/ -/* Copyright (c) 2002, 2003, 2004 Marek Michalkiewicz - Copyright (c) 2005, 2007 Joerg Wunsch - Copyright (c) 2013 Dave Hylands - Copyright (c) 2013 Frederic Nadeau - Copyright (c) 2015 Doc Walker - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of the copyright holders nor the names of - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. */ - - -#ifndef _UTIL_CRC16_H_ -#define _UTIL_CRC16_H_ - - -/** @ingroup util_crc16 - Processor-independent CRC-16 calculation. - - Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
- Initial value: 0xFFFF - - This CRC is normally used in disk-drive controllers. - - @param uint16_t crc (0x0000..0xFFFF) - @param uint8_t a (0x00..0xFF) - @return calculated CRC (0x0000..0xFFFF) -*/ -static uint16_t crc16_update(uint16_t crc, uint8_t a) -{ - int i; - - crc ^= a; - for (i = 0; i < 8; ++i) - { - if (crc & 1) - crc = (crc >> 1) ^ 0xA001; - else - crc = (crc >> 1); - } - - return crc; -} - - -#endif /* _UTIL_CRC16_H_ */ diff --git a/lib/ModbusMaster/src/util/word.h b/lib/ModbusMaster/src/util/word.h deleted file mode 100644 index c72ad944..00000000 --- a/lib/ModbusMaster/src/util/word.h +++ /dev/null @@ -1,64 +0,0 @@ -/** -@file -Utility Functions for Manipulating Words - -@defgroup util_word "util/word.h": Utility Functions for Manipulating Words -@code#include "util/word.h"@endcode - -This header file provides utility functions for manipulating words. - -*/ -/* - - word.h - Utility Functions for Manipulating Words - - This file is part of ModbusMaster. - - ModbusMaster is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ModbusMaster is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ModbusMaster. If not, see . - - Written by Doc Walker (Rx) - Copyright © 2009-2015 Doc Walker <4-20ma at wvfans dot net> - -*/ - - -#ifndef _UTIL_WORD_H_ -#define _UTIL_WORD_H_ - - -/** @ingroup util_word - Return low word of a 32-bit integer. - - @param uint32_t ww (0x00000000..0xFFFFFFFF) - @return low word of input (0x0000..0xFFFF) -*/ -static inline uint16_t lowWord(uint32_t ww) -{ - return (uint16_t) ((ww) & 0xFFFF); -} - - -/** @ingroup util_word - Return high word of a 32-bit integer. - - @param uint32_t ww (0x00000000..0xFFFFFFFF) - @return high word of input (0x0000..0xFFFF) -*/ -static inline uint16_t highWord(uint32_t ww) -{ - return (uint16_t) ((ww) >> 16); -} - - -#endif /* _UTIL_WORD_H_ */ diff --git a/platformio.ini b/platformio.ini index 98ed5b66..bd0eef56 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,10 +35,9 @@ extra_scripts = ./tools/littlefsbuilder.py ;============================================================================================================================================= [env:esp8266_01_1m] framework = arduino +board = nodemcuv2 ;board = esp01_1m ;board = esp12e -board = nodemcuv2 -;board_build.ldscript = eagle.flash.1m256.ld board_build.ldscript = eagle.flash.1m512.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 1bff0d34..b28778bb 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -9,7 +9,6 @@ #include "items/vLogging.h" #include "items/vImpulsOut.h" #include "items/vCountDown.h" -#include "items/SensorModbusClass.h" void loopCmdAdd(const String& cmdStr) { orderBuf += cmdStr; @@ -88,11 +87,6 @@ void csvCmdExecute(String& cmdStr) { else if (order == F("bmp280-press")) { sCmd.addCommand(order.c_str(), bmp280Press); } -#endif -#ifdef SensorModbusEnabled - else if (order == F("modbus")) { - sCmd.addCommand(order.c_str(), modbus); - } #endif else if (order == F("uptime")) { sCmd.addCommand(order.c_str(), sysUptime); diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 8a7dc57e..00673fce 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -1,3 +1,4 @@ +#include "Consts.h" #ifdef uartEnable #include "SoftUART.h" #include "Global.h" diff --git a/src/Telegram.cpp b/src/Telegram.cpp index 2ab73d32..ee8fabb9 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -1,11 +1,12 @@ -#ifdef telegram +#include "Consts.h" +#ifdef telegramEnable #include "Telegram.h" CTBot* myBot{ nullptr }; void telegramInit() { if (isTelegramEnabled()) { telegramInitBeen = true; - sCmd.addCommand("telegram", sendTelegramMsg); + sCmd.addCommand("telegramEnable", sendTelegramMsg); String token = jsonReadStr(configSetupJson, "telegramApi"); if (!myBot) { myBot = new CTBot(); diff --git a/src/Utils/TimeUtils.cpp b/src/Utils/TimeUtils.cpp index 6d2dcdee..47786861 100644 --- a/src/Utils/TimeUtils.cpp +++ b/src/Utils/TimeUtils.cpp @@ -3,7 +3,8 @@ #include "Utils/StringUtils.h" static const uint8_t days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static const char* week_days[7] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; + +//static const char* week_days[7] = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; // String getTimeUnix() { // time_t t; diff --git a/src/items/SensorModbusClass.cpp b/src/items/SensorModbusClass.cpp deleted file mode 100644 index a4e84157..00000000 --- a/src/items/SensorModbusClass.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "Consts.h" -#ifdef SensorModbusEnabled -#include "items/SensorModbusClass.h" -#include "BufferExecute.h" -//=========================================Модуль modbus=================================================================================== -//modbus;id;anydata;Сенсоры;Температура;order;addr[1];regaddr[0];c[1] -//========================================================================================================================================= -SensorModbusClass mySensorModbus; - -void modbus() { - mySensorModbus.update(); - String key = mySensorModbus.gkey(); - sCmd.addCommand(key.c_str(), modbusReading); - mySensorModbus.SensorModbusInit(); - mySensorModbus.clear(); -} -void modbusReading() { - String key = sCmd.order(); - String addr = sCmd.next(); - String regaddr = sCmd.next(); - mySensorModbus.SensorModbusRead(key, addr.toInt(), regaddr.toInt()); -} -#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 60edbce8..78e16b92 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -50,7 +50,7 @@ void setup() { itemsListInit(); espInit(); routerConnect(); -#ifdef telegram +#ifdef telegramEnable telegramInit(); #endif uptime_init(); @@ -95,7 +95,7 @@ void loop() { myNotAsyncActions->loop(); ts.update(); -#ifdef telegram +#ifdef telegramEnable handleTelegram(); #endif From d5826b70389207a23b63ba2444b7d000bfc8921a Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 16 Dec 2020 19:30:39 +0100 Subject: [PATCH 44/94] 1mb --- include/Consts.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index 0bce4c82..77f5a752 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,7 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 272 -//#define FLASH_SIZE_1MB true +#define FLASH_SIZE_1MB true #ifdef ESP8266 #ifdef FLASH_SIZE_1MB #define FIRMWARE_NAME "esp8266-1mb" @@ -40,7 +40,9 @@ #define SensorDhtEnabled #define PwmOutEnable //=========Features================================================================================================================================= -//#define telegramEnable +#ifndef FLASH_SIZE_1MB +#define telegramEnable +#endif #define uartEnable From 245475ae99b60e7430c4967cb0b8681c688af583 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 17 Dec 2020 01:29:30 +0100 Subject: [PATCH 45/94] full change of constuction --- data/items/items.txt | 29 ++++++++++ data/presets/presets.c.txt | 46 +++++++++++++++ data/presets/presets.s.txt | 84 +++++++++++++++++++++++++++ data/set.device.json | 72 +++++++++++------------ include/Consts.h | 22 ++++--- include/ItemsList.h | 2 + src/ItemsList.cpp | 115 ++++++++++++++++++++++++------------- src/Web.cpp | 4 +- 8 files changed, 283 insertions(+), 91 deletions(-) create mode 100644 data/items/items.txt create mode 100644 data/presets/presets.c.txt create mode 100644 data/presets/presets.s.txt diff --git a/data/items/items.txt b/data/items/items.txt new file mode 100644 index 00000000..032966d6 --- /dev/null +++ b/data/items/items.txt @@ -0,0 +1,29 @@ +1;0;button-out;id;toggle;Кнопки;Освещение;order;pin +2;0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] +3;0;button-out;id;toggle;Кнопки;Освещение;order +4;0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] +5;0;pwm-out;id;range;Ползунки;Яркость;order;pin +6;0;inoutput;id;inputDigit;Ввод;Введите#цифру;order +7;0;inoutput;id;inputTime;Ввод;Введите#время;order +8;0;inoutput;id;anydata;Вывод;Сигнализация;order +9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1] +10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;index[0];int[10] +11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] +12;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1] +13;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1] +14;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1] +15;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1] +16;0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] +17;0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] +18;0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] +19;0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] +20;0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] +21;0;impuls-out;id;na;na;na;order;pin +22;0;count-down;id;anydata;Таймер;Обратный#отчет;order +23;0;modbus;id;anydata;Modbus;Регистр;order;addr[1];reg[0];c[1] +24;0;button-out;id;toggle;Кнопки;Освещение;order;type[UART] +25;0;inoutput;id;anydata;Вывод;Вывод#uart;order +26;0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] +27;0;uptime;id;anydataTime;Системные;%name%#uptime;order + + diff --git a/data/presets/presets.c.txt b/data/presets/presets.c.txt new file mode 100644 index 00000000..5db609f9 --- /dev/null +++ b/data/presets/presets.c.txt @@ -0,0 +1,46 @@ +0;dallas-temp;t1;anydataTemp;Термостат1;Температура;1;pin[2];index[0];int[10] +0;logging;log1;chart;Термостат1;История;2;val[t1];int[60];cnt[100] +0;inoutput;tUp1;inputDigit;Термостат1;Верхний#порог;3 +0;inoutput;tLow1;inputDigit;Термостат1;Нижний#порог;4 +0;button-out;btn1;toggle;Термостат1;Нагрев;5;pin[12]* +0;dallas-temp;t2;anydataTemp;Термостат2;Температура;1;pin[2];index[0];int[60] +0;logging;log2;chart;Термостат2;История;2;val[t2];int[10];cnt[100] +0;inoutput;threshold;inputDigitTemp;Термостат2;Заданная#температура;3 +0;button-out;heater2;toggle;Термостат2;Нагреватель;7;pin[12] +0;inoutput;time21;inputTimeClock;Расписание2;Утренний#период;8 +0;inoutput;threshold1;inputDigitTemp;Расписание2;Температура;9 +0;inoutput;time22;inputTimeClock;Расписание2;Дневной#период;10 +0;inoutput;threshold2;inputDigitTemp;Расписание2;Температура;11 +0;inoutput;time23;inputTimeClock;Расписание2;Вечерний#период;12 +0;inoutput;threshold3;inputDigitTemp;Расписание2;Температура;13 +0;inoutput;time24;inputTimeClock;Расписание2;Ночной#период;14 +0;inoutput;threshold4;inputDigitTemp;Расписание2;Температура;15* +0;dht-hum;h3;anydataHum;Теплица3;Влажность;1;pin[2];type[dht11];c[1] +0;logging;log3;chart;Теплица3;История;2;val[hum];int[60];cnt[100] +0;inoutput;hUp3;inputDigit;Теплица3;Верхний#порог;3 +0;inoutput;hLow3;inputDigit;Теплица3;Нижний#порог;4 +0;button-out;hUp3;toggle;Теплица3;Полив;5;pin[12]* +0;button-out;btn41;toggle;Реле4;Освещение;1;pin[12] +0;button-out;btn42;toggle;Реле4;Освещение;2;pin[13] +0;inoutput;time41;inputTime;Реле4;Введите#время#включения;3 +0;inoutput;time42;inputTime;Реле4;Введите#время#выключения;4* +0;button-out;btn51;toggle;Свет5;Выключить#все;1 +0;button-out;btn52;toggle;Свет5;Гостинная;2;pin[12] +0;button-out;btn53;toggle;Свет5;Спальня;3;pin[13] +0;button-out;btn54;toggle;Свет5;Прихожая;4;pin[14] +0;pwm-out;pwm51;range;Свет5;Яркость;5;pin[15] +0;pwm-out;pwm52;range;Свет5;Яркость;6;pin[16] +0;inoutput;txt5;anydata;Свет5;Статус;7* +0;button-out;button;toggle;Таймер;Освещение;1;pin[12] +0;count-down;count;anydata;Таймер;Обратный#отчет;2 +0;inoutput;input;inputDigit;Таймер;Введите#цифру;3* +0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 +0;inoutput;time;anydataTime;Сигнализация;Время:;2 +0;button-in;sensor;na;na;na;3;pin[0];db[20] +0;button-out;reset;toggle;Сигнализация;Сбросить;4* +0;button-in;sensor;na;na;na;1;pin[0];db[20] +0;button-out;light;toggle;Освещение;Освещение;2;pin[13] +0;count-down;count;anydata;Освещение;Обратный#отчет;3 +0;inoutput;period;inputDigit;Освещение;Период#включения;4* +0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] +0;button-in;switch;na;na;na;2;pin[0];db[20]* \ No newline at end of file diff --git a/data/presets/presets.s.txt b/data/presets/presets.s.txt new file mode 100644 index 00000000..0e36c7c1 --- /dev/null +++ b/data/presets/presets.s.txt @@ -0,0 +1,84 @@ +t1 > tUp1 +btn1 0 +telegram нагрев#выключен 1 +end +t1 < tLow1 +btn1 1 +telegram нагрев#включен 1 +end* +t2 > threshold+-2 +heater2 0 +end +t2 < threshold+-2 +heater2 1 +end +timenow = time21 +threshold threshold1 +end +timenow = time22 +threshold threshold2 +end +timenow = time23 +threshold threshold3 +end +timenow = time24 +threshold threshold4 +end* +h3 > hUp3 +hUp3 0 +telegram полив#выключен 1 +end +h3 < hLow3 +hUp3 1 +telegram полив#включен 1 +end* +timenow = time41 +btn41 1 +btn42 0 +end +timenow = time42 +btn41 0 +btn42 1 +end* +btn51 = 1 +btn52 1 +btn53 1 +btn54 1 +pwm51 200 +pwm52 800 +txt5 включено +end +btn51 = 0 +btn52 0 +btn53 0 +btn54 0 +pwm51 800 +pwm52 200 +txt5 выключено +end* +button = 1 +count input +end +count = 0 +button 0 +end* +sensor = 1 +text обнаружено +time %date% +telegram text обнаружено#движение 1 +end +reset = 1 +text не#обнаружено +time %date% +reset 0 +end* +sensor = 1 +light 1 +count period +end +count = 0 +light 0 +end* +switch = 1 +light change +end* \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index eec1e537..bd7affaa 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -70,33 +70,33 @@ "style": "display:inline", "title": { "#": "Выберите элемент из списка", - "/set?addItem=button-out.pin": "1.Кнопка управляющая пином", - "/set?addItem=button-out.inv": "2.Кнопка управляющая пином (с инверсией)", - "/set?addItem=button-out.npin": "3.Кнопка виртуальная (не привязанная к пину, для использования в сценариях)", - "/set?addItem=button-in": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", - "/set?addItem=pwm-out": "3.Широтно импульсная модуляция pwm", - "/set?addItem=input-digit": "5.Окно ввода цифровых значений", - "/set?addItem=input-time": "6.Окно ввода времени", - "/set?addItem=output-text": "7.Окно вывода любого текста, предупреждения, цифры", - "/set?addItem=analog-adc": "8.Датчик аналоговый, чтение аналогового входа", - "/set?addItem=dallas-temp": "9.Датчик температуры ds18b20", - "/set?addItem=ultrasonic-cm": "10.Датчик расстояния ультрозвуковой JSN-SR04T, HC-SR04, HY-SRF05", - "/set?addItem=dht11-temp": "11.Датчик температуры DHT11", - "/set?addItem=dht11-hum": "12.Датчик влажности DHT11", - "/set?addItem=dht22-temp": "13.Датчик температуры DHT22, DHT33, DHT44, AM2302, RHT03", - "/set?addItem=dht22-hum": "14.Датчик влажности DHT22, DHT33, DHT44, AM2302, RHT03", - "/set?addItem=bme280-temp": "15.Датчик температуры bme280", - "/set?addItem=bme280-hum": "16.Датчик влажности bme280", - "/set?addItem=bme280-press": "17.Датчик давления bme280", - "/set?addItem=bmp280-temp": "18.Датчик температуры bmp280", - "/set?addItem=bmp280-press": "19.Датчик давления bmp280", - "/set?addItem=impuls-out": "20.Создать импульсы через заданный промежуток времени (управление шд)", - "/set?addItem=count-down": "21.Таймер обратного отчета", - "/set?addItem=modbus": "22.Прочитать регистр modbus устройства", - "/set?addItem=uart-button": "23.UART кнопка (шлет свое состояние в UART)", - "/set?addItem=uart-widget": "24.UART виджет (позволяет вывести полученные данные в любой виджет)", - "/set?addItem=logging": "a.Логгирование и вывод в график любой величины", - "/set?addItem=uptime": "b.Отобразить время работы устройства" + "/set?addItem=1-btn": "1.Кнопка управляющая пином", + "/set?addItem=2-btn": "2.Кнопка управляющая пином (с инверсией)", + "/set?addItem=3-btn": "3.Кнопка виртуальная (не привязанная к пину, для использования в сценариях)", + "/set?addItem=4-btn": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", + "/set?addItem=5-pwm": "5.Широтно импульсная модуляция pwm", + "/set?addItem=6-dgt": "6.Окно ввода цифровых значений", + "/set?addItem=7-tm": "7.Окно ввода времени", + "/set?addItem=8-txt": "8.Окно вывода любого текста, предупреждения, цифры", + "/set?addItem=9-adc": "9.Датчик аналоговый, чтение аналогового входа", + "/set?addItem=10-tmp": "10.Датчик температуры ds18b20", + "/set?addItem=11-rng": "11.Датчик расстояния ультрозвуковой JSN-SR04T, HC-SR04, HY-SRF05", + "/set?addItem=12-tmp": "12.Датчик температуры DHT11", + "/set?addItem=13-hmd": "13.Датчик влажности DHT11", + "/set?addItem=14-tmp": "14.Датчик температуры DHT22, DHT33, DHT44, AM2302, RHT03", + "/set?addItem=15-hmd": "15.Датчик влажности DHT22, DHT33, DHT44, AM2302, RHT03", + "/set?addItem=16-tmp": "16.Датчик температуры bme280", + "/set?addItem=17-hmd": "17.Датчик влажности bme280", + "/set?addItem=18-ps": "18.Датчик давления bme280", + "/set?addItem=19-tmp": "19.Датчик температуры bmp280", + "/set?addItem=20-ps": "20.Датчик давления bmp280", + "/set?addItem=21-ips": "21.Создать импульсы через заданный промежуток времени (управление шд)", + "/set?addItem=22-cnt": "22.Таймер обратного отчета", + "/set?addItem=23-mb": "23.Прочитать регистр modbus устройства", + "/set?addItem=24-btn": "24.UART кнопка (шлет свое состояние в UART)", + "/set?addItem=25-txt": "25.UART виджет (позволяет вывести полученные данные в любой виджет)", + "/set?addItem=26-log": "26.Логгирование и вывод в график любой величины", + "/set?addItem=27-ut": "27.Отобразить время работы устройства" } }, { @@ -106,15 +106,15 @@ "style": "display:inline", "title": { "#": "Выберите пресет из списка", - "/set?addPreset=1.c": "1.Термостат на основе ds18b20 с оповещением в телеграм", - "/set?addPreset=2.c": "2.Гистерезис термостат на основе ds18b20 с суточным расписанием", - "/set?addPreset=3.c": "3.Контроль влажности на основе DHT с оповещением в телеграм", - "/set?addPreset=4.c": "4.Включение выключение реле в заданное время", - "/set?addPreset=5.c": "5.Выключить все (пример работы сценариев)", - "/set?addPreset=6.c": "6.Включить кнопку на определенное время (пример работы таймера обратного отчета)", - "/set?addPreset=7.c": "7.Охранный датчик движения", - "/set?addPreset=8.c": "8.Датчик движения включающий свет с настраиваемой задержкой", - "/set?addPreset=9.c": "9.Управление светом с помощью выключателя и приложения" + "/set?addPreset=1": "1.Термостат на основе ds18b20 с оповещением в телеграм", + "/set?addPreset=2": "2.Гистерезис термостат на основе ds18b20 с суточным расписанием", + "/set?addPreset=3": "3.Контроль влажности на основе DHT с оповещением в телеграм", + "/set?addPreset=4": "4.Включение выключение реле в заданное время", + "/set?addPreset=5": "5.Выключить все (пример работы сценариев)", + "/set?addPreset=6": "6.Включить кнопку на определенное время (пример работы таймера обратного отчета)", + "/set?addPreset=7": "7.Охранный датчик движения", + "/set?addPreset=8": "8.Датчик движения включающий свет с настраиваемой задержкой", + "/set?addPreset=9": "9.Управление светом с помощью выключателя и приложения" } }, { diff --git a/include/Consts.h b/include/Consts.h index 77f5a752..e53537d2 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -3,18 +3,6 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 272 #define FLASH_SIZE_1MB true -#ifdef ESP8266 -#ifdef FLASH_SIZE_1MB -#define FIRMWARE_NAME "esp8266-1mb" -#else -#define FIRMWARE_NAME "esp8266" -#endif - -#endif -#ifdef ESP32 -#define FIRMWARE_NAME "esp32" -#endif - //===========FileSystem============================================================================================================================================== #define littlefs_on //================================================================================================================================================================== @@ -46,7 +34,17 @@ #define uartEnable +#ifdef ESP8266 +#ifdef FLASH_SIZE_1MB +#define FIRMWARE_NAME "esp8266-1mb" +#else +#define FIRMWARE_NAME "esp8266" +#endif +#endif +#ifdef ESP32 +#define FIRMWARE_NAME "esp32" +#endif //================================================================================================================================================================ enum TimerTask_t { WIFI_SCAN, diff --git a/include/ItemsList.h b/include/ItemsList.h index c0b716ec..54089580 100644 --- a/include/ItemsList.h +++ b/include/ItemsList.h @@ -4,8 +4,10 @@ #include "Global.h" extern void itemsListInit(); +extern void addItem2(String param); extern void addItem(String name); extern void addPreset(String name); +extern void addPreset2(int num); extern void delChoosingItems(); extern void delAllItems(); extern uint8_t getNewElementNumber(String file); diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 13937acf..df27a11c 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -18,63 +18,96 @@ void itemsListInit() { delChoosingItems(); }, nullptr); - + SerialPrint("I", F("Items"), F("Items Init")); } -void addItem(String name) { +void addItem2(String param) { + int num = selectToMarker(param, "-").toInt(); + File configFile = LittleFS.open("/items/items.txt", "r"); + if (!configFile) { + return; + } + configFile.seek(0, SeekSet); + String seachingLine; - String item = readFile("items/" + name + ".txt", 1024); - - name = selectToMarker(name, "-"); + while (configFile.position() != configFile.size()) { + String item = configFile.readStringUntil('\n'); + int tmpNum = selectToMarker(item, ";").toInt(); + if (tmpNum == num) { + seachingLine = item; + break; + } + } + configFile.close(); + String name = deleteBeforeDelimiter(param, "-"); randomSeed(micros()); - unsigned int num = random(0, 1000); + unsigned int rnd = random(0, 1000); + seachingLine.replace("id", name + String(rnd)); + seachingLine.replace("order", String(getNewElementNumber("order.txt"))); - item.replace("id", name + String(num)); - item.replace("order", String(getNewElementNumber("order.txt"))); - - if (item.indexOf("pin") != -1) { //all cases (random pins from available) - item.replace("pin", "pin[" + String(getFreePinAll()) + "]"); - } - else if (item.indexOf("gol") != -1) { //analog - item.replace("gol", "pin[" + String(getFreePinAnalog()) + "]"); - } - else if (item.indexOf("cin") != -1) { //ultrasonic - item.replace("cin", "pin[" + String(getFreePinAll()) + "," + String(getFreePinAll()) + "]"); - } - else if (item.indexOf("sal") != -1) { //dallas - item.replace("sal", "pin[2]"); - } - else if (item.indexOf("thd") != -1) { //dht11/22 - item.replace("thd", "pin[2]"); + if (seachingLine.indexOf("pin") != -1) { + seachingLine.replace("pin", "pin[" + String(getFreePinAll()) + "]"); } - item.replace("\r\n", ""); - item.replace("\r", ""); - item.replace("\n", ""); - - addFile(DEVICE_CONFIG_FILE, "\n" + item); - Serial.println(item); + seachingLine = deleteBeforeDelimiter(seachingLine, ";"); + seachingLine.replace("\r\n", ""); + seachingLine.replace("\r", ""); + seachingLine.replace("\n", ""); + addFile(DEVICE_CONFIG_FILE, "\n" + seachingLine); + Serial.println(seachingLine); } -void addPreset(String name) { - String preset = readFile("presets/" + name + ".txt", 4048); - addFile(DEVICE_CONFIG_FILE, "\n" + preset); - Serial.println(preset); - name.replace(".c", ".s"); +void addPreset2(int num) { + File configFile = LittleFS.open("/presets/presets.c.txt", "r"); + if (!configFile) { + return; + } + configFile.seek(0, SeekSet); + String config; + int i1 = 0; + while (configFile.position() != configFile.size()) { + i1++; + String item = configFile.readStringUntil('*'); + if (i1 == num) { + if (i1 == 1) { + config = "\n" + item; + } + else { + config = item; + } + break; + } + } + configFile.close(); + addFile(DEVICE_CONFIG_FILE, config); - String scenario = readFile("presets/" + name + ".txt", 4048); - - removeFile(DEVICE_SCENARIO_FILE); - - addFile(DEVICE_SCENARIO_FILE, scenario); - loadScenario(); - Serial.println(scenario); + File scenFile = LittleFS.open("/presets/presets.s.txt", "r"); + if (!scenFile) { + return; + } + scenFile.seek(0, SeekSet); + String scen; + int i2 = 0; + while (scenFile.position() != scenFile.size()) { + i2++; + String item = scenFile.readStringUntil('*'); + if (i2 == num) { + scen = item; + break; + } + } + scenFile.close(); + if (readFile(String(DEVICE_SCENARIO_FILE), 2048) == "//") { + removeFile(DEVICE_SCENARIO_FILE); + } + addFile(DEVICE_SCENARIO_FILE, scen); } + void delAllItems() { removeFile(DEVICE_CONFIG_FILE); addFile(DEVICE_CONFIG_FILE, String(firstLine)); diff --git a/src/Web.cpp b/src/Web.cpp index c673a79a..31a93c3d 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -20,12 +20,12 @@ void web_init() { server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { //==============================set.device.json==================================================================================================== if (request->hasArg("addItem")) { - addItem(request->getParam("addItem")->value()); + addItem2(request->getParam("addItem")->value()); request->redirect("/?set.device"); } if (request->hasArg("addPreset")) { - addPreset(request->getParam("addPreset")->value()); + addPreset2(request->getParam("addPreset")->value().toInt()); jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); request->redirect("/?set.device"); } From 2e240def888378ad7dafcef8008639ed8b7dffb6 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 17 Dec 2020 02:01:12 +0100 Subject: [PATCH 46/94] working version --- include/Consts.h | 4 +--- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index e53537d2..dd97d73a 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,9 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 272 -#define FLASH_SIZE_1MB true -//===========FileSystem============================================================================================================================================== -#define littlefs_on +//#define FLASH_SIZE_1MB true //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN 2 diff --git a/platformio.ini b/platformio.ini index bd0eef56..f5848391 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266_01_1m +default_envs = esp8266 ;============================================================================================================================================= [common_env_data] lib_deps_external = From ce383fe7fe71d236cdbc95a657ba956622829033 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 17 Dec 2020 22:48:20 +0100 Subject: [PATCH 47/94] global change 1 mb working version --- data/dev_conf.txt | 0 data/edit.htm.gz | Bin 5455 -> 0 bytes data/items/items.txt | 8 +- data/presets/presets.c.txt | 46 -------- data/presets/presets.s.txt | 84 ------------- data/set.device.json | 8 +- data/set.utilities.json | 10 ++ doc/calculator.xlsx | Bin 0 -> 9890 bytes doc/old files/items/analog-adc.txt | 1 + doc/old files/items/bme280-hum.txt | 1 + doc/old files/items/bme280-press.txt | 1 + doc/old files/items/bme280-temp.txt | 1 + doc/old files/items/bmp280-press.txt | 1 + doc/old files/items/bmp280-temp.txt | 1 + doc/old files/items/button-in.txt | 1 + doc/old files/items/button-out.inv.txt | 1 + doc/old files/items/button-out.npin.txt | 1 + doc/old files/items/button-out.pin.txt | 1 + doc/old files/items/count-down.txt | 1 + doc/old files/items/dallas-temp.txt | 1 + doc/old files/items/dht11-hum.txt | 1 + doc/old files/items/dht11-temp.txt | 1 + doc/old files/items/dht22-hum.txt | 1 + doc/old files/items/dht22-temp.txt | 1 + doc/old files/items/impuls-out.txt | 1 + doc/old files/items/input-digit.txt | 1 + doc/old files/items/input-time.txt | 1 + doc/old files/items/logging.txt | 1 + doc/old files/items/modbus.txt | 1 + doc/old files/items/output-text.txt | 1 + doc/old files/items/pwm-out.txt | 1 + doc/old files/items/uart-button.txt | 1 + doc/old files/items/uart-widget.txt | 1 + doc/old files/items/ultrasonic-cm.txt | 1 + doc/old files/items/uptime.txt | 1 + doc/old files/presets/1.c.txt | 5 + doc/old files/presets/1.s.txt | 8 ++ doc/old files/presets/2.c.txt | 12 ++ doc/old files/presets/2.s.txt | 18 +++ doc/old files/presets/3.c.txt | 5 + doc/old files/presets/3.s.txt | 8 ++ doc/old files/presets/4.c.txt | 4 + doc/old files/presets/4.s.txt | 8 ++ doc/old files/presets/5.c.txt | 7 ++ doc/old files/presets/5.s.txt | 16 +++ doc/old files/presets/6.c.txt | 3 + doc/old files/presets/6.s.txt | 6 + doc/old files/presets/7.c.txt | 4 + doc/old files/presets/7.s.txt | 10 ++ doc/old files/presets/8.c.txt | 4 + doc/old files/presets/8.s.txt | 7 ++ doc/old files/presets/9.c.txt | 2 + doc/old files/presets/9.s.txt | 3 + include/Consts.h | 4 +- platformio.ini | 2 +- src/BufferExecute.cpp | 16 ++- src/MqttClient.cpp | 13 +- src/SoftUART.cpp | 7 +- src/Telegram.cpp | 3 +- src/Web.cpp | 151 ++++++++++-------------- src/items/vButtonOut.cpp | 11 -- 61 files changed, 265 insertions(+), 255 deletions(-) delete mode 100644 data/dev_conf.txt delete mode 100644 data/edit.htm.gz delete mode 100644 data/presets/presets.c.txt delete mode 100644 data/presets/presets.s.txt create mode 100644 doc/calculator.xlsx create mode 100644 doc/old files/items/analog-adc.txt create mode 100644 doc/old files/items/bme280-hum.txt create mode 100644 doc/old files/items/bme280-press.txt create mode 100644 doc/old files/items/bme280-temp.txt create mode 100644 doc/old files/items/bmp280-press.txt create mode 100644 doc/old files/items/bmp280-temp.txt create mode 100644 doc/old files/items/button-in.txt create mode 100644 doc/old files/items/button-out.inv.txt create mode 100644 doc/old files/items/button-out.npin.txt create mode 100644 doc/old files/items/button-out.pin.txt create mode 100644 doc/old files/items/count-down.txt create mode 100644 doc/old files/items/dallas-temp.txt create mode 100644 doc/old files/items/dht11-hum.txt create mode 100644 doc/old files/items/dht11-temp.txt create mode 100644 doc/old files/items/dht22-hum.txt create mode 100644 doc/old files/items/dht22-temp.txt create mode 100644 doc/old files/items/impuls-out.txt create mode 100644 doc/old files/items/input-digit.txt create mode 100644 doc/old files/items/input-time.txt create mode 100644 doc/old files/items/logging.txt create mode 100644 doc/old files/items/modbus.txt create mode 100644 doc/old files/items/output-text.txt create mode 100644 doc/old files/items/pwm-out.txt create mode 100644 doc/old files/items/uart-button.txt create mode 100644 doc/old files/items/uart-widget.txt create mode 100644 doc/old files/items/ultrasonic-cm.txt create mode 100644 doc/old files/items/uptime.txt create mode 100644 doc/old files/presets/1.c.txt create mode 100644 doc/old files/presets/1.s.txt create mode 100644 doc/old files/presets/2.c.txt create mode 100644 doc/old files/presets/2.s.txt create mode 100644 doc/old files/presets/3.c.txt create mode 100644 doc/old files/presets/3.s.txt create mode 100644 doc/old files/presets/4.c.txt create mode 100644 doc/old files/presets/4.s.txt create mode 100644 doc/old files/presets/5.c.txt create mode 100644 doc/old files/presets/5.s.txt create mode 100644 doc/old files/presets/6.c.txt create mode 100644 doc/old files/presets/6.s.txt create mode 100644 doc/old files/presets/7.c.txt create mode 100644 doc/old files/presets/7.s.txt create mode 100644 doc/old files/presets/8.c.txt create mode 100644 doc/old files/presets/8.s.txt create mode 100644 doc/old files/presets/9.c.txt create mode 100644 doc/old files/presets/9.s.txt diff --git a/data/dev_conf.txt b/data/dev_conf.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/data/edit.htm.gz b/data/edit.htm.gz deleted file mode 100644 index 5786121f98a84b5695314c9ad8e5537f57650ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5455 zcmV-V6|m|biwFp~9I0Of0A*xpbS`LgZ2-+2X?NO2@O$6uf7q&rp$2i-aT9DO1&m|I zw}GTin)V540og)QC1Jp+|9fW-X(g?MvFjfAK-!&aXJ=<-4{7I{a=AA)eFVS37$eux zOdE}ZiG_S;>ggCcLs~J$bA)Z(F|dI;h8Z|s`CJ=egeL=RkU~J~o*(Y-cUi5fyDlo1 zUw{48&Nq8!?cUXShms`)l)R#`M+Ox(c=Gjpn=9^KTYqOB?vA?o2BC zd$syjd$V5dwDz0j%}%XeZf#Uwm0PWi{k^@P>Kn~=tug^^sMr1*EeO2by3w5PpAY+Z z^x~(^(M#vm?-wte{SVsRw7lWm-8Hu~Z>-nMi|XVT*BbY)|F}M~rkfv6%UjL&HP1DS z8|&`Uv|fIwU7!8F;k4)V>dDPWLHbzpd>rg5{oa20rSf{G!W#5?#}5`3j<;6#eFaUh zVQRYy0AZ#+1RqtOpf7MqKX+x*Ht@WGHmcR_u+VjUGcX;yfj|Y0H4Si7W#M7aKrh^R zR5EZp1A7frbLR+vO#^+qxw$E-HT`DfIaAvx>yG6B;J5oP$WH`em_FFjqJeD3#xb(< zvOm@gXVySf_^DNazBd|Zg=z`0Uv;w>fe#%!DEsCe20`jfI#E8e%dm39>vTF2R&YCoE-HyTp*$hx^1hIJRptR& zaHj$Kvcf2FTunEF1>+zS`VzN3!qA&&7PVXS9(#snYbDh5OwB5xu57a9iE~$;LK&1> zSPxQm2-ZBfF@q%#a?LU$pglk4Bc#UqT5gi)EurA@zzG6p!rX=29c);?e9&kgvct4& z^aB}-?z>#XVs&( zBc}5t&5)g+&bcJl(h+O_rT;U7&>{MKmo`kiL;sB(BZ`*5^%K?<$v<@<59 zUw<_*4veur={GMvT;5(y`lIfL+W2GrefQ&s{Tuwt!Ml&W86fOA-Tr0keQWfwrFZ57 z>$LT@IcU~(j7Po8*7We`Y-F0_?#I)f-hOf1KWLic(|4EEo3}^pJ^kw4)z;od<)@!c zj5E{j-QG1v?b*wP-y0v^+Liat_2J?K{?5TL^V3uPZnAD^ug-dw=GFQ}v$EctL{Z}xH%Y)kB)l#M)S^Vp3R!`PQQ7u ze$+Xs*1A_4qm!*!%kMNVTGi3J^@;Y;X>W`Ux6Jb^t$Emb=bM+4gMPKyJU+ww?G5dy zu73di_kY1Ztv6?9RZD+`dk0rN+B+RW3O>Sb zBuBYsn-k7CEHSP%9Ya)u0X~6mHquEJHkgiv<{Spm(6r3}qngiT6jD+-L((^RG4!+v z_E)AHlW*ubQI?VChXvm-g06-UT_h-cJ9RBb1Ld#ep;OV?qb@`a-;$H5bAKhf#`>o0 z<;s){&0Vt0F79qu2z1{DU^=U}E+Hy&#midAvxwSOHOpHIT0$I5v+feVb3L$NJbR*i zVFx@Yu#itApP`yX1}bY$7Eta9iYK@IMMm2nYib)?->2KUS^ZveGn}lPp4Y1Ik#g>r zqQ!$3r+JYlTRN2x4mfgXl#Wnx_4~Nr2|P%-hLDm$w(SI3fD2Uv zkBV8YvQ~Wvbz@6Hj=pv(kZ>lat9*uL4pD(jYn2}!yu%%oZ8rPW|U0F^Bk|B(DjP}6#6j8 z3$a3F3FVG(K@!a;`x%hux10ML+7ij2OT z4CD-8O`H-y=86O9GjKY>i-P8P+M=4C9p#A6|FFq;w-B6hB@;G`5)>bF$xY(X@IxLajAjbo@V0kcSy1r|O~ zpZX<4nE9?_`?v?GGqZ!VK%~X2e^m?HqhL(^9;CyxLhfLhLL88zg*_Li?vKr3P!NVG za!lzavewl!LZ_%iCCR>YNu83i1`tejHHb`p+$Q!Rj_kp#G}#%Hf;I6*!ZS z@`C^s#e7w{e|gw;CSXFu7gGPWj+9*m{%6#S_E4{t?CE=XCX($d@GKP^3XG5ptvWUx zOgbO8*As2#$B zp1@A9N?A+X{JMch1|xyV6)529J-}pK6_hrtkCBNc@xn?8DVpnArcRT=%Dg2mcE?Ek%e3Bo_4pX!#?%az@BaT22w)Ddf37O)KcL!}Q>uPO4!u12v z!;B>^eArF2i`XbAq0lEwA&dJ&cj}J|UkFZvAu=ir{v8r(NCX?vRTSSvz)Pr2Zl>_g z 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 tUp1 -btn1 0 -telegram нагрев#выключен 1 -end -t1 < tLow1 -btn1 1 -telegram нагрев#включен 1 -end* -t2 > threshold+-2 -heater2 0 -end -t2 < threshold+-2 -heater2 1 -end -timenow = time21 -threshold threshold1 -end -timenow = time22 -threshold threshold2 -end -timenow = time23 -threshold threshold3 -end -timenow = time24 -threshold threshold4 -end* -h3 > hUp3 -hUp3 0 -telegram полив#выключен 1 -end -h3 < hLow3 -hUp3 1 -telegram полив#включен 1 -end* -timenow = time41 -btn41 1 -btn42 0 -end -timenow = time42 -btn41 0 -btn42 1 -end* -btn51 = 1 -btn52 1 -btn53 1 -btn54 1 -pwm51 200 -pwm52 800 -txt5 включено -end -btn51 = 0 -btn52 0 -btn53 0 -btn54 0 -pwm51 800 -pwm52 200 -txt5 выключено -end* -button = 1 -count input -end -count = 0 -button 0 -end* -sensor = 1 -text обнаружено -time %date% -telegram text обнаружено#движение 1 -end -reset = 1 -text не#обнаружено -time %date% -reset 0 -end* -sensor = 1 -light 1 -count period -end -count = 0 -light 0 -end* -switch = 1 -light change -end* \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json index bd7affaa..cf6a6369 100644 --- a/data/set.device.json +++ b/data/set.device.json @@ -92,11 +92,9 @@ "/set?addItem=20-ps": "20.Датчик давления bmp280", "/set?addItem=21-ips": "21.Создать импульсы через заданный промежуток времени (управление шд)", "/set?addItem=22-cnt": "22.Таймер обратного отчета", - "/set?addItem=23-mb": "23.Прочитать регистр modbus устройства", - "/set?addItem=24-btn": "24.UART кнопка (шлет свое состояние в UART)", - "/set?addItem=25-txt": "25.UART виджет (позволяет вывести полученные данные в любой виджет)", - "/set?addItem=26-log": "26.Логгирование и вывод в график любой величины", - "/set?addItem=27-ut": "27.Отобразить время работы устройства" + "/set?addItem=23-txt": "23.Виджет для отображения информации полученной из uart, get-запроса, или по udp", + "/set?addItem=24-log": "24.Логгирование и вывод в график любой величины", + "/set?addItem=25-ut": "25.Отобразить время работы устройства" } }, { diff --git a/data/set.utilities.json b/data/set.utilities.json index dee60f7c..16f10037 100644 --- a/data/set.utilities.json +++ b/data/set.utilities.json @@ -52,6 +52,16 @@ { "type": "hr" }, + { + "type": "checkbox", + "name": "uartEvents", + "title": "Включить отправку всех событий в UART", + "action": "/set?uartEvents=[[uartEvents]]", + "state": "{{uartEvents}}" + }, + { + "type": "hr" + }, { "type": "h4", "title": "Скорость", diff --git a/doc/calculator.xlsx b/doc/calculator.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6fd98cf2035fc8bfc8a7921684392c3e8e604571 GIT binary patch literal 9890 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 literal 0 HcmV?d00001 diff --git a/doc/old files/items/analog-adc.txt b/doc/old files/items/analog-adc.txt new file mode 100644 index 00000000..8bc0e914 --- /dev/null +++ b/doc/old files/items/analog-adc.txt @@ -0,0 +1 @@ +0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1] \ No newline at end of file diff --git a/doc/old files/items/bme280-hum.txt b/doc/old files/items/bme280-hum.txt new file mode 100644 index 00000000..c8afd255 --- /dev/null +++ b/doc/old files/items/bme280-hum.txt @@ -0,0 +1 @@ +0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] \ No newline at end of file diff --git a/doc/old files/items/bme280-press.txt b/doc/old files/items/bme280-press.txt new file mode 100644 index 00000000..f79973e2 --- /dev/null +++ b/doc/old files/items/bme280-press.txt @@ -0,0 +1 @@ +0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/doc/old files/items/bme280-temp.txt b/doc/old files/items/bme280-temp.txt new file mode 100644 index 00000000..6e5e9003 --- /dev/null +++ b/doc/old files/items/bme280-temp.txt @@ -0,0 +1 @@ +0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/doc/old files/items/bmp280-press.txt b/doc/old files/items/bmp280-press.txt new file mode 100644 index 00000000..44b9e8c9 --- /dev/null +++ b/doc/old files/items/bmp280-press.txt @@ -0,0 +1 @@ +0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] \ No newline at end of file diff --git a/doc/old files/items/bmp280-temp.txt b/doc/old files/items/bmp280-temp.txt new file mode 100644 index 00000000..c3cb42eb --- /dev/null +++ b/doc/old files/items/bmp280-temp.txt @@ -0,0 +1 @@ +0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] \ No newline at end of file diff --git a/doc/old files/items/button-in.txt b/doc/old files/items/button-in.txt new file mode 100644 index 00000000..79d79ec9 --- /dev/null +++ b/doc/old files/items/button-in.txt @@ -0,0 +1 @@ +0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] \ No newline at end of file diff --git a/doc/old files/items/button-out.inv.txt b/doc/old files/items/button-out.inv.txt new file mode 100644 index 00000000..5499a108 --- /dev/null +++ b/doc/old files/items/button-out.inv.txt @@ -0,0 +1 @@ +0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] \ No newline at end of file diff --git a/doc/old files/items/button-out.npin.txt b/doc/old files/items/button-out.npin.txt new file mode 100644 index 00000000..ecffd881 --- /dev/null +++ b/doc/old files/items/button-out.npin.txt @@ -0,0 +1 @@ +0;button-out;id;toggle;Кнопки;Освещение;order \ No newline at end of file diff --git a/doc/old files/items/button-out.pin.txt b/doc/old files/items/button-out.pin.txt new file mode 100644 index 00000000..b0645025 --- /dev/null +++ b/doc/old files/items/button-out.pin.txt @@ -0,0 +1 @@ +0;button-out;id;toggle;Кнопки;Освещение;order;pin \ No newline at end of file diff --git a/doc/old files/items/count-down.txt b/doc/old files/items/count-down.txt new file mode 100644 index 00000000..3300573c --- /dev/null +++ b/doc/old files/items/count-down.txt @@ -0,0 +1 @@ +0;count-down;id;anydata;Таймер;Обратный#отчет;order \ No newline at end of file diff --git a/doc/old files/items/dallas-temp.txt b/doc/old files/items/dallas-temp.txt new file mode 100644 index 00000000..0da8bdca --- /dev/null +++ b/doc/old files/items/dallas-temp.txt @@ -0,0 +1 @@ +0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;index[0];int[10] \ No newline at end of file diff --git a/doc/old files/items/dht11-hum.txt b/doc/old files/items/dht11-hum.txt new file mode 100644 index 00000000..b7d9a820 --- /dev/null +++ b/doc/old files/items/dht11-hum.txt @@ -0,0 +1 @@ +0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/doc/old files/items/dht11-temp.txt b/doc/old files/items/dht11-temp.txt new file mode 100644 index 00000000..39c5e949 --- /dev/null +++ b/doc/old files/items/dht11-temp.txt @@ -0,0 +1 @@ +0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1] \ No newline at end of file diff --git a/doc/old files/items/dht22-hum.txt b/doc/old files/items/dht22-hum.txt new file mode 100644 index 00000000..ab69cf97 --- /dev/null +++ b/doc/old files/items/dht22-hum.txt @@ -0,0 +1 @@ +0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/doc/old files/items/dht22-temp.txt b/doc/old files/items/dht22-temp.txt new file mode 100644 index 00000000..d14a434d --- /dev/null +++ b/doc/old files/items/dht22-temp.txt @@ -0,0 +1 @@ +0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1] \ No newline at end of file diff --git a/doc/old files/items/impuls-out.txt b/doc/old files/items/impuls-out.txt new file mode 100644 index 00000000..99994ae5 --- /dev/null +++ b/doc/old files/items/impuls-out.txt @@ -0,0 +1 @@ +0;impuls-out;id;na;na;na;order;pin \ No newline at end of file diff --git a/doc/old files/items/input-digit.txt b/doc/old files/items/input-digit.txt new file mode 100644 index 00000000..7df7260e --- /dev/null +++ b/doc/old files/items/input-digit.txt @@ -0,0 +1 @@ +0;inoutput;id;inputDigit;Ввод;Введите#цифру;order \ No newline at end of file diff --git a/doc/old files/items/input-time.txt b/doc/old files/items/input-time.txt new file mode 100644 index 00000000..3fc078b0 --- /dev/null +++ b/doc/old files/items/input-time.txt @@ -0,0 +1 @@ +0;inoutput;id;inputTime;Ввод;Введите#время;order \ No newline at end of file diff --git a/doc/old files/items/logging.txt b/doc/old files/items/logging.txt new file mode 100644 index 00000000..8253774f --- /dev/null +++ b/doc/old files/items/logging.txt @@ -0,0 +1 @@ +0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] \ No newline at end of file diff --git a/doc/old files/items/modbus.txt b/doc/old files/items/modbus.txt new file mode 100644 index 00000000..a782395a --- /dev/null +++ b/doc/old files/items/modbus.txt @@ -0,0 +1 @@ +0;modbus;id;anydata;Modbus;Регистр;order;addr[1];reg[0];c[1] \ No newline at end of file diff --git a/doc/old files/items/output-text.txt b/doc/old files/items/output-text.txt new file mode 100644 index 00000000..65d45284 --- /dev/null +++ b/doc/old files/items/output-text.txt @@ -0,0 +1 @@ +0;inoutput;id;anydata;Вывод;Сигнализация;order \ No newline at end of file diff --git a/doc/old files/items/pwm-out.txt b/doc/old files/items/pwm-out.txt new file mode 100644 index 00000000..2acbb459 --- /dev/null +++ b/doc/old files/items/pwm-out.txt @@ -0,0 +1 @@ +0;pwm-out;id;range;Ползунки;Яркость;order;pin \ No newline at end of file diff --git a/doc/old files/items/uart-button.txt b/doc/old files/items/uart-button.txt new file mode 100644 index 00000000..ee2a6b22 --- /dev/null +++ b/doc/old files/items/uart-button.txt @@ -0,0 +1 @@ +0;button-out;id;toggle;Кнопки;Освещение;order;type[UART] \ No newline at end of file diff --git a/doc/old files/items/uart-widget.txt b/doc/old files/items/uart-widget.txt new file mode 100644 index 00000000..469ab016 --- /dev/null +++ b/doc/old files/items/uart-widget.txt @@ -0,0 +1 @@ +0;inoutput;id;anydata;Вывод;Вывод#uart;order \ No newline at end of file diff --git a/doc/old files/items/ultrasonic-cm.txt b/doc/old files/items/ultrasonic-cm.txt new file mode 100644 index 00000000..c5d518c3 --- /dev/null +++ b/doc/old files/items/ultrasonic-cm.txt @@ -0,0 +1 @@ +0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] \ No newline at end of file diff --git a/doc/old files/items/uptime.txt b/doc/old files/items/uptime.txt new file mode 100644 index 00000000..972241b4 --- /dev/null +++ b/doc/old files/items/uptime.txt @@ -0,0 +1 @@ +0;uptime;id;anydataTime;Системные;%name%#uptime;order \ No newline at end of file diff --git a/doc/old files/presets/1.c.txt b/doc/old files/presets/1.c.txt new file mode 100644 index 00000000..abcd06dc --- /dev/null +++ b/doc/old files/presets/1.c.txt @@ -0,0 +1,5 @@ +0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[10] +0;logging;log;chart;Термостат;История;2;val[temp];int[60];cnt[100] +0;inoutput;inputU;inputDigit;Термостат;Верхний#порог;3 +0;inoutput;inputL;inputDigit;Термостат;Нижний#порог;4 +0;button-out;button;toggle;Термостат;Нагрев;5;pin[12] \ No newline at end of file diff --git a/doc/old files/presets/1.s.txt b/doc/old files/presets/1.s.txt new file mode 100644 index 00000000..dc4c8c07 --- /dev/null +++ b/doc/old files/presets/1.s.txt @@ -0,0 +1,8 @@ +temp > inputU +button 0 +telegram нагрев#выключен 1 +end +temp < inputL +button 1 +telegram нагрев#включен 1 +end \ No newline at end of file diff --git a/doc/old files/presets/2.c.txt b/doc/old files/presets/2.c.txt new file mode 100644 index 00000000..d91fa5c7 --- /dev/null +++ b/doc/old files/presets/2.c.txt @@ -0,0 +1,12 @@ +0;dallas-temp;temp;anydataTemp;Термостат;Температура;1;pin[2];index[0];int[60] +0;logging;log;chart;Термостат;История;2;val[temp];int[10];cnt[100] +0;inoutput;threshold;inputDigitTemp;Термостат;Заданная#температура;3 +0;button-out;heater;toggle;Термостат;Нагреватель;7;pin[12] +0;inoutput;time1;inputTimeClock;Расписание;Утренний#период;8 +0;inoutput;threshold1;inputDigitTemp;Расписание;Температура;9 +0;inoutput;time2;inputTimeClock;Расписание;Дневной#период;10 +0;inoutput;threshold2;inputDigitTemp;Расписание;Температура;11 +0;inoutput;time3;inputTimeClock;Расписание;Вечерний#период;12 +0;inoutput;threshold3;inputDigitTemp;Расписание;Температура;13 +0;inoutput;time4;inputTimeClock;Расписание;Ночной#период;14 +0;inoutput;threshold4;inputDigitTemp;Расписание;Температура;15 \ No newline at end of file diff --git a/doc/old files/presets/2.s.txt b/doc/old files/presets/2.s.txt new file mode 100644 index 00000000..5a6f36f1 --- /dev/null +++ b/doc/old files/presets/2.s.txt @@ -0,0 +1,18 @@ +temp > threshold+-2 +heater 0 +end +temp < threshold+-2 +heater 1 +end +timenow = time1 +threshold threshold1 +end +timenow = time2 +threshold threshold2 +end +timenow = time3 +threshold threshold3 +end +timenow = time4 +threshold threshold4 +end \ No newline at end of file diff --git a/doc/old files/presets/3.c.txt b/doc/old files/presets/3.c.txt new file mode 100644 index 00000000..fb2c602b --- /dev/null +++ b/doc/old files/presets/3.c.txt @@ -0,0 +1,5 @@ +0;dht-hum;hum;anydataHum;Теплица;Влажность;1;pin[2];type[dht11];c[1] +0;logging;log;chart;Теплица;История;2;val[hum];int[60];cnt[100] +0;inoutput;inputU;inputDigit;Теплица;Верхний#порог;3 +0;inoutput;inputL;inputDigit;Теплица;Нижний#порог;4 +0;button-out;button;toggle;Теплица;Полив;5;pin[12] \ No newline at end of file diff --git a/doc/old files/presets/3.s.txt b/doc/old files/presets/3.s.txt new file mode 100644 index 00000000..f69cdc54 --- /dev/null +++ b/doc/old files/presets/3.s.txt @@ -0,0 +1,8 @@ +hum > inputU +button 0 +telegram полив#выключен 1 +end +hum < inputL +button 1 +telegram полив#включен 1 +end \ No newline at end of file diff --git a/doc/old files/presets/4.c.txt b/doc/old files/presets/4.c.txt new file mode 100644 index 00000000..3f1ccaa1 --- /dev/null +++ b/doc/old files/presets/4.c.txt @@ -0,0 +1,4 @@ +0;button-out;button1;toggle;Реле;Освещение;1;pin[12] +0;button-out;button2;toggle;Реле;Освещение;2;pin[13] +0;inoutput;T1;inputTime;Реле;Введите#время#включения;3 +0;inoutput;T2;inputTime;Реле;Введите#время#выключения;4 \ No newline at end of file diff --git a/doc/old files/presets/4.s.txt b/doc/old files/presets/4.s.txt new file mode 100644 index 00000000..844d1c5b --- /dev/null +++ b/doc/old files/presets/4.s.txt @@ -0,0 +1,8 @@ +timenow = T1 +button1 1 +button2 0 +end +timenow = T2 +button1 0 +button2 1 +end \ No newline at end of file diff --git a/doc/old files/presets/5.c.txt b/doc/old files/presets/5.c.txt new file mode 100644 index 00000000..359a7a52 --- /dev/null +++ b/doc/old files/presets/5.c.txt @@ -0,0 +1,7 @@ +0;button-out;button-out-1;toggle;Кнопки;Выключить#все;1 +0;button-out;button-out-2;toggle;Кнопки;Гостинная;2;pin[12] +0;button-out;button-out-3;toggle;Кнопки;Спальня;3;pin[13] +0;button-out;button-out-4;toggle;Кнопки;Прихожая;4;pin[14] +0;pwm-out;pwm-out-5;range;Кнопки;Яркость;5;pin[15] +0;pwm-out;pwm-out-6;range;Кнопки;Яркость;6;pin[16] +0;inoutput;output-text-7;anydata;Кнопки;Статус;7 \ No newline at end of file diff --git a/doc/old files/presets/5.s.txt b/doc/old files/presets/5.s.txt new file mode 100644 index 00000000..0424728a --- /dev/null +++ b/doc/old files/presets/5.s.txt @@ -0,0 +1,16 @@ +button-out-1 = 1 +button-out-2 1 +button-out-3 1 +button-out-4 1 +pwm-out-5 200 +pwm-out-6 800 +output-text-7 включено +end +button-out-1 = 0 +button-out-2 0 +button-out-3 0 +button-out-4 0 +pwm-out-5 800 +pwm-out-6 200 +output-text-7 выключено +end \ No newline at end of file diff --git a/doc/old files/presets/6.c.txt b/doc/old files/presets/6.c.txt new file mode 100644 index 00000000..c7fd096a --- /dev/null +++ b/doc/old files/presets/6.c.txt @@ -0,0 +1,3 @@ +0;button-out;button;toggle;Таймер;Освещение;1;pin[12] +0;count-down;count;anydata;Таймер;Обратный#отчет;2 +0;inoutput;input;inputDigit;Таймер;Введите#цифру;3 \ No newline at end of file diff --git a/doc/old files/presets/6.s.txt b/doc/old files/presets/6.s.txt new file mode 100644 index 00000000..06b70a4a --- /dev/null +++ b/doc/old files/presets/6.s.txt @@ -0,0 +1,6 @@ +button = 1 +count input +end +count = 0 +button 0 +end \ No newline at end of file diff --git a/doc/old files/presets/7.c.txt b/doc/old files/presets/7.c.txt new file mode 100644 index 00000000..984c6b8d --- /dev/null +++ b/doc/old files/presets/7.c.txt @@ -0,0 +1,4 @@ +0;inoutput;text;anydataAlarm;Сигнализация;Движение:;1 +0;inoutput;time;anydataTime;Сигнализация;Время:;2 +0;button-in;sensor;na;na;na;3;pin[0];db[20] +0;button-out;reset;toggle;Сигнализация;Сбросить;4 \ No newline at end of file diff --git a/doc/old files/presets/7.s.txt b/doc/old files/presets/7.s.txt new file mode 100644 index 00000000..32d28e78 --- /dev/null +++ b/doc/old files/presets/7.s.txt @@ -0,0 +1,10 @@ +sensor = 1 +text обнаружено +time %date% +telegram text обнаружено#движение 1 +end +reset = 1 +text не#обнаружено +time %date% +reset 0 +end \ No newline at end of file diff --git a/doc/old files/presets/8.c.txt b/doc/old files/presets/8.c.txt new file mode 100644 index 00000000..d18bdd0b --- /dev/null +++ b/doc/old files/presets/8.c.txt @@ -0,0 +1,4 @@ +0;button-in;sensor;na;na;na;1;pin[0];db[20] +0;button-out;light;toggle;Освещение;Освещение;2;pin[13] +0;count-down;count;anydata;Освещение;Обратный#отчет;3 +0;inoutput;period;inputDigit;Освещение;Период#включения;4 \ No newline at end of file diff --git a/doc/old files/presets/8.s.txt b/doc/old files/presets/8.s.txt new file mode 100644 index 00000000..10d55654 --- /dev/null +++ b/doc/old files/presets/8.s.txt @@ -0,0 +1,7 @@ +sensor = 1 +light 1 +count period +end +count = 0 +light 0 +end \ No newline at end of file diff --git a/doc/old files/presets/9.c.txt b/doc/old files/presets/9.c.txt new file mode 100644 index 00000000..502f3ae4 --- /dev/null +++ b/doc/old files/presets/9.c.txt @@ -0,0 +1,2 @@ +0;button-out;light;toggle;Кнопки;Освещение;1;pin[13] +0;button-in;switch;na;na;na;2;pin[0];db[20] \ No newline at end of file diff --git a/doc/old files/presets/9.s.txt b/doc/old files/presets/9.s.txt new file mode 100644 index 00000000..6f6d7dcc --- /dev/null +++ b/doc/old files/presets/9.s.txt @@ -0,0 +1,3 @@ +switch = 1 +light change +end \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h index dd97d73a..6b4e6764 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,8 +1,8 @@ #pragma once //===========Firmware============================================================================================================================================= -#define FIRMWARE_VERSION 272 -//#define FLASH_SIZE_1MB true +#define FIRMWARE_VERSION 273 +#define FLASH_SIZE_1MB true //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN 2 diff --git a/platformio.ini b/platformio.ini index f5848391..bd0eef56 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266 +default_envs = esp8266_01_1m ;============================================================================================================================================= [common_env_data] lib_deps_external = diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index b28778bb..e0752de6 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -1,5 +1,6 @@ #include "BufferExecute.h" #include "Global.h" +#include "SoftUART.h" // #include "items/vSensorDallas.h" #include "items/vSensorUltrasonic.h" @@ -11,9 +12,18 @@ #include "items/vCountDown.h" void loopCmdAdd(const String& cmdStr) { - orderBuf += cmdStr; - if (!cmdStr.endsWith(",")) { - orderBuf += ","; + if (cmdStr.endsWith(",")) { + orderBuf += cmdStr; +#ifdef uartEnable + if (jsonReadBool(configSetupJson, "uart")) { + if (jsonReadBool(configSetupJson, "uartEvents")) { + if (myUART) { + myUART->print(cmdStr); + SerialPrint("I", "<=UART", cmdStr); + } + } + } +#endif } } diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 951d16c4..fa334f78 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -1,5 +1,5 @@ #include "MqttClient.h" - +#include "BufferExecute.h" #include #include "items/vLogging.h" #include "Class/NotAsync.h" @@ -132,10 +132,13 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { String key = selectFromMarkerToMarker(topicStr, "/", 3); - orderBuf += key; - orderBuf += " "; - orderBuf += payloadStr; - orderBuf += ","; + String order; + order += key; + order += " "; + order += payloadStr; + order += ","; + + loopCmdAdd(order); SerialPrint("I", "=>MQTT", "Msg from iotmanager app: " + key + " " + payloadStr); } diff --git a/src/SoftUART.cpp b/src/SoftUART.cpp index 00673fce..c40a3f08 100644 --- a/src/SoftUART.cpp +++ b/src/SoftUART.cpp @@ -2,6 +2,7 @@ #ifdef uartEnable #include "SoftUART.h" #include "Global.h" +#include "BufferExecute.h" #ifdef ESP8266 SoftwareSerial* myUART = nullptr; @@ -10,7 +11,7 @@ HardwareSerial* myUART = nullptr; #endif void uartInit() { - if (!jsonReadBool(configSetupJson, "uartEnable")) { + if (!jsonReadBool(configSetupJson, "uart")) { return; } if (!myUART) { @@ -27,7 +28,7 @@ void uartInit() { void uartHandle() { if (myUART) { - if (!jsonReadBool(configSetupJson, "uartEnable")) { + if (!jsonReadBool(configSetupJson, "uart")) { return; } static String incStr; @@ -49,7 +50,7 @@ void parse(String& incStr) { incStr.replace("\n", ""); if (incStr.indexOf("set") != -1) { incStr = deleteBeforeDelimiter(incStr, " "); - orderBuf += incStr; + loopCmdAdd(incStr); SerialPrint("I", "=>UART", incStr); } } diff --git a/src/Telegram.cpp b/src/Telegram.cpp index ee8fabb9..6663b20c 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -1,6 +1,7 @@ #include "Consts.h" #ifdef telegramEnable #include "Telegram.h" +#include "BufferExecute.h" CTBot* myBot{ nullptr }; void telegramInit() { @@ -49,7 +50,7 @@ void telegramMsgParse(String msg) { if (msg.indexOf("set") != -1) { msg = deleteBeforeDelimiter(msg, "_"); msg.replace("_", " "); - orderBuf += String(msg) + ","; + loopCmdAdd(String(msg) + ","); myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), "order done"); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + String(msg)); } diff --git a/src/Web.cpp b/src/Web.cpp index 31a93c3d..6027fcc5 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -19,65 +19,55 @@ bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { void web_init() { server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { //==============================set.device.json==================================================================================================== - if (request->hasArg("addItem")) { + if (request->hasArg(F("addItem"))) { addItem2(request->getParam("addItem")->value()); request->redirect("/?set.device"); } - if (request->hasArg("addPreset")) { - addPreset2(request->getParam("addPreset")->value().toInt()); - jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); - request->redirect("/?set.device"); + if (request->hasArg(F("addPreset"))) { + addPreset2(request->getParam(F("addPreset"))->value().toInt()); + if (FLASH_SIZE_1MB) { + jsonWriteStr(configSetupJson, F("warning1"), F("

Присеты не доступны, модуль на 1mb

")); + } + else { + jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); + } + request->redirect(F("/?set.device")); } - if (request->hasArg("delChoosingItems")) { - jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); + if (request->hasArg(F("delChoosingItems"))) { + jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); myNotAsyncActions->make(do_delChoosingItems); request->send(200); } - if (request->hasArg("delAllItems")) { + if (request->hasArg(F("delAllItems"))) { delAllItems(); cleanLogAndData(); - jsonWriteStr(configSetupJson, "warning1", F("

Требуется перезагрузка

")); - request->redirect("/?set.device"); + jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); + request->redirect(F("/?set.device")); } - if (request->hasArg("saveItems")) { + if (request->hasArg(F("saveItems"))) { myNotAsyncActions->make(do_deviceInit); request->send(200); } - if (request->hasArg("scen")) { - bool value = request->getParam("scen")->value().toInt(); - jsonWriteBool(configSetupJson, "scen", value); + if (request->hasArg(F("scen"))) { + bool value = request->getParam(F("scen"))->value().toInt(); + jsonWriteBool(configSetupJson, F("scen"), value); saveConfig(); loadScenario(); request->send(200); } - if (request->hasArg("sceninit")) { + if (request->hasArg(F("sceninit"))) { loadScenario(); request->send(200); } - //if (request->hasArg("snaUdp")) { - // bool value = request->getParam("snaUdp")->value().toInt(); - // jsonWriteBool(configSetupJson, "snaUdp", value); - // saveConfig(); - // #ifdef UDP_ENABLED - // asyncUdpInit(); - // #endif - // request->send(200); - //} - - //if (request->hasArg("scenUdp")) { - // myNotAsyncActions->make(do_sendScenUDP); - // request->send(200); - //} - - if (request->hasArg("MqttIn")) { - bool value = request->getParam("MqttIn")->value().toInt(); + if (request->hasArg(F("MqttIn"))) { + bool value = request->getParam(F("MqttIn"))->value().toInt(); jsonWriteBool(configSetupJson, "MqttIn", value); saveConfig(); mqtt.subscribe((mqttPrefix + "/+/+/event").c_str()); @@ -85,111 +75,103 @@ void web_init() { request->send(200); } - if (request->hasArg("MqttOut")) { - bool value = request->getParam("MqttOut")->value().toInt(); - jsonWriteBool(configSetupJson, "MqttOut", value); + if (request->hasArg(F("MqttOut"))) { + bool value = request->getParam(F("MqttOut"))->value().toInt(); + jsonWriteBool(configSetupJson, F("MqttOut"), value); saveConfig(); request->send(200); } - if (request->hasArg("scenMqtt")) { + if (request->hasArg(F("scenMqtt"))) { myNotAsyncActions->make(do_sendScenMQTT); request->send(200); } - if (request->hasArg("cleanlog")) { + if (request->hasArg(F("cleanlog"))) { cleanLogAndData(); request->send(200); } //==============================wifi settings============================================= - if (request->hasArg("devname")) { - jsonWriteStr(configSetupJson, "name", request->getParam("devname")->value()); + if (request->hasArg(F("devname"))) { + jsonWriteStr(configSetupJson, F("name"), request->getParam(F("devname"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("routerssid")) { - jsonWriteStr(configSetupJson, "routerssid", request->getParam("routerssid")->value()); + if (request->hasArg(F("routerssid"))) { + jsonWriteStr(configSetupJson, F("routerssid"), request->getParam(F("routerssid"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("routerpass")) { - jsonWriteStr(configSetupJson, "routerpass", request->getParam("routerpass")->value()); + if (request->hasArg(F("routerpass"))) { + jsonWriteStr(configSetupJson, F("routerpass"), request->getParam(F("routerpass"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("apssid")) { - jsonWriteStr(configSetupJson, "apssid", request->getParam("apssid")->value()); + if (request->hasArg(F("apssid"))) { + jsonWriteStr(configSetupJson, F("apssid"), request->getParam(F("apssid"))->value()); saveConfig(); request->send(200, "text/text", "OK"); } - if (request->hasArg("appass")) { - jsonWriteStr(configSetupJson, "appass", request->getParam("appass")->value()); + if (request->hasArg(F("appass"))) { + jsonWriteStr(configSetupJson, F("appass"), request->getParam(F("appass"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("weblogin")) { - jsonWriteStr(configSetupJson, "weblogin", request->getParam("weblogin")->value()); + if (request->hasArg(F("weblogin"))) { + jsonWriteStr(configSetupJson, F("weblogin"), request->getParam(F("weblogin"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("webpass")) { - jsonWriteStr(configSetupJson, "webpass", request->getParam("webpass")->value()); + if (request->hasArg(F("webpass"))) { + jsonWriteStr(configSetupJson, F("webpass"), request->getParam(F("webpass"))->value()); saveConfig(); request->send(200); } - if (request->hasArg("timezone")) { - String timezoneStr = request->getParam("timezone")->value(); - jsonWriteStr(configSetupJson, "timezone", timezoneStr); + if (request->hasArg(F("timezone"))) { + String timezoneStr = request->getParam(F("timezone"))->value(); + jsonWriteStr(configSetupJson, F("timezone"), timezoneStr); saveConfig(); timeNow->setTimezone(timezoneStr.toInt()); request->send(200); } - if (request->hasArg("ntp")) { - String ntpStr = request->getParam("ntp")->value(); - jsonWriteStr(configSetupJson, "ntp", ntpStr); + if (request->hasArg(F("ntp"))) { + String ntpStr = request->getParam(F("ntp"))->value(); + jsonWriteStr(configSetupJson, F("ntp"), ntpStr); saveConfig(); timeNow->setNtpPool(ntpStr); request->send(200); } - if (request->hasArg("blink")) { - bool value = request->getParam("blink")->value().toInt(); - jsonWriteBool(configSetupJson, "blink", value); + if (request->hasArg(F("blink"))) { + bool value = request->getParam(F("blink"))->value().toInt(); + jsonWriteBool(configSetupJson, F("blink"), value); saveConfig(); request->send(200); } - if (request->hasArg("reqReset")) { + if (request->hasArg(F("reqReset"))) { String tmp = "{}"; jsonWriteStr(tmp, "title", F("Вы действительно хотите перезагрузить устройство?
Идет перезагрузка устройства')\">Перезагрузить")); jsonWriteStr(tmp, "class", "pop-up"); request->send(200, "text/html", tmp); } - if (request->hasArg("reset")) { + if (request->hasArg(F("reset"))) { ESP.restart(); request->send(200); } - if (request->hasArg("test")) { - if (request->getParam("test")->value() == "ok") { - Serial.println("test pass"); - } - request->send(200); - } - //==============================mqtt settings============================================= - if (request->hasArg("mqttServer")) { jsonWriteStr(configSetupJson, "mqttServer", request->getParam("mqttServer")->value()); saveConfig(); @@ -237,7 +219,7 @@ void web_init() { request->send(200, "text/html", payload); } - //==============================push settings============================================= + //==============================telegram settings============================================= if (request->hasArg("telegramApi")) { jsonWriteStr(configSetupJson, "telegramApi", request->getParam("telegramApi")->value()); saveConfig(); @@ -266,15 +248,21 @@ void web_init() { myNotAsyncActions->make(do_BUSSCAN); request->redirect("/?set.utilities"); } - if (request->hasArg("uartEnable")) { - bool value = request->getParam("uartEnable")->value().toInt(); - jsonWriteBool(configSetupJson, "uartEnable", value); + if (request->hasArg("uart")) { + bool value = request->getParam("uart")->value().toInt(); + jsonWriteBool(configSetupJson, "uart", value); saveConfig(); #ifdef uartEnable uartInit(); #endif request->send(200); } + if (request->hasArg("uartEvents")) { + bool value = request->getParam("uartEvents")->value().toInt(); + jsonWriteBool(configSetupJson, "uartEvents", value); + saveConfig(); + request->send(200); + } if (request->hasArg("uartS")) { jsonWriteStr(configSetupJson, "uartS", request->getParam("uartS")->value()); saveConfig(); @@ -309,22 +297,7 @@ void web_init() { } }); - //==============================list of items===================================================== - //server.on("/del", HTTP_GET, [](AsyncWebServerRequest* request) { - // if (request->hasArg("file")) { - // itemsFile = request->getParam("file")->value(); - // } - // if (request->hasArg("line")) { - // itemsLine = request->getParam("line")->value(); - // } - // delElementFlag = true; - // deviceInit(); - // request->redirect("/?setn.device"); - //}); - /* - * Check - */ server.on("/check", HTTP_GET, [](AsyncWebServerRequest* request) { myNotAsyncActions->make(do_GETLASTVERSION); SerialPrint("I", "Update", "firmware version: " + String(lastVersion)); diff --git a/src/items/vButtonOut.cpp b/src/items/vButtonOut.cpp index 3ef137b0..fb422e34 100644 --- a/src/items/vButtonOut.cpp +++ b/src/items/vButtonOut.cpp @@ -20,17 +20,6 @@ ButtonOut::ButtonOut(String pin, boolean inv, String key, String type) { ButtonOut::~ButtonOut() {} void ButtonOut::execute(String state) { - if (_type == "UART") { - if (jsonReadBool(configSetupJson, "uartEnable")) { -#ifdef uartEnable - if (myUART) { - String msg = _key + " " + state; - myUART->print(msg); - SerialPrint("I", "<=UART", msg); - } -#endif - } - } if (state != "" && _pin != "") { if (state == "change") { state = String(!digitalRead(_pin.toInt())); From 0bb028229a71099555b0f942ffa97e5bb0ba2e5a Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 17 Dec 2020 22:59:44 +0100 Subject: [PATCH 48/94] ziped data --- data/index.json.gz | Bin 0 -> 516 bytes data/set.dev.json.gz | Bin 0 -> 545 bytes data/set.device.json.gz | Bin 0 -> 2741 bytes data/set.manual.json.gz | Bin 0 -> 340 bytes data/set.mqtt.json.gz | Bin 0 -> 831 bytes data/set.telegram.json.gz | Bin 0 -> 857 bytes data/set.udp.json.gz | Bin 0 -> 665 bytes data/set.utilities.json.gz | Bin 0 -> 623 bytes data/set.wifi.json.gz | Bin 0 -> 1026 bytes {data => data_ungzip}/index.json | 0 {data => data_ungzip}/set.dev.json | 0 {data => data_ungzip}/set.device.json | 0 {data => data_ungzip}/set.manual.json | 0 {data => data_ungzip}/set.mqtt.json | 0 {data => data_ungzip}/set.telegram.json | 0 {data => data_ungzip}/set.udp.json | 0 {data => data_ungzip}/set.utilities.json | 0 {data => data_ungzip}/set.wifi.json | 0 18 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/index.json.gz create mode 100644 data/set.dev.json.gz create mode 100644 data/set.device.json.gz create mode 100644 data/set.manual.json.gz create mode 100644 data/set.mqtt.json.gz create mode 100644 data/set.telegram.json.gz create mode 100644 data/set.udp.json.gz create mode 100644 data/set.utilities.json.gz create mode 100644 data/set.wifi.json.gz rename {data => data_ungzip}/index.json (100%) rename {data => data_ungzip}/set.dev.json (100%) rename {data => data_ungzip}/set.device.json (100%) rename {data => data_ungzip}/set.manual.json (100%) rename {data => data_ungzip}/set.mqtt.json (100%) rename {data => data_ungzip}/set.telegram.json (100%) rename {data => data_ungzip}/set.udp.json (100%) rename {data => data_ungzip}/set.utilities.json (100%) rename {data => data_ungzip}/set.wifi.json (100%) diff --git a/data/index.json.gz b/data/index.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..8c2326efca8f7ad1eb4bb8cddeaaec018becdeb2 GIT binary patch literal 516 zcmV+f0{i_RiwFn-?9*Qa0BLSyWq2-Xb8l_{&6PoG)Ib!+?}dJcA?GG>TX(w_q&Hy? zJqQaP#KSb1WXDcsV& z^M3!wBpGjRFy;hCM>4j~9(!8mwBy%Xo|4DHdv1+(wyr5wK%QQc6xVSzd0AddrSh!O z9gv^|m2mJQUf>MB;sR&InNzEQ;+6nP7UkN+HBm$r+zwmjoqJW3gpIP@x&xtMZ2j$gCH2v| z3it^xaZ$X%Iesf%7gK7;TbvhXjFMNyX)z`FXBknqW>*g20Z60E6a0Ph?1kc324WrO zA!tU!Jya$*Dx?3${ABQG(8BmqM)H;k4;~MPEriJnfLkU6p+ubVq=mA1#ym{ypV_yf;zLGd{om?1mhn#Uqz_#H2( zzg&lzk>Oe&BY@QI`F^VSi5D9aE8ztu@hzB4_&iOm9}3`7*?k#4q%WMXX@9%d^Q3{K zE_0{7%XDP4XtQ3K^k`Z-&+>A|Cx7nJpfAHtr`vt7bHBg4`%iAGKf>k)t$zSd<=(-3 G1^@ui@)!^ z&++8(BM*Q6AQdr7l;82fa>cIgSBou0A${ZKequ- z9B}03&v5gb%76-JR#q2XsXWW9$l?7uv~RrloUl*?01@YR#ly)ik0P{ jHY`hpvRq-!(@=^Q;L#?&(umu1BnokT7 literal 0 HcmV?d00001 diff --git a/data/set.device.json.gz b/data/set.device.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1a011335e6942d00fade3951557ccf17417ab83b GIT binary patch literal 2741 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 literal 0 HcmV?d00001 diff --git a/data/set.manual.json.gz b/data/set.manual.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1758467685592e37b410f1ec2c7b0baca6499b0e GIT binary patch literal 340 zcmV-a0jvHWiwFoG6wF@)0CQz@E^T3Mbzy8SYIARH0KJkiYQr!Xh4(=2U<}=C#inF& zheEdwh3=(;;>fldSu**5y9vfn3f+2%-k@Wb_6+uoD#@WH!G(|^gN^l`_`mlo)zgXN zxUrH6Pr;3x2ea17td|HdEDQ1kO1j>Gr3&Oqj(dVv?7$IJruIeFWp5eh(dz>U3L0;c z2p@7^C5a~cp|e|ufgZF=icI2=)E2M=+!oA?$mV+QpU6>IwXP-2S*y#6*XUs)AVsSK zG3P*HsP^1#AM}|oF^j?=xVXv~Pcw`z=RvU<>(qXml%gpu zOxmWsF|on4hpG>5{)lL${;_R#6^M)%tvQ z@qPa6yGu_N7Yw7&X4D~WP_T@KoP(wqt5yRnLVxvhz-VDbn><8a^|Q*u*d#5@m3S!+ z1+b7@(=&sv$s7mPFv~_VFPD_mwY6nU&tE9PbG4zO=Nk|@K`Ag5DM|%iVG_qamO(pJ z@Es|M@RauCgrY9)_fuO66M2{m)5Z=8JyD45{gu4+Jwo@hBfJX5HX?Kfx#NLQTM$O2 zPcbWeOHc#8X3JykeJR}5oDH#LDcjvbGPEjrdl=~>P3 z7!sC;9Z{Palu$nuS#fXZ;5d}3dmu!N!bIF1!+=BrHuaf=a(#r76Ss2<_9j!a!?>6y zDKdUOCEDX53E|iwy}44N5KJazI?5T-@-diy0!a+{w8S0{n>Iq`Zt@;bzK9S z786UF++a&5iB;cmuxT-|=z|7}7Guk7dvQ$Pz4*y$%hU!@5bDAOok(Q>u8(gg(Z%pI zx{kg_0~jxdUxr_e;Tb3f(a-2Ix`Ocv#sMe|x!;L9Y;E{Y+vLzt&uuOz$tx4Qd?T4#45;qLs^zV^18=wTjhFGw<(7xq`=Imn5-ljFm|I5K z?->E}h;1yrc`ZLRBQ}{F*(3}sqvH2K2amg)h153Xityml+p7H33?JDx+$>lsjyt+` zpAe_ze!pJyx9UX+1_s!~))QGdWUbwYtlPY`|Jz8^y~pBCy`=j1dP#JlO2Q!e6@80- z3{QvWASDIje0T+VPkc8wG21VB$YXkhD#iP@aLvwMV{|CjE J&0Qc50072kkcI#N literal 0 HcmV?d00001 diff --git a/data/set.telegram.json.gz b/data/set.telegram.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..8386cf0920a5893d9689e849e263f2c930853178 GIT binary patch literal 857 zcmV-f1E%~RiwFo7Zqr``0CQz@E_7vVWoL3>Z7ynaZ*BmsR^4jbFciKY^c_UX)|lT)-UG@sn8|}!B;|%P@ zg<>2ZopW^l^(lu7j}R)fIBQeOFH}%nuYpsHSSvmefxGhF=d7?CiyX|X@MpyfB9WFf zmLpRR_Q8<0=@@?3;O#cpFv@7ME|)^8YsQk*c>OFS94i*A#p|cwdJ?4|Oe9f;!G|%4 z1D9y0_6F!qnN&C-_wbP6F6s9ZSrR68h$jq_v~l2wLL~1mXW4fs>!f>l5s25AvOR45 z>j<|5I+9oNEbZJe{L=hkS=9z zAadm^|3IE2$oCG(YjrBe@`t)mpFnzw)Uh1H^9`0m`5Uxuva6A}g>oc|ta>CG4^t3( zJuTaJ+KY#~sAXe;c6a8Q$*fySp|#Cj)4-mUo?;}=livSh-`?9rf_Dg;Yv?}Fi;4Md zmu9Chqm(0$W*`J-nV9hw?`PpfI6#T7c!HAf8jUAWIYAT=5%+#LGn?Z<6xl~=3cFG+ zl^$13>bnj;s89ybN9JamV<-jbrM9DH(tQ+$u({NQJSZ~+cOeS5SDmbz9BSE(SW8>A zHrxmLj^N(rQq#e$4)EOAM3Z|a@hYh7_K?pVYNExRm-?xqNMv}}q=8>SYiI6OjET54af$p8g7yZJn3&5dp&j8Gh!1o9k07aTs zgIdwusuguP-~(mcn%jgNeX`e54v0r!Dx)Dv8yuJWRF7|^O?$+w>i%uO5ILkRLLwTh z_0mjFLz%G}vh-*e*r67yvqLN`^d2+@3QrG;&%6tC=uJb@Owgq}#9}L1dKwHRH4p8Ob!4yTCy(G~{l+Z+>oW zH$OJNHn+dO;qBa!APKrL9)*`IJQarM!Rt6%y3@{23u+~2mVTNaL}UeBOJAVrZ4AI9 z>|$6KFN5hu=a6RSHi)}G*K0`@EX#JX9ZVuv3?5}6Nd>rtJZtQ;FSwkSdw7|`JHq89 ziS9ch0dOUaPOHql57NP(=L;3g&8aq&SzN8sEysGr;Cg)dmM4&9)kdoQ1j+JmN47{? z-V7GPZB#NWJ>6MLfI#kqvSqVyv=iMF5q;qlX1z|k{iI)(V@YIX2sNTKwLg^4YfTZV z$?Coaz^;Ct=2`-^9=09op)>7SjZUN1!=kH^USKOI3t&L`(#cCbJYA4D;?e`9vD$jo z-_pJUP>X_fIvRwyC(Q0p@$l3aB$y*lTNA#DDT{kfN3Wbj2~M4p@hkK6okHZrgz_}$ zIbFR%9$t@Pl}hRvi+J(m&AEB{jwY0H8KJ4dug&yF63b4O4O)6MXc;W{!Wc#sof`^8 zpeI-e{mNV&9_;tH>^_8JwyZJTTjvm&*y+5=27Oy4%EY3S_^1}N> zysvHV*>ru;=$uuf^;0ag+`E9U$ns!!Z_y4XlRLPl)BoP;!HflSU#&_Kno_MKegF@Z zWQ^H;y$WVT9v)_E3*)uNBG`Y+WaTwF=26Y*S;?H*44BvS_$d1WYSV+1K?wi=;8a8G literal 0 HcmV?d00001 diff --git a/data/set.utilities.json.gz b/data/set.utilities.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..5356db03b5880f04f4b50cf660e98c0b895625c6 GIT binary patch literal 623 zcmV-#0+9V5iwFpa(A!@G0CQz@E_HNiY-x09WpgfSb8l_{&6VG3+b|Ty?+bkgW7w|h zG-wM=YS)OcfDT-?P#P*y}Z7E$bhRCiA)v!0VYOg|{s5>{5(4x`khJ+Du zlD}FINl7S1qN~)S*n$))`^qlajLq2`rqX0T}sK4nWbmwk4w@DI>j@iWESGt1X*=5v5&kFy%h@V5$^^AmQ?-zm^6*$;jO`hxw0cm)75 zF<{6BMKH1%w}ZrO79DFSaSFc^QSXMAV>#!w*QajlpnmVr#y+AgY}xd%G@nOCQSP}Z z*ps$~0;d>7JC2LJHLtw|p5s<{m-1PB6Ss`gIl4Otougt3QvYWd^gG0$mtfFu6+&o&xce@fS=Z_>!U0n!Vg4e$Q-X*Z1fa#JBFJ8Mxrjq0oDUwuwQV{C85zX5aZ J+BAj<006^%D~kXC literal 0 HcmV?d00001 diff --git a/data/set.wifi.json.gz b/data/set.wifi.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..d5cab6bfe66f4538aa23730a7f95f984a74a5a78 GIT binary patch literal 1026 zcmV+d1pWITiwFqWZqr``0CQz@E_Z2WX)bDWZ*BnXSYL0`Fc5!7;yXk|zzf;hjUm*I zA^wOr{s@pJQT4$|+}13$P2y~Ys?uNxUJyt;!UJDn0wxA%@D-eI#Mw@oP9$rpQjjK8 zH97m7yPxlVcTPJvdBUlIEF!+}Fl4lHE>-!6cJyT-h>S{U zT3TNbMEDpCRpdMQLmtG><-YtB@5I}3H+~`a<2PVOyYx{A4wY};4tq9hx51ZPvyySG zs->QnYL?RRMJ@Q9RI!|nFM#P}Hv&_U?Pg$Z3f*I_!F5;C?TkYn3@Ga1;V{!>Fp-Y~ zVSBiZB41RDet53PzE9{zzK5%kxQPh8gF1&DAy*Ja)tbdT_l}?zd~L^P?uL>|L+J*S zmn`RFy@$m*xj}A0P;R%cQ;^St&sV|vrb*VraleJsO$iFBns=+ zGVjA2Jl+XdL_J$sga=RESW%yb#gXR`+5uC>?(O$@>8 z+`P*ofrD#|QtXNnyeQ;n4i^J$I}C|eOvh|Z3FjO*vjs7q%D;N3*nhUBg#T%aq6Eo{ zDkzH!6j3%Xb#x{}4ccsAv=g^+yb5P&PKD!kvAf~0eh#smqT@-vmEYv|_;vgePU>gz zD=U5~KgzxM864%G<-WSY6byGky93HyOa4@|qsR8-L3+sley}NGXy1@^eQ&k$S*?{N z2z|VmurenmOlYB3To;Go9Y*M%ZlH)C-8_x{P|M*+Kr_&ESs2vLEmbEYnzE}Reso7Q z`eINQOM#lU)gJDih$o#Bgsf{CWH4)l|L-J|3TpY^6x2F)W+^239y;nL=(Yzr?A^kU zR0wx5f72E7EGzi|PJnHI`8|G>5Ax_5OBT!uake5$0|f<@ibpW<3ygKsv zztNFLzn?JV&4heRt2S&J91Dp`aJ^0X*lVchtyE7peB2gV*dm_j*3VbRBC2)vlAcnD zv5`+YwC-XG)NkY^zDqn0(~0;EH49qp9< Date: Fri, 18 Dec 2020 00:53:39 +0100 Subject: [PATCH 49/94] change to 4mb --- data/dev_conf.txt | 0 data/edit.htm.gz | Bin 0 -> 5455 bytes data/presets/presets.c.txt | 46 ++++++++++++++++++++ data/presets/presets.s.txt | 84 ++++++++++++++++++++++++++++++++++++ data/set.device.json.gz | Bin 2741 -> 2741 bytes data_ungzip/set.device.json | 2 +- include/Consts.h | 2 +- platformio.ini | 2 +- 8 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 data/dev_conf.txt create mode 100644 data/edit.htm.gz create mode 100644 data/presets/presets.c.txt create mode 100644 data/presets/presets.s.txt diff --git a/data/dev_conf.txt b/data/dev_conf.txt new file mode 100644 index 00000000..e69de29b diff --git a/data/edit.htm.gz b/data/edit.htm.gz new file mode 100644 index 0000000000000000000000000000000000000000..5786121f98a84b5695314c9ad8e5537f57650ba4 GIT binary patch literal 5455 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 z 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 tUp1 +btn1 0 +telegram нагрев#выключен 1 +end +t1 < tLow1 +btn1 1 +telegram нагрев#включен 1 +end* +t2 > threshold+-2 +heater2 0 +end +t2 < threshold+-2 +heater2 1 +end +timenow = time21 +threshold threshold1 +end +timenow = time22 +threshold threshold2 +end +timenow = time23 +threshold threshold3 +end +timenow = time24 +threshold threshold4 +end* +h3 > hUp3 +hUp3 0 +telegram полив#выключен 1 +end +h3 < hLow3 +hUp3 1 +telegram полив#включен 1 +end* +timenow = time41 +btn41 1 +btn42 0 +end +timenow = time42 +btn41 0 +btn42 1 +end* +btn51 = 1 +btn52 1 +btn53 1 +btn54 1 +pwm51 200 +pwm52 800 +txt5 включено +end +btn51 = 0 +btn52 0 +btn53 0 +btn54 0 +pwm51 800 +pwm52 200 +txt5 выключено +end* +button = 1 +count input +end +count = 0 +button 0 +end* +sensor = 1 +text обнаружено +time %date% +telegram text обнаружено#движение 1 +end +reset = 1 +text не#обнаружено +time %date% +reset 0 +end* +sensor = 1 +light 1 +count period +end +count = 0 +light 0 +end* +switch = 1 +light change +end* \ No newline at end of file diff --git a/data/set.device.json.gz b/data/set.device.json.gz index 1a011335e6942d00fade3951557ccf17417ab83b..c0686cbfcac8b920c6914e072b8e89f3fe6d34e6 100644 GIT binary patch delta 2603 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 NUjYL4(k@{v005i$`IZ0x delta 2603 zcmV+`3e@$r6}1%xABzYGrNWU0B!4N+hLWX-A_PbjQ4RT9dj*nY`o91WqZ114qi9(lUkouLp6)^U(BiSWxjf3JP^uIU4IY>r88u= zEL(*FNFpaCah*&6!Iu1Z9AZgb0Zz6F|FKecC>ZfuzU?WweUZK%XCC=^uF6aCyR`+j8>n`)NU*ksF}v4v*!)8@$6~>(|C{di}Tj+xZM~(*`x4-;+QBDu1m6q?Hj# zOUwN!i7sXX7XFi4$v(jHf>>k? zA(B5cF0nb36@((_KNF1)7!2?M9-n4mTl|Y`o*qAkbN+=0B2NS&PjsPl9=96ET*Bl4 zQy|m0MHQMjdeXn!soVf%2yUM4KoQP2Th`lDzPIIGdGnzUU#F zl`;01H8_J=_y9Ffl}MfEO8Hm)3ljT43@-_wK8ABxC3~dDs(uV$$T-s|rx#9bwd|cH zc~`f|p=d3FxF_r_T)cx7+Rz4}UK{7TGmj{V(R+A;_`>$LRQ0chOR+{)^Es2C12zUJ z?uZgI?9f`1#5Pza7iBM4^QcTLaAPH{#jGS|h z2w&^U9v~*ha*Bo@B+AiqJWpfj3i4SKVoHIR= z6x{qdH3ChPM*>!FIh;U|$4Jb8B<*yRD5<1^RH1;U-(hvo>^(j#3JxL{aDkNoWGYL} z!{RxRq<@B40hAPl$qY|?B+*A+FIH5?8<&c3B!TFOPHGC$4K9BYk4lxi9YNv=*|3Tv zF~pIFs5N}*qNJz=38el_!o$*ty(65?qs_F_-$(SGd6a;xX(38lQ_^yUBbJ8j+C~R! zB{ZJa)&op5|CYAoYTv$otdrZ>U({GAe`8~TW`97X<^0cWEA}`VjMgPZQ}LWwHbro( z0WYdg@fuAO4Is$2^^I*(S%TFa^82et<5MxD5Cwi%bWEeh~O2_8%;8} zi=vBvi2_fPtSf*lu>5FZrGgd(xGZp+#8B5Km-LE+nepcXH6c2XJ3+$`kR(6unIsrL z(SPbU0hvUc|Hc8Ma88T<;ZB=P+mWY$+;ZNZ@sQi$(p?P~jAmCvVYyQD5_h^8HiG`k zp9z2u>T#}bA03CvHytDkoDNGDkz`*Lv?=}tB-PG>a~(+;8{QyaMB9heqG7o=gbt|P zZYZg7uk)j@S=0aqgqjGmH9D!QAxao>g@2tO6hN?nQ&GfPBRZHDz4^Pv7_&cIP!=P9 zz5->#jSkBt(C0-DFFJngZ`!*3SSlF#P%7u192mn7^?@Z<(sIYM*6F^OmAtaiXMrEW z{g#E!`6InqO!Y`@H-@iTVxaxtOm|Apd!n-<6x;ws%pvr5; z=8|Q(aOO&gEn6`O)BEAf-l}-Z3l)>7EniUuoKzgfN{&`ow=BK^Zj0r$lX^~DCQX9s z5^POk#pKyvkNKz&Lb@1&QUJq7!+-cqHZ%MTd~IGoDUsE$TUJ(`qHfBbWrN|{rzgdA z*77{7%%_&EUt55ehxks>a@H-&8fxx1%-I2ntmWi2CnF{8(~@f$dR|(xWix&zCB7s( zYF@9n87XC-hBBrje3+!dsn(mw z*Jjs4d-<5XWKY>G_K4kwlJt!P|J`D@*+aN|#a^t9XV=Fh7*k$Gf!asD4Q#V0+c!kw z{@9V_2gviKRczL<5c6kR4Sz~+BSQDLFs3ePAvmZOSR);A9DcclHr9AT_clpYH0$*Te>qj))k}|~Yo^=#m^$|%2iELog{5o=E zY+$0QR^g7(UnmxI5OYZOL!vQg`6Fe&lBO3+UM7_WHypMf>~l~Ia(_&sK%kqt_v-`T z$VoHQI(DA$yxkRV2pmti< z+Gm32L;KjPZzQ6)EPu+9J}}X97sev^Dj@JT`f4a(k1 Date: Fri, 18 Dec 2020 04:39:44 +0300 Subject: [PATCH 50/94] FSInfo --- include/Consts.h | 2 +- include/FileSystem.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 include/FileSystem.h diff --git a/include/Consts.h b/include/Consts.h index 0bce4c82..8905e172 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -16,7 +16,7 @@ #endif //===========FileSystem============================================================================================================================================== -#define littlefs_on +#define USE_LITTLEFS true //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN 2 diff --git a/include/FileSystem.h b/include/FileSystem.h new file mode 100644 index 00000000..09fe0307 --- /dev/null +++ b/include/FileSystem.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Consts.h" + +#define FILE_READ "r" +#define FILE_WRITE "w" +#define FILE_APPEND "a" + +#if USE_LITTLEFS +#include +extern FS LittleFS; +using littlefs_impl::LittleFSConfig; +extern FS *filesystem; +#define FileFS LittleFS +#define FS_NAME "LittleFS" +#else +extern FS *filesystem; +#define FileFS SPIFFS +#define FS_NAME "SPIFFS" +#endif + +/* +* Информация о ФС + size_t totalBytes; // всего + size_t usedBytes; // использовано + size_t maxOpenFiles; // лимит на открые файлы + size_t maxPathLength; // лимит на полное пути + имя файла + + FSInfo buf; + getInfo(buf); + size_t freeBytes = buf.totalBytes - buf.usedBytes; + float freePer = buf.usedBytes / buf.totalBytes * 100; +*/ +bool getInfo(FSInfo& info) { + return FileFS.info(info); +} \ No newline at end of file From 5f9d9ec48eaee1d8ada09094a8ced4ef80f8a60c Mon Sep 17 00:00:00 2001 From: Yuri Trikoz Date: Fri, 18 Dec 2020 04:44:23 +0300 Subject: [PATCH 51/94] Use system defined led --- include/Consts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Consts.h b/include/Consts.h index 8905e172..04c9d6fc 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -19,7 +19,7 @@ #define USE_LITTLEFS true //================================================================================================================================================================== #define NUM_BUTTONS 6 -#define LED_PIN 2 +#define LED_PIN LED_BUILTIN //===========MQTT================================================================================================================================================= #define MQTT_RECONNECT_INTERVAL 20000 //==========Telemetry============================================================================================================================================= From eddb2eb289408de789cda5733465160571c2113e Mon Sep 17 00:00:00 2001 From: Yuri Trikoz Date: Fri, 18 Dec 2020 13:43:13 +0300 Subject: [PATCH 52/94] Mqtt Reserve --- src/MqttClient.cpp | 59 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 951d16c4..062d8d36 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -6,8 +6,25 @@ #include "Global.h" #include "Init.h" +enum MqttBroker {MQTT_PRIMARY, MQTT_RESERVE}; +MqttBroker activeBroker = MQTT_PRIMARY; + String mqttPrefix; String mqttRootDevice; +String mqttPass; +String mqttServer; +String mqttUser; +uint16_t mqttPort{0}; +uint16_t reconnectionCounter{0}; +bool primaryExist = false; + +const String getParamName(const char* param, MqttBroker broker) { + return String("mqtt") + param + (broker == MQTT_RESERVE? "2": ""); +} + +bool checkBrokerParams(MqttBroker broker) { + return !jsonReadStr(configSetupJson, getParamName("Server", broker)).isEmpty(); +} void mqttInit() { myNotAsyncActions->add( @@ -29,6 +46,16 @@ void mqttInit() { } else { SerialPrint("E", "MQTT", "lost connection"); + if (reconnectionCounter++ > 5) { + if (activeBroker == MQTT_PRIMARY) { + if (checkBrokerParams(MQTT_RESERVE)) { + activeBroker = MQTT_RESERVE; + } + } else { + activeBroker = MQTT_PRIMARY; + } + reconnectionCounter = 0; + } mqttConnect(); } } @@ -77,25 +104,35 @@ void mqttSubscribe() { } } -boolean mqttConnect() { - SerialPrint("I", "MQTT", "start connection"); - String addr = jsonReadStr(configSetupJson, "mqttServer"); - if (!addr) { - SerialPrint("E", "MQTT", "no broker address"); +bool readBrokerParams(MqttBroker broker) { + if(!checkBrokerParams(broker)) { return false; } - int port = jsonReadInt(configSetupJson, "mqttPort"); - String user = jsonReadStr(configSetupJson, "mqttUser"); - String pass = jsonReadStr(configSetupJson, "mqttPass"); + mqttServer = jsonReadStr(configSetupJson, getParamName("Server", broker)); + mqttPort = jsonReadInt(configSetupJson, getParamName("Port", broker)); + mqttUser = jsonReadStr(configSetupJson, getParamName("User", broker)); + mqttPass = jsonReadStr(configSetupJson, getParamName("Pass", broker)); + + return true; +} + +boolean mqttConnect() { + SerialPrint("I", "MQTT", String("use ") + (activeBroker == MQTT_PRIMARY? "primary": "reserve")); + if (!checkBrokerParams(activeBroker)) { + SerialPrint("E", "MQTT", "empty broker address"); + return false; + } + readBrokerParams(activeBroker); + SerialPrint("I", "MQTT", "start connection"); mqttPrefix = jsonReadStr(configSetupJson, "mqttPrefix"); mqttRootDevice = mqttPrefix + "/" + chipId; - SerialPrint("I", "MQTT", "broker " + addr + ":" + String(port, DEC)); + SerialPrint("I", "MQTT", "broker " + mqttServer + ":" + String(mqttPort, DEC)); SerialPrint("I", "MQTT", "topic " + mqttRootDevice); setLedStatus(LED_FAST); - mqtt.setServer(addr.c_str(), port); + mqtt.setServer(mqttServer.c_str(), mqttPort); bool res = false; if (!mqtt.connected()) { - if (mqtt.connect(chipId.c_str(), user.c_str(), pass.c_str())) { + if (mqtt.connect(chipId.c_str(), mqttUser.c_str(), mqttPass.c_str())) { SerialPrint("I", "MQTT", "connected"); setLedStatus(LED_OFF); mqttSubscribe(); From 9ac72f14e065d17d7ac6a029fda02e59e98950da Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 18 Dec 2020 14:28:31 +0100 Subject: [PATCH 53/94] telegram --- include/Global.h | 1 + src/Global.cpp | 5 +++-- src/Telegram.cpp | 22 ++++++++++------------ src/Web.cpp | 11 +++++------ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/include/Global.h b/include/Global.h index b4ca622a..bdd63086 100644 --- a/include/Global.h +++ b/include/Global.h @@ -54,6 +54,7 @@ extern String configSetupJson; //все настройки extern String configLiveJson; //все данные с датчиков (связан с mqtt) extern String configStoreJson; //все данные которые должны сохраняться extern String configOptionJson; //для трансфера +extern String telegramMsgJson; extern String getValue(String& key); // Mqtt diff --git a/src/Global.cpp b/src/Global.cpp index 1cd2a46c..93db61b1 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -10,7 +10,7 @@ WiFiClient espClient; PubSubClient mqtt(espClient); StringCommand sCmd; AsyncWebServer server(80); -OneWire *oneWire; +OneWire* oneWire; DallasTemperature sensors; /* @@ -25,6 +25,7 @@ String configSetupJson = "{}"; String configLiveJson = "{}"; String configStoreJson = "{}"; String configOptionJson = "{}"; +String telegramMsgJson = "{}"; // Mqtt String chipId = ""; @@ -70,7 +71,7 @@ String presetName; String serverIP; // Scenario -int scenario_line_status[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; +int scenario_line_status[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int lastVersion; boolean busScanFlag = false; diff --git a/src/Telegram.cpp b/src/Telegram.cpp index 6663b20c..af81e785 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -70,23 +70,21 @@ void telegramMsgParse(String msg) { } void sendTelegramMsg() { + String id = sCmd.next(); String msg = sCmd.next(); - String type = sCmd.next(); msg.replace("#", " "); - if (type == "1") { - static String prevMsg; - if (prevMsg != msg) { - prevMsg = msg; - if (msg != "na") { - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); - } - SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); - } - } - else if (type == "2") { + if (id == "often") { myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); } + else { + String prevMsg = jsonReadStr(telegramMsgJson, id); + if (prevMsg != msg) { + jsonWriteStr(telegramMsgJson, id, msg); + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); + SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); + } + } } bool isTelegramEnabled() { diff --git a/src/Web.cpp b/src/Web.cpp index 6027fcc5..e3ff767e 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -26,12 +26,11 @@ void web_init() { if (request->hasArg(F("addPreset"))) { addPreset2(request->getParam(F("addPreset"))->value().toInt()); - if (FLASH_SIZE_1MB) { - jsonWriteStr(configSetupJson, F("warning1"), F("

Присеты не доступны, модуль на 1mb

")); - } - else { - jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); - } +#ifdef FLASH_SIZE_1MB + jsonWriteStr(configSetupJson, F("warning1"), F("

Присеты не доступны, модуль на 1mb

")); +#else + jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); +#endif request->redirect(F("/?set.device")); } From 19f728e209353f5036bbf6ddc59707a067e83c88 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 18 Dec 2020 21:48:06 +0100 Subject: [PATCH 54/94] prime and sec broker --- data/config.json | 13 ++++-- data/items/items.txt | 24 +++++------ data/set.mqtt.json.gz | Bin 831 -> 978 bytes data_ungzip/set.mqtt.json | 82 +++++++++++++++++++++++++++++++++++++- src/ItemsList.cpp | 4 +- src/Web.cpp | 55 ++++++++++++++++++++----- 6 files changed, 148 insertions(+), 30 deletions(-) diff --git a/data/config.json b/data/config.json index 84768aa7..57cfd757 100644 --- a/data/config.json +++ b/data/config.json @@ -12,10 +12,15 @@ "mqttPrefix": "/iotTest", "mqttUser": "rise", "mqttPass": "23ri22se32", + "mqttServer2": "", + "mqttPort2": 1883, + "mqttPrefix2": "/iotTest", + "mqttUser2": "", + "mqttPass2": "", "scen": "1", "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", - "teleginput":"0", + "teleginput": "0", "weblogin": "admin", "webpass": "admin", "MqttIn": "0", @@ -24,7 +29,7 @@ "oneWirePin": "2", "serverip": "http://206.189.49.244", "uart": "0", - "uartS":"9600", - "uartTX":"12", - "uartRX":"13" + "uartS": "9600", + "uartTX": "12", + "uartRX": "13" } \ No newline at end of file diff --git a/data/items/items.txt b/data/items/items.txt index d19eba63..3ff53afd 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -1,24 +1,24 @@ -1;0;button-out;id;toggle;Кнопки;Освещение;order;pin -2;0;button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1] +1;0;button-out;id;toggle;Кнопки;Освещение;order;gpio +2;0;button-out;id;toggle;Кнопки;Освещение;order;gpio;inv[1] 3;0;button-out;id;toggle;Кнопки;Освещение;order -4;0;button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] -5;0;pwm-out;id;range;Ползунки;Яркость;order;pin +4;0;button-in;id;toggle;Кнопки;Освещение;order;gpio;db[20] +5;0;pwm-out;id;range;Ползунки;Яркость;order;gpio 6;0;inoutput;id;inputDigit;Ввод;Введите#цифру;order 7;0;inoutput;id;inputTime;Ввод;Введите#время;order 8;0;inoutput;id;anydata;Вывод;Сигнализация;order -9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gol;map[0,1024,0,100];c[1] -10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;sal;index[0];int[10] -11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;cin;map[0,500,0,100];c[1];int[10] -12;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht11];c[1] -13;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht11];c[1] -14;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;thd;type[dht22];c[1] -15;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;thd;type[dht22];c[1] +9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gpio;map[0,1024,0,100];c[1] +10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10] +11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10] +12;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1] +13;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1] +14;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1] +15;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1] 16;0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] 17;0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] 18;0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] 19;0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] 20;0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] -21;0;impuls-out;id;na;na;na;order;pin +21;0;impuls-out;id;na;na;na;order;gpio 22;0;count-down;id;anydata;Таймер;Обратный#отчет;order 23;0;inoutput;id;anydata;Вывод;Вывод#uart;order 24;0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] diff --git a/data/set.mqtt.json.gz b/data/set.mqtt.json.gz index 6ee1c9ab6f4cbcea53eba60dce852f3b3893da47..f6c1c7c4139144305d39340df0cc1ce0515b8f28 100644 GIT binary patch literal 978 zcmV;@11gFc5y9#D9oD8!rTC3TR3T zleTGZOl&aiq3T20xT#0#1jmIUR0+o8_8Z22!p2q&Cc$q={?X1(+_aFkX;<1lpsLCF z&hdTzY~ST_GWSGL=F1MLV9T4=l>%>rW+hZDdx+KDNN&#MAV#hAx zPOIzCx2?p?7TS{&V$@Kx8QWr*hK(pwO;j=JHk%La&81%B+nDSpdw5xA+Xf~(hSlxJ zC^PI3-e)DreTfkT{%Xl~%KKbO4W&v4cgZGU^}hvY{*8a@UyAFs;{OazLHO&pg42!% zJa!yDaD+`()ibNBPjeO9F_>k`j?($EHHjfqk+Fd#oTDFT6(CT~oVm|002+ThGF zNm^zlb!dZg$9QSUCV@E)>y{)puH#`|`RK53k(pzTt7pL{XGC8ILBd#d8@U?H1rt@+ zR86QDYQX!YjV+>=5tMy2msCAfv1uYQ5PYf5m_vHjZM()HZ1W+~SWyYxS!s{zDI1R47Wae8P31pAwd*yKm1?8X>blw6d=xn zGZ1*(xaJZ>kK5!gmpn31bnu=N(lam!x`vDm@DUlcSBKwml%gpu zOxmWsF|on4hpG>5{)lL${;_R#6^M)%tvQ z@qPa6yGu_N7Yw7&X4D~WP_T@KoP(wqt5yRnLVxvhz-VDbn><8a^|Q*u*d#5@m3S!+ z1+b7@(=&sv$s7mPFv~_VFPD_mwY6nU&tE9PbG4zO=Nk|@K`Ag5DM|%iVG_qamO(pJ z@Es|M@RauCgrY9)_fuO66M2{m)5Z=8JyD45{gu4+Jwo@hBfJX5HX?Kfx#NLQTM$O2 zPcbWeOHc#8X3JykeJR}5oDH#LDcjvbGPEjrdl=~>P3 z7!sC;9Z{Palu$nuS#fXZ;5d}3dmu!N!bIF1!+=BrHuaf=a(#r76Ss2<_9j!a!?>6y zDKdUOCEDX53E|iwy}44N5KJazI?5T-@-diy0!a+{w8S0{n>Iq`Zt@;bzK9S z786UF++a&5iB;cmuxT-|=z|7}7Guk7dvQ$Pz4*y$%hU!@5bDAOok(Q>u8(gg(Z%pI zx{kg_0~jxdUxr_e;Tb3f(a-2Ix`Ocv#sMe|x!;L9Y;E{Y+vLzt&uuOz$tx4Qd?T4#45;qLs^zV^18=wTjhFGw<(7xq`=Imn5-ljFm|I5K z?->E}h;1yrc`ZLRBQ}{F*(3}sqvH2K2amg)h153Xityml+p7H33?JDx+$>lsjyt+` zpAe_ze!pJyx9UX+1_s!~))QGdWUbwYtlPY`|Jz8^y~pBCy`=j1dP#JlO2Q!e6@80- z3{QvWASDIje0T+VPkc8wG21VB$YXkhD#iP@aLvwMV{|CjE J&0Qc50072kkcI#N diff --git a/data_ungzip/set.mqtt.json b/data_ungzip/set.mqtt.json index 58223acf..179f19d6 100644 --- a/data_ungzip/set.mqtt.json +++ b/data_ungzip/set.mqtt.json @@ -19,6 +19,14 @@ { "type": "hr" }, + { + "type": "h3", + "title": "Основной брокер", + "style": "width:100%;float:left;" + }, + { + "type": "hr" + }, { "type": "h4", "title": "{{SetMQTTServerName}}", @@ -79,6 +87,77 @@ "state": "{{mqttPass}}", "style": "width:40%;float:right" }, + { + "type": "hr" + }, + { + "type": "h3", + "title": "Резервный брокер", + "style": "width:100%;float:left;" + }, + { + "type": "hr" + }, + { + "type": "h4", + "title": "{{SetMQTTServerName}}", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "mqttServer2-arg", + "state": "{{mqttServer2}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "{{SetMQTTPort}}", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "mqttPort2-arg", + "state": "{{mqttPort2}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "{{SetMQTTPrefix}}", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "mqttPrefix2-arg", + "state": "{{mqttPrefix2}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "{{SetMQTTUserName}}", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "mqttUser2-arg", + "state": "{{mqttUser2}}", + "style": "width:40%;float:right" + }, + { + "type": "h4", + "title": "{{SetMQTTPassword}}", + "style": "width:60%;float:left;" + }, + { + "type": "input", + "title": "", + "name": "mqttPass2-arg", + "state": "{{mqttPass2}}", + "style": "width:40%;float:right" + }, { "type": "h3", "name": "my-block", @@ -89,9 +168,10 @@ "type": "button", "title": "{{ButSave}}", "style": "width:100%;float:left;", - "action": "set?mqttServer=[[mqttServer-arg]]&mqttPort=[[mqttPort-arg]]&mqttPrefix=[[mqttPrefix-arg]]&mqttUser=[[mqttUser-arg]]&mqttPass=[[mqttPass-arg]]", + "action": "set?mqttServer=[[mqttServer-arg]]&mqttPort=[[mqttPort-arg]]&mqttPrefix=[[mqttPrefix-arg]]&mqttUser=[[mqttUser-arg]]&mqttPass=[[mqttPass-arg]]&mqttServer2=[[mqttServer2-arg]]&mqttPort2=[[mqttPort2-arg]]&mqttPrefix2=[[mqttPrefix2-arg]]&mqttUser2=[[mqttUser2-arg]]&mqttPass2=[[mqttPass2-arg]]", "class": "btn btn-block btn-default" }, + { "type": "button", "style": "width:100%;float:left;", diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index df27a11c..361b0538 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -48,8 +48,8 @@ void addItem2(String param) { seachingLine.replace("id", name + String(rnd)); seachingLine.replace("order", String(getNewElementNumber("order.txt"))); - if (seachingLine.indexOf("pin") != -1) { - seachingLine.replace("pin", "pin[" + String(getFreePinAll()) + "]"); + if (seachingLine.indexOf("gpio") != -1) { + seachingLine.replace("gpio", "pin[" + String(getFreePinAll()) + "]"); } seachingLine = deleteBeforeDelimiter(seachingLine, ";"); diff --git a/src/Web.cpp b/src/Web.cpp index e3ff767e..0d4c5875 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -171,33 +171,66 @@ void web_init() { } //==============================mqtt settings============================================= - if (request->hasArg("mqttServer")) { - jsonWriteStr(configSetupJson, "mqttServer", request->getParam("mqttServer")->value()); + //primary + if (request->hasArg(F("mqttServer"))) { + jsonWriteStr(configSetupJson, F("mqttServer"), request->getParam(F("mqttServer"))->value()); saveConfig(); myNotAsyncActions->make(do_MQTTPARAMSCHANGED); request->send(200); } - if (request->hasArg("mqttPort")) { - int port = (request->getParam("mqttPort")->value()).toInt(); - jsonWriteInt(configSetupJson, "mqttPort", port); + if (request->hasArg(F("mqttPort"))) { + int port = (request->getParam(F("mqttPort"))->value()).toInt(); + jsonWriteInt(configSetupJson, F("mqttPort"), port); saveConfig(); myNotAsyncActions->make(do_MQTTPARAMSCHANGED); request->send(200); } - if (request->hasArg("mqttPrefix")) { - jsonWriteStr(configSetupJson, "mqttPrefix", request->getParam("mqttPrefix")->value()); + if (request->hasArg(F("mqttPrefix"))) { + jsonWriteStr(configSetupJson, F("mqttPrefix"), request->getParam(F("mqttPrefix"))->value()); saveConfig(); myNotAsyncActions->make(do_MQTTPARAMSCHANGED); request->send(200); } - if (request->hasArg("mqttUser")) { - jsonWriteStr(configSetupJson, "mqttUser", request->getParam("mqttUser")->value()); + if (request->hasArg(F("mqttUser"))) { + jsonWriteStr(configSetupJson, F("mqttUser"), request->getParam(F("mqttUser"))->value()); saveConfig(); myNotAsyncActions->make(do_MQTTPARAMSCHANGED); request->send(200); } - if (request->hasArg("mqttPass")) { - jsonWriteStr(configSetupJson, "mqttPass", request->getParam("mqttPass")->value()); + if (request->hasArg(F("mqttPass"))) { + jsonWriteStr(configSetupJson, F("mqttPass"), request->getParam(F("mqttPass"))->value()); + saveConfig(); + myNotAsyncActions->make(do_MQTTPARAMSCHANGED); + request->send(200); + } + //secondary + if (request->hasArg(F("mqttServer2"))) { + jsonWriteStr(configSetupJson, F("mqttServer2"), request->getParam(F("mqttServer2"))->value()); + saveConfig(); + myNotAsyncActions->make(do_MQTTPARAMSCHANGED); + request->send(200); + } + if (request->hasArg(F("mqttPort2"))) { + int port = (request->getParam(F("mqttPort2"))->value()).toInt(); + jsonWriteInt(configSetupJson, F("mqttPort2"), port); + saveConfig(); + myNotAsyncActions->make(do_MQTTPARAMSCHANGED); + request->send(200); + } + if (request->hasArg(F("mqttPrefix2"))) { + jsonWriteStr(configSetupJson, F("mqttPrefix2"), request->getParam(F("mqttPrefix2"))->value()); + saveConfig(); + myNotAsyncActions->make(do_MQTTPARAMSCHANGED); + request->send(200); + } + if (request->hasArg(F("mqttUser2"))) { + jsonWriteStr(configSetupJson, F("mqttUser2"), request->getParam(F("mqttUser2"))->value()); + saveConfig(); + myNotAsyncActions->make(do_MQTTPARAMSCHANGED); + request->send(200); + } + if (request->hasArg(F("mqttPass2"))) { + jsonWriteStr(configSetupJson, F("mqttPass2"), request->getParam(F("mqttPass2"))->value()); saveConfig(); myNotAsyncActions->make(do_MQTTPARAMSCHANGED); request->send(200); From 433d28a785dff0f5efddb9e4ce0187647ad731ae Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 18 Dec 2020 23:20:42 +0100 Subject: [PATCH 55/94] wrong pin error warning added --- data/set.device.json.gz | Bin 2741 -> 2745 bytes data_ungzip/set.device.json | 4 +++ doc/calculator.xlsx | Bin 9890 -> 99679 bytes include/Class/LineParsing.h | 67 ++++++++++++++++++++++-------------- include/ItemsList.h | 1 + src/Init.cpp | 18 ++++++++-- src/ItemsList.cpp | 10 ++++++ 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/data/set.device.json.gz b/data/set.device.json.gz index c0686cbfcac8b920c6914e072b8e89f3fe6d34e6..0d278d22fa6d799ac687e8110d9771cc6f2f90df 100644 GIT binary patch delta 2323 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 delta 2318 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 diff --git a/data_ungzip/set.device.json b/data_ungzip/set.device.json index 175ff26b..ee17e69b 100644 --- a/data_ungzip/set.device.json +++ b/data_ungzip/set.device.json @@ -60,6 +60,10 @@ "type": "text", "title": "{{warning2}}" }, + { + "type": "text", + "title": "{{warning3}}" + }, { "type": "hr" }, diff --git a/doc/calculator.xlsx b/doc/calculator.xlsx index 6fd98cf2035fc8bfc8a7921684392c3e8e604571..edff5c8ebd763645aada1c765887d2595278b42a 100644 GIT binary patch delta 94792 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 F{{vM%znlO7 delta 4456 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 Z{t&pK4&=Id|HJH*0H^B=0q#H6e*sZ+A?W}B diff --git a/include/Class/LineParsing.h b/include/Class/LineParsing.h index 99a807f1..3edad798 100644 --- a/include/Class/LineParsing.h +++ b/include/Class/LineParsing.h @@ -1,12 +1,12 @@ #pragma once #include - +#include "ItemsList.h" #include "Global.h" #include "Utils/JsonUtils.h" class LineParsing { - protected: +protected: String _key; String _file; String _page; @@ -26,29 +26,33 @@ class LineParsing { String _val; String _index; - public: + int pinErrors; + +public: LineParsing() : - _key{""}, - _file{""}, - _page{""}, - _descr{""}, - _order{""}, - _addr{""}, - _reg{""}, - _pin{""}, - _map{""}, - _c{""}, - _inv{""}, - _state{""}, - _db{""}, - _type{""}, - _int{""}, - _cnt{""}, - _val{""}, - _index{""} + _key{ "" }, + _file{ "" }, + _page{ "" }, + _descr{ "" }, + _order{ "" }, + _addr{ "" }, + _reg{ "" }, + _pin{ "" }, + _map{ "" }, + _c{ "" }, + _inv{ "" }, + _state{ "" }, + _db{ "" }, + _type{ "" }, + _int{ "" }, + _cnt{ "" }, + _val{ "" }, + _index{ "" }, - {}; + pinErrors{ 0 } + + {}; void update() { //String order = sCmd.order(); @@ -106,17 +110,19 @@ class LineParsing { } } + if (!isPinExist(_pin.toInt()) || !isDigitStr(_pin)) { + pinErrors++; + _pin = ""; + } + _page.replace("#", " "); - _descr.replace("#", " "); - _descr.replace("%ver%", String(FIRMWARE_VERSION)); _descr.replace("%name%", jsonReadStr(configSetupJson, F("name"))); createWidget(_descr, _page, _order, _file, _key); } - //jsonWriteStr(configOptionJson, _key + "_pin", _pin); String gkey() { return _key; @@ -171,6 +177,15 @@ class LineParsing { } + int getPinErrors() { + return pinErrors; + } + + void clearErrors() { + pinErrors = 0; + } + + void clear() { _key = ""; _file = ""; @@ -205,7 +220,7 @@ class LineParsing { return; } - if(filename.indexOf("chart") != -1) jsonWriteStr(buf, "maxCount", _cnt); + if (filename.indexOf("chart") != -1) jsonWriteStr(buf, "maxCount", _cnt); jsonWriteStr(buf, "page", page); jsonWriteStr(buf, "order", order); diff --git a/include/ItemsList.h b/include/ItemsList.h index 54089580..2961d9e1 100644 --- a/include/ItemsList.h +++ b/include/ItemsList.h @@ -12,4 +12,5 @@ extern void delChoosingItems(); extern void delAllItems(); extern uint8_t getNewElementNumber(String file); extern uint8_t getFreePinAll(); +extern bool isPinExist(unsigned int num); extern uint8_t getFreePinAnalog(); diff --git a/src/Init.cpp b/src/Init.cpp index fc0fa3ae..cf1c4248 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -2,6 +2,7 @@ #include "BufferExecute.h" #include "Cmd.h" #include "Global.h" +#include "Class/LineParsing.h" #include "items/vLogging.h" #include "items/vImpulsOut.h" #include "items/vButtonOut.h" @@ -20,6 +21,7 @@ void loadConfig() { jsonWriteStr(configSetupJson, "warning1", ""); jsonWriteStr(configSetupJson, "warning2", ""); + jsonWriteStr(configSetupJson, "warning3", ""); jsonWriteStr(configSetupJson, "chipID", chipId); jsonWriteInt(configSetupJson, "firmware_version", FIRMWARE_VERSION); @@ -76,13 +78,13 @@ void deviceInit() { inOutput_KeyList = ""; inOutput_EnterCounter = -1; //======clear pwm params======= - #ifdef PwmOutEnable +#ifdef PwmOutEnable if (myPwmOut != nullptr) { myPwmOut->clear(); } pwmOut_KeyList = ""; pwmOut_EnterCounter = -1; - #endif +#endif //=================================== if (myCountDown != nullptr) { myCountDown->clear(); @@ -97,8 +99,20 @@ void deviceInit() { #else removeFile(String("layout.txt")); #endif + + myLineParsing.clearErrors(); fileCmdExecute(String(DEVICE_CONFIG_FILE)); + + int errors = myLineParsing.getPinErrors(); + + if (errors != 0) { + jsonWriteStr(configSetupJson, F("warning3"), F("

Обнаружен неверный номер пина

")); + } + else { + jsonWriteStr(configSetupJson, F("warning3"), ""); + } + //outcoming_date(); } //-------------------------------сценарии----------------------------------------------------- diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 361b0538..aa83a65d 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -143,6 +143,16 @@ uint8_t getFreePinAll() { } } +bool isPinExist(unsigned int num) { + bool ret = false; + unsigned int pins[] = { 0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 16 }; + uint8_t array_sz = sizeof(pins) / sizeof(pins[0]); + for (uint8_t i = 0; i < array_sz; i++) { + if (pins[i] == num) ret = true; + } + return ret; +} + uint8_t getFreePinAnalog() { #ifdef ESP8266 return 0; From 17162a2d855f554f79e9c65e735f92ef41c4a7ee Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 18 Dec 2020 23:52:35 +0100 Subject: [PATCH 56/94] memory remain --- data/set.device.json.gz | Bin 2745 -> 2780 bytes data_ungzip/set.device.json | 8 ++++++++ data_ungzip/set.device.json.gz | Bin 0 -> 2780 bytes include/FileSystem.h | 20 ++++---------------- src/FileSystem.cpp | 22 ++++++++++++++++++++++ src/main.cpp | 4 ++-- 6 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 data_ungzip/set.device.json.gz create mode 100644 src/FileSystem.cpp diff --git a/data/set.device.json.gz b/data/set.device.json.gz index 0d278d22fa6d799ac687e8110d9771cc6f2f90df..cf2a10771ba8b58ed225d08cbd5fbbd8344ad147 100644 GIT binary patch delta 2739 zcmV;k3QYC672FjDABzYGzA%vn9~~?Z0;F~7hLWX-A_PbjQ4R+ zgVw55Qz>hclihc;g;5O6@nlsiC>6t7(!PIdax9#)p_?a$@!nkVb}72KS1I-rLCGV| zcxQaDcsb9M;Gdi`to#WaeW$1;XVeGA2Wz%ttK`r4Q~tDnj(uhyal~d>Eh|;4`I2t$ z*g84+Gr8@RSdGmAuX%q8dFb{}rO)mlY1Z^_B5_YIYo>Mjr*b%GBUPkcq0vtz&hCEz zF6aCyR`+j8>PzxAc14!Wli~}Y)oQ|e#KI>`j?`OMLb0#OH zWuvkk)%88?j5iE*FeXzsi^-7zh)91p^{WV{MGgWTyczK zc+3}(zUM~QJ8N)ex@#NC*{p6tCej81(3Z8mwp2vEtL!a0Fsq}JO8#~B34Im*E%?(C zM3E2Z!(Y&gA)M6Kxwc|TBXE;c0*6^+_4j zLdMA$d%zYTM0`Q2kW46U)gWqo0*k-lUtu)}C&-uXYKD_e1*B6Wl9pEZQxaXw29XT3 z|F|-oWjbJ)?qmss>Og23Sl58*HCRHHE8u&c0yG9^|0eqY%L`(WHH1k1%$UUHP*xC% zp#MxXK44762Y8sDg>CUKvUz`c{2b2t7b1u}9*8{Nh0=N4Y9Mn7b1Y1OOkklGYj5}o{!4;lx$Hcii9}?K zfn+iQ-LzaPt3)^5Nf$#VY|MMMAUwYwndW#)*72yarcz`0l}>-t*EnFaX;&(pfZ2W9 z4-f? z#kDQ{*GAOejzl<>5Td3Mp`u2ln3Q!v65RM1Ip-V^zSfmJKunC~6b(N}l%wZ(p2pA> za%i#oyJVs&$ zBx$FkM9EY-NEIpY^gFB$n!U$|MZrPj0xqx;fJ|k{d00FLlGHFOfRdsxnc<0#B>Kqf z#fsv1V^R@Lb`U+$kWN9m!R1flQK_QZ5hNa$4Xa2JLmYp3h+4y^E=r18kU;9+Bs?sQ z*gL}MJlae<{e495nMVo8niis@wNyr~aKti@UEAnjt%SzY+IoPA=HJqmT2vZRyS z z2^^I*(S(0cXmZ|nw5`o3LZn3dahIQY1VIF^fZk}5!Ce$x{7V#gnq*x8WP#;J6Du9G zD8OZb+a!j%KDney#q4|m#Z z+KxO0cZm$=i-un~XsU;a!0d{B>bef#J*RKDpTS>SY7 zx`-tE>Yz>WFCeLQ7M$xy%GmG*`6AjrtPu^%y&-f!?RG;+jeDJ+D9)k=Fd)=Kn61%) zXAM!pkSpv2p#XvnoQfjWTG7G0=*{0P#+d!#g0dL-^A#u?Zgf~Kfj%#Kc+v4=f790O z$5Ma6$cIvS_td}`ez*@T`I44Dk+aV9#jNC&jXn$f5bn1ubj}~`&0>m2YP&IfV-*AK z2WPs|y6Tm($wXq=#K0Fc!cJV}l!jG-8=hc)``en~Z=%=>ikY8Uu@19y+oO99{`{*$ zusE)qQrx_w+g`TnYNmS3(N0!0*IVJ0ba#JjIYFgr#+H(0xp3x6h%H|^0n-QI)rVE_ zRun2GQCqRH3OFe^jFlX%uzq=bBit5KwNrXtTP{t2>Jn^CV&%l-ug82;2q9ezK`DS? zlVSWOn;CuvzE0IoNo4iwmzNc%sGG89*7vpml#^Qq+<))n9#EWVnxob`Xp zbB2;X0dsahB4;_O=47R$eMWLELszAxTesk6LgGuZqo{ht%}Qze49vmfMaQa`s*Duj z!KK?W_?eJw2)|$^K#KEaPp|+%YwS3=qN0)8XAner#(PV9a?!uqWy4smjCJG?I-kK^ zY)ADtTtc4g3iYkhy#WM(9o`}iqw0Sc@?nw+r&@0!Uzghe?d46OH7LD} z2;JYpn7X8e;GkMyjda9u_~jPbSmO!Z+a&QFCO5B{gSmGXZQV6sJuCnq`SphnQwSe-63y$x|#0y&1==jADe>phIdv3IQ^n>68dWfDS z&$HcZJYLkjQY8mXU^%|SIUdKAF<-Koa;-~c!`me`hWRowY2gcR%SuW1t z%x**Tg?MBO`Wa1~z*>Jl16khC3LXu3<{wXdQ~%SNoT4dgCS=8cpI2oeCU}}NF&Lnd zuBw_jqH^K>%--@-Nbb*Fzijho`h>XQ#!tzr=LR_Vu`Sajo`(+yxE?+FZ`lJKU;Xzc t@nobfCoG=BvBf@wEyN@U+V8Ro_$jp3vB!LPbn&9euRFQf(WnD0002!uOgI1l delta 2703 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 J|FgO*004CpGmiiO diff --git a/data_ungzip/set.device.json b/data_ungzip/set.device.json index ee17e69b..a6d32d35 100644 --- a/data_ungzip/set.device.json +++ b/data_ungzip/set.device.json @@ -49,6 +49,14 @@ "type": "h4", "title": "{{signal}}" }, + { + "type": "h4", + "title": "Осталось памяти: {{freeBytes}}" + }, + { + "type": "h4", + "title": "Осталось памяти: {{freePer}} %" + }, { "type": "hr" }, diff --git a/data_ungzip/set.device.json.gz b/data_ungzip/set.device.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..cf2a10771ba8b58ed225d08cbd5fbbd8344ad147 GIT binary patch literal 2780 zcmV<23M2I&iwFq5Fx_7S0CQz@E@WkPX=7zBYIARH0Nq+gkJCUDz9aD;ED!>ub?Sza zrHCQ~NEA^H2oQ2&Cowp-u^oz{mDpXX=;eSwbb&Z<2+J;nkcGc6{zts`W}J{}hYkeD zc*cI^n^(uFE?y+TpCx(AEa*jdNme?{XJ|Igr(>?>RqWVt*D{xk^;x!TPq)m$>xO$7SoW|97jITgOlSC5VbLK%t+BB6AK?3QJ#Pyk8fq$IAB2_V>#-_uO5Q+}abi=%he z){tB&6Fk>m(mjLLs#Q}dYm<}RceI6349)RmRVyeJ!&}n6YjP}{v!RZ9AZgb0Zz6F|FKecC`loU@Xd_jm zUZK%XCC=^uF6aCyR`+j8>PzxAc14!Wli~}Y)oQ|e#KI>`j z?`OMLb0#OHWuvkk)%88?j5iE*FeXzsi^-7zh)6i~s|cq@COk5Bs$@7MltD}=j{&n^V zeHH#K_|p&0!Z;26;<8g zY321v8Pr0?$ryXU79d1?L8_2UC~nmtYJ38Vzu{kDH3%ojm+oqYlTHPsQzMd=R`^pA zUCaiN47C5aGMr^PV43b@354oEXc}18fax_@LY6Dwd!7O`250{!`vA)eVv#k3NdC;2 z#O6>|5Q?DxOf)`VOvMLyn4g7h@h`G@di)&D`4=LHJRXQV-i6Y6+-e|m33Dt=flOnP ze~BXZ0wMzZC@@XK|2J`}1tL7Yg2bGI=RN_#mq5&UP=89~Ba(a$qZzBCqP?YqKNCUr zjBKASqwJX&dyNQ#B-5w^?p_lZ%^*+;=vRM^lk6D8= zn1v5e167ICd9IXy)xRLI55(}20P15nht;x2daUlp0EWC8;q=0(t&zPmB=4FwITWo$ z5ch<=g^PEvLL1uPsb-At&OD+dM(^PX;tSi~Qq{j2F2!0|QS&Ip+8DbBa7+Urb=YgP zSo8wUp5*%2X;?kC;5XOd0@om%dolr#+7wt4o&1px8V9;IP0ye{lIkYchcI~gTlB!pkcE{s~Fn3f497UUt;{gn6!NhjbLyH2HO&u2$G~)I;JA& zxOMx1WHJKXv|K8yL^s_@7egj&%zL&VJii{9=6Fli@u;$nVsxupnye1hBr?%8vh5PhHC;64cyaObwLu`_!&9p91*_Ol|4XAjO7#!KS-3L=XjpR z&=usfCdNw8Y8u9ku8=LQ__pR~=g-S*E<%<|rR2Qnk)+_} kkqC66?dduMiiabVQ z1|(^xqeRJ6I!F~M@bo*Z4w}8kheg3b&1%Vcw2vZRyS z z2^^I*(S%TFa^82et<5Mxq(u91m!EkAK?JXW-e{7+T@+pXOB8sTWL*Jdf#pXND;=~b zz-58kB!;>^xujPl%#1%DsBzJO+zA?nfF${G&m_V4iB`V}$Ry(YHx3wub4K(JciL>) zjywhAR`C9ehujXA?rOMTG`lhi%T=P6xYNzB5%gdFOaOdPk8^$d=r~lq=^$C)bXdBG zB>U>1P4O=vsdg5e>qyGj@CNxJ+CHoi4a>bDbU^KPLrIN$ou4Sqq6RP^)I^xA(Sc_T zQNoZb>;$0zf(@LCBGy{b!My0r-z~w5cST2D+FM4><@ne6}*6qhq z!N`YFdH2-77=E}9Ecud_KasP}^u?^?m5n|N{1EQ9EOgEv?ag9}M{2t@?FVPN z)4J-FvdKhZ*~GvXG{R0?<&=h1fg7G+fBV~-;cueY3yPVaTConZbK9eP4*vYBL$Elm zoKoDpquXA#>T0HX%+XF(G}l|Jn^CV&%l-ug82;2q9ezK`DS?lVSWO zn;CuvzE0IoNo4iwmzNc%sGG89*7vpml#^Qq+<))n9#EWVnxob}6dhLS%4 zb9O)?XF00oWTm8iMsh7fSEZ#}x8P?&;!ColsCvcCN@@EH%)#SD$EujBj1=L)rQ0(2 znUHJ^QliqLJHY5JY*#drNzA(ZAYd!&t72b>t8_pTS*hNA);d zLZ0jj^{vvq0R(^@-Xad8>KO82k_x96gWw+QPb{|U8Hxm4Ji``}q z;qn!Gu`Zt55R+g`c^L(2ANe-0&7y4I5Q+O^N0uKT&zDxQS;IoipJ_EHy^RRn-@=%> zq=n$1T40TI#BuoL7TQ?j3EkTy@g3v3xDB;BnhSh^nQa)+a7(EnZuhLC=&FxMI!I&# zn=+V@<5HN!9za)2d?(QsoDH;rshzXre^<}m!oj}{P{zUaZ_Hvsw<`T!UO zydCI;d=L)Zj?q1FWaM%c$hE_Od4{l%r+p@PKD3X$`bHv(%c3ml0~0-WVMzpE1qA*^ zUkwH9F$QT=m4~H{LqLEB|IWgPy6aZi+dkV3FXr-wrkHRX`Rl<6sYIXRsVjfW*_(}P zL)75aby+bhit)#_fbxe6j_=6C3tHCb_{9)^IXKIEZnS&!gWv>uh@K_Sv)ybwUevu( zB?nDlIljX>uuC!FcX-ZT$2zX%y>DjXr}Y#1ug7C#lP{$Q#?|s!F3#Z0ZbS2hcw`Iu z8BLwQT0a9>-p~pj4S41sPkdAV)0&*3DQqTW#eknzWg#YbnlmvNppvetnmM9!;r`6t z@>59e&t1Q4^Jn^mxZ%c6$*SiDIQX$G( extern FS LittleFS; using littlefs_impl::LittleFSConfig; -extern FS *filesystem; +extern FS* filesystem; #define FileFS LittleFS #define FS_NAME "LittleFS" #else -extern FS *filesystem; +extern FS* filesystem; #define FileFS SPIFFS #define FS_NAME "SPIFFS" #endif -/* -* Информация о ФС - size_t totalBytes; // всего - size_t usedBytes; // использовано - size_t maxOpenFiles; // лимит на открые файлы - size_t maxPathLength; // лимит на полное пути + имя файла - FSInfo buf; - getInfo(buf); - size_t freeBytes = buf.totalBytes - buf.usedBytes; - float freePer = buf.usedBytes / buf.totalBytes * 100; -*/ -bool getInfo(FSInfo& info) { - return FileFS.info(info); -} \ No newline at end of file +extern void getFSInfo(); +extern bool getInfo(FSInfo& info); \ No newline at end of file diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp new file mode 100644 index 00000000..9a658f70 --- /dev/null +++ b/src/FileSystem.cpp @@ -0,0 +1,22 @@ +#include "FileSystem.h" +#include "Global.h" + +void getFSInfo() { + // Информация о ФС + size_t totalBytes; // всего + size_t usedBytes; // использовано + size_t maxOpenFiles; // лимит на открые файлы + size_t maxPathLength; // лимит на полное пути + имя файла + + FSInfo buf; + getInfo(buf); + size_t freeBytes = buf.totalBytes - buf.usedBytes; + float freePer = buf.usedBytes / buf.totalBytes * 100; + + jsonWriteInt(configSetupJson, F("freeBytes"), freeBytes); + jsonWriteFloat(configSetupJson, F("freePer"), freePer); +} + +bool getInfo(FSInfo& info) { + return FileFS.info(info); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 78e16b92..3bd8fb0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ #include "items/vSensorUltrasonic.h" #include "Telegram.h" #include "SoftUART.h" +#include "FileSystem.h" void not_async_actions(); @@ -66,9 +67,8 @@ void setup() { #ifdef SSDP_ENABLED SsdpInit(); #endif - + getFSInfo(); //esp_log_level_set("esp_littlefs", ESP_LOG_NONE); - just_load = false; initialized = true; } From fe029a624c0d66fb43a876cd01f2c2883f1c26ab Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 00:10:45 +0100 Subject: [PATCH 57/94] some --- doc/calculator.xlsx | Bin 99679 -> 99701 bytes platformio.ini | 1 + src/FileSystem.cpp | 8 ++++---- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/calculator.xlsx b/doc/calculator.xlsx index edff5c8ebd763645aada1c765887d2595278b42a..4009f34f07e9078bfa60fde430ccdbe1744d9f33 100644 GIT binary patch delta 3301 zcmZ9OXHe6L7RB?YNn1c6R0R=`5^2&zigYOoQbLm!%09Y)NLflKA%tEcM1;_hUWGvD zuAzxkkq)6K7&?N4BJkX|^Jd??`|X}N_s;on=Qo!aPxB+5rjF(codB`#LjeT{^bO5? zMF_5h0Ev~qR&Uf@F!QoKv&1h^K_b$&1+}1;WUQXv>1xRa@*2mCo;3+t<16A9aN*67`zGQ~i=a`D1hvNp4P4 zpMwWH-i$a8^3>fasmO{fvBtMTbHJ@%;PVq11}c(19=Ewe)li=wN`3JY^~?PbYI@Ln zU5%~w6>!?hR?R!Q@l(J@&UEwf=9dO zbR<)5_u(*9+)t><=Cw>j0CO?1$4D8IYH`hfSMHcIeOIg#EFb&OTY1NBfL6Gpk(gE)CnZp2`E`p7J=+CR^zXz78NhC2_T2 z!8%jObWNu|^tZ>S9QMO3uaMkZ+CGtz2)Vj|Skq5kn1V0>{C1vBpMt(FJfq)&3IyV~ zik4!$3AaU8++ztNR-7}l?NU;)Jkal8u56x+t)88oNm-{=H>E`UHkLJ0eZD(~MHMDS zS;~|Zw2hDq*HiCZ9;N+jGHoC}!4byV_g37TaBI288+N&WM#jW>LF3u!hP2+X9|>1( zJsaeMzv^N*G2iAgW!Dm^i0QDX$iaJqcdZN|@P{v}B9zKg6`_03o%xEFiMMA(M{I}( zw-id->mDPrwpcZbMN?+<39&ig4^(M)is+9mLhQ_6G%D|~N|b)ze$q*}CCN|7{FhDG zMm{d1@9j_xOS-pA|5-Fb7N;us^JI^2Lh6t9n3o5Z@%qIdBKvEW=MCW-hPK>!0!;_I zU^o&{;@UZ;FbO7f0f*RcK$WBi6X}q_+Rj*?Y2rG(}OGpxSj z9ifaW^rMCmFsC$i#ujVEz9t%Rod=8-xn<{n9$}>f3ph>$=Hi(%uASJg(J1119=_t* zrrq6L3tvj?TfNEHn{orj-YC$XcRlhMpO&t^lEyK5m5}wQP&(_TV~MJ7jB9$09^8b( zM;mNAToTlr|CV11YBCY$L!)XxNgE&i2GSVgKFvN+6Xm8P(SZ*^;88~Gn>cF4r!5gd zNbNtuU}FKWE8IR`VlSNiqTLxaq;xTrX$Hy<5J+rW`I~Qvw7S--FJes&se~G&t+>oXUQ}0 z819WSlg_(CWCNa9=xO<8)HK#yU{7{;NRvn4>Ps=3*v(J<@8R*G*av{` zCUau%RQK=Bv{Gd{a0l5=8hHWD2z9eF%A$R_Ixa4XpNS3E1agKH|B~~@%uNzDOYq$8 zvlBH)78|om{Z9ij^c6|924N3nLqG~ISlGY5kL4_n*-Q6rK3o`VDSwXbZW^_X2oafU zV)sV*N{<|_QD(@NRHOCb)m|K83)TUpG~uR$tr1Q8pR4k8Xb$nji3Y=^%uKhy`OEH_ zWxOgm+j^32e*EsXDq~Bz3$|Na{NRd5)_+}O7?(m*6>Ce0OAD+IyB(qE?QSLP({V#t zQW##BNy5T9v`JLnZdn0v38&{3cVnZ{ccvxHc7lwp%()DHaM4K>E9wL@g_;)<(5$>t zIE{B!Wf!X`kjK9(qa8F$pdVDs>1EwCHWk0 zdg3IWJsj_96_;GorIJ}1@TBs9-2YkQfpRW8zQRb**WTQPJf6^Mh^^gWN*Z73ENso= z;=mtNk%aS!K6NBwmruo@O{MvEn~-S8(QC=Uk@k;p){`=StIIZLPI6vWuM>WfT`mh}DP6I}m=z$J9Jk~Txy zp3o9)p%FMS;pfGGa?8oXUCf3Q77W@uDQJF76t_JJqI9iWo8^v6^6;7=1fU*!kt9Z% z^;Xl0AF-z$=O1Fer@E^mO;YRb=UM#l4e%JOIoyX^m#}5a2cdH#T@L#TG-PzSJBD z$u55W9D$?EP*G&566uWj;ti2^(=A-M=x}TM%!EuK=c0FWL-cU>%kG~7n-x0isM}Q9 z`Pu@Ij=`~~_+*TTU#`c%rjBNE^3rHUOUD2LR#?C1$U`}4u~euwo=k@>>L4RwTLfOV zKs}#tH@$f(TbSV`3$XV+97Mp7&dPy0*I-9+C!(G!?i=Ezqt@$ZPst!rsfm2GkXSd3 z{k*AY=*~v8UDiG5*Oc2^{_#pK2GXdav!#l$X`I*i@F^$%gC4%@^{MdBE#nK-RQb2i zQFILR3b_8gM%vg){6V(Qsu%V5WzIv0is)$zC6zN9MV&JLj#Y4;ifV?3SutYRsR1?6 zUTmPVYo)K+md3H$rlzThrMWbCr1$94D~Un>-u@c`{u;`Z$azd&{xLOrl#dO=69X_c z6T$!`%6~q73~eM}NYzXCi*#Fb+!#d!z{2y-mjwcGg8sKyFnN&xJO_6a{Eg{1ik3LhKlwXh008!}vx6PbeD# z0ZdCEAo!mUnB!=Go3btlz~F-Zmc%IQg8@u)Fd%|SM*Nym!r&2rz~2{wSw;Y0KpR6w T0IEPIMm`1*ph<`TentN`i#b#^ delta 3308 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 EKeN#@X#fBK diff --git a/platformio.ini b/platformio.ini index f5848391..10f8c1f8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -55,6 +55,7 @@ board_build.filesystem = littlefs [env:esp8266] framework = arduino board = nodemcuv2 +board_build.ldscript = eagle.flash.4m1m.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 9a658f70..2345f088 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -3,10 +3,10 @@ void getFSInfo() { // Информация о ФС - size_t totalBytes; // всего - size_t usedBytes; // использовано - size_t maxOpenFiles; // лимит на открые файлы - size_t maxPathLength; // лимит на полное пути + имя файла + //size_t totalBytes; // всего + //size_t usedBytes; // использовано + //size_t maxOpenFiles; // лимит на открые файлы + //size_t maxPathLength; // лимит на полное пути + имя файла FSInfo buf; getInfo(buf); From 4711af65896051cbf27fa378695abb6c5f5fe23d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 13:31:38 +0100 Subject: [PATCH 58/94] fixed --- data/set.device.json.gz | Bin 2780 -> 2770 bytes data_ungzip/set.device.json | 6 +----- data_ungzip/set.device.json.gz | Bin 2780 -> 2770 bytes doc/calculator.xlsx | Bin 99701 -> 99742 bytes src/FileSystem.cpp | 32 +++++++++++++++++++++++--------- src/Utils/FileUtils.cpp | 2 +- 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/data/set.device.json.gz b/data/set.device.json.gz index cf2a10771ba8b58ed225d08cbd5fbbd8344ad147..ab29128af32f371342a7642eb7b2fb90aee70eca 100644 GIT binary patch literal 2770 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 YZzQW7d(4MN7B4#c4R`m9P%kY20QtaSfdBvi literal 2780 zcmV<23M2I&iwFq5Fx_7S0CQz@E@WkPX=7zBYIARH0Nq+gkJCUDz9aD;ED!>ub?Sza zrHCQ~NEA^H2oQ2&Cowp-u^oz{mDpXX=;eSwbb&Z<2+J;nkcGc6{zts`W}J{}hYkeD zc*cI^n^(uFE?y+TpCx(AEa*jdNme?{XJ|Igr(>?>RqWVt*D{xk^;x!TPq)m$>xO$7SoW|97jITgOlSC5VbLK%t+BB6AK?3QJ#Pyk8fq$IAB2_V>#-_uO5Q+}abi=%he z){tB&6Fk>m(mjLLs#Q}dYm<}RceI6349)RmRVyeJ!&}n6YjP}{v!RZ9AZgb0Zz6F|FKecC`loU@Xd_jm zUZK%XCC=^uF6aCyR`+j8>PzxAc14!Wli~}Y)oQ|e#KI>`j z?`OMLb0#OHWuvkk)%88?j5iE*FeXzsi^-7zh)6i~s|cq@COk5Bs$@7MltD}=j{&n^V zeHH#K_|p&0!Z;26;<8g zY321v8Pr0?$ryXU79d1?L8_2UC~nmtYJ38Vzu{kDH3%ojm+oqYlTHPsQzMd=R`^pA zUCaiN47C5aGMr^PV43b@354oEXc}18fax_@LY6Dwd!7O`250{!`vA)eVv#k3NdC;2 z#O6>|5Q?DxOf)`VOvMLyn4g7h@h`G@di)&D`4=LHJRXQV-i6Y6+-e|m33Dt=flOnP ze~BXZ0wMzZC@@XK|2J`}1tL7Yg2bGI=RN_#mq5&UP=89~Ba(a$qZzBCqP?YqKNCUr zjBKASqwJX&dyNQ#B-5w^?p_lZ%^*+;=vRM^lk6D8= zn1v5e167ICd9IXy)xRLI55(}20P15nht;x2daUlp0EWC8;q=0(t&zPmB=4FwITWo$ z5ch<=g^PEvLL1uPsb-At&OD+dM(^PX;tSi~Qq{j2F2!0|QS&Ip+8DbBa7+Urb=YgP zSo8wUp5*%2X;?kC;5XOd0@om%dolr#+7wt4o&1px8V9;IP0ye{lIkYchcI~gTlB!pkcE{s~Fn3f497UUt;{gn6!NhjbLyH2HO&u2$G~)I;JA& zxOMx1WHJKXv|K8yL^s_@7egj&%zL&VJii{9=6Fli@u;$nVsxupnye1hBr?%8vh5PhHC;64cyaObwLu`_!&9p91*_Ol|4XAjO7#!KS-3L=XjpR z&=usfCdNw8Y8u9ku8=LQ__pR~=g-S*E<%<|rR2Qnk)+_} kkqC66?dduMiiabVQ z1|(^xqeRJ6I!F~M@bo*Z4w}8kheg3b&1%Vcw2vZRyS z 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 YZzQW7d(4MN7B4#c4R`m9P%kY20QtaSfdBvi literal 2780 zcmV<23M2I&iwFq5Fx_7S0CQz@E@WkPX=7zBYIARH0Nq+gkJCUDz9aD;ED!>ub?Sza zrHCQ~NEA^H2oQ2&Cowp-u^oz{mDpXX=;eSwbb&Z<2+J;nkcGc6{zts`W}J{}hYkeD zc*cI^n^(uFE?y+TpCx(AEa*jdNme?{XJ|Igr(>?>RqWVt*D{xk^;x!TPq)m$>xO$7SoW|97jITgOlSC5VbLK%t+BB6AK?3QJ#Pyk8fq$IAB2_V>#-_uO5Q+}abi=%he z){tB&6Fk>m(mjLLs#Q}dYm<}RceI6349)RmRVyeJ!&}n6YjP}{v!RZ9AZgb0Zz6F|FKecC`loU@Xd_jm zUZK%XCC=^uF6aCyR`+j8>PzxAc14!Wli~}Y)oQ|e#KI>`j z?`OMLb0#OHWuvkk)%88?j5iE*FeXzsi^-7zh)6i~s|cq@COk5Bs$@7MltD}=j{&n^V zeHH#K_|p&0!Z;26;<8g zY321v8Pr0?$ryXU79d1?L8_2UC~nmtYJ38Vzu{kDH3%ojm+oqYlTHPsQzMd=R`^pA zUCaiN47C5aGMr^PV43b@354oEXc}18fax_@LY6Dwd!7O`250{!`vA)eVv#k3NdC;2 z#O6>|5Q?DxOf)`VOvMLyn4g7h@h`G@di)&D`4=LHJRXQV-i6Y6+-e|m33Dt=flOnP ze~BXZ0wMzZC@@XK|2J`}1tL7Yg2bGI=RN_#mq5&UP=89~Ba(a$qZzBCqP?YqKNCUr zjBKASqwJX&dyNQ#B-5w^?p_lZ%^*+;=vRM^lk6D8= zn1v5e167ICd9IXy)xRLI55(}20P15nht;x2daUlp0EWC8;q=0(t&zPmB=4FwITWo$ z5ch<=g^PEvLL1uPsb-At&OD+dM(^PX;tSi~Qq{j2F2!0|QS&Ip+8DbBa7+Urb=YgP zSo8wUp5*%2X;?kC;5XOd0@om%dolr#+7wt4o&1px8V9;IP0ye{lIkYchcI~gTlB!pkcE{s~Fn3f497UUt;{gn6!NhjbLyH2HO&u2$G~)I;JA& zxOMx1WHJKXv|K8yL^s_@7egj&%zL&VJii{9=6Fli@u;$nVsxupnye1hBr?%8vh5PhHC;64cyaObwLu`_!&9p91*_Ol|4XAjO7#!KS-3L=XjpR z&=usfCdNw8Y8u9ku8=LQ__pR~=g-S*E<%<|rR2Qnk)+_} kkqC66?dduMiiabVQ z1|(^xqeRJ6I!F~M@bo*Z4w}8kheg3b&1%Vcw2vZRyS z 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 QmI(&YfDoiQ;tT}(8x2zpn*aa+ delta 2613 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 OMgsiQacJOg)PDf_f8gr? diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 2345f088..c4559685 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -3,18 +3,32 @@ void getFSInfo() { // Информация о ФС - //size_t totalBytes; // всего - //size_t usedBytes; // использовано - //size_t maxOpenFiles; // лимит на открые файлы - //size_t maxPathLength; // лимит на полное пути + имя файла + FSInfo buf; - getInfo(buf); - size_t freeBytes = buf.totalBytes - buf.usedBytes; - float freePer = buf.usedBytes / buf.totalBytes * 100; + if (getInfo(buf)) { + SerialPrint("I", F("FS"), F("Get FS info completed")); - jsonWriteInt(configSetupJson, F("freeBytes"), freeBytes); - jsonWriteFloat(configSetupJson, F("freePer"), freePer); + size_t totalBytes = buf.totalBytes; // всего + size_t usedBytes = buf.usedBytes; // использовано + size_t maxOpenFiles = buf.maxOpenFiles; // лимит на открые файлы + size_t maxPathLength = buf.maxPathLength; // лимит на пути и имена файлов + + size_t freeBytes = totalBytes - usedBytes; + float freePer = freeBytes * 100 / totalBytes; + + jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + String(freeBytes) + ")"); + + SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); + SerialPrint("I", F("FS"), "usedBytes=" + String(usedBytes)); + SerialPrint("I", F("FS"), "maxOpenFiles=" + String(maxOpenFiles)); + SerialPrint("I", F("FS"), "maxPathLength=" + String(maxPathLength)); + SerialPrint("I", F("FS"), "freeBytes=" + String(freeBytes)); + SerialPrint("I", F("FS"), "freePer=" + String(freePer)); + } + else { + SerialPrint("E", F("FS"), F("FS info error")); + } } bool getInfo(FSInfo& info) { diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp index 70955088..31927eed 100644 --- a/src/Utils/FileUtils.cpp +++ b/src/Utils/FileUtils.cpp @@ -13,7 +13,7 @@ bool fileSystemInit() { SerialPrint("E", F("FS"), F("FS Init ERROR, may be FS was not flashed")); return false; } - SerialPrint("I", F("FS"), F("FS Init")); + SerialPrint("I", F("FS"), F("FS Init completed")); return true; } From cab8aff5622d542b8bddc3f50674e8f4453fcf42 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 13:39:04 +0100 Subject: [PATCH 59/94] fixed var type --- src/FileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index c4559685..d33c8857 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -15,7 +15,7 @@ void getFSInfo() { size_t maxPathLength = buf.maxPathLength; // лимит на пути и имена файлов size_t freeBytes = totalBytes - usedBytes; - float freePer = freeBytes * 100 / totalBytes; + float freePer = ((float) freeBytes / totalBytes) * 100; jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + String(freeBytes) + ")"); From 5280a8914f02d4f72857a888f487bc34eac7b815 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 13:51:53 +0100 Subject: [PATCH 60/94] some --- src/FileSystem.cpp | 7 ++----- src/Utils/WiFiUtils.cpp | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index d33c8857..9504cd2e 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -1,10 +1,7 @@ #include "FileSystem.h" #include "Global.h" - +// Информация о ФС void getFSInfo() { - // Информация о ФС - - FSInfo buf; if (getInfo(buf)) { SerialPrint("I", F("FS"), F("Get FS info completed")); @@ -17,7 +14,7 @@ void getFSInfo() { size_t freeBytes = totalBytes - usedBytes; float freePer = ((float) freeBytes / totalBytes) * 100; - jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + String(freeBytes) + ")"); + jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + prettyBytes(freeBytes) + ")"); SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); SerialPrint("I", F("FS"), "usedBytes=" + String(usedBytes)); diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index a8326df2..8a948ee7 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -1,4 +1,5 @@ #include "Utils/WiFiUtils.h" +#include "FileSystem.h" void routerConnect() { @@ -145,6 +146,7 @@ void wifiSignalInit() { ts.add( SYGNAL, 1000 * 60, [&](void*) { SerialPrint("I", "System", printMemoryStatus()); + getFSInfo(); switch (RSSIquality()) { case 0: jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); From 305b3f32d63912b504d52f1ecf614128c67f5ff3 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 14:06:58 +0100 Subject: [PATCH 61/94] =?UTF-8?q?1=D0=BC=D0=B1=20=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B0=20=D1=81=20=D0=BF=D1=80=D0=B5=D1=81=D0=B5=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=20=D0=B8=20=D1=81=20edit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Consts.h | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index 66737178..1a37c29b 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,7 +2,7 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 273 -//#define FLASH_SIZE_1MB true +#define FLASH_SIZE_1MB true //===========FileSystem============================================================================================================================================== #define USE_LITTLEFS true //================================================================================================================================================================== diff --git a/platformio.ini b/platformio.ini index 10f8c1f8..9135ebc3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,6 +1,6 @@ [platformio] -default_envs = esp8266 +default_envs = esp8266_01_1m ;============================================================================================================================================= [common_env_data] lib_deps_external = From 017c6f62344e909f77ada2c795e7391d2b7a9837 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 14:18:22 +0100 Subject: [PATCH 62/94] =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=20=D0=B1=D0=BB=D0=BE=D0=BA?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/FileSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 9504cd2e..e07fb8c0 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -9,6 +9,8 @@ void getFSInfo() { size_t totalBytes = buf.totalBytes; // всего size_t usedBytes = buf.usedBytes; // использовано size_t maxOpenFiles = buf.maxOpenFiles; // лимит на открые файлы + size_t blockSize = buf.blockSize; + size_t pageSize = buf.pageSize; size_t maxPathLength = buf.maxPathLength; // лимит на пути и имена файлов size_t freeBytes = totalBytes - usedBytes; @@ -19,6 +21,8 @@ void getFSInfo() { SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); SerialPrint("I", F("FS"), "usedBytes=" + String(usedBytes)); SerialPrint("I", F("FS"), "maxOpenFiles=" + String(maxOpenFiles)); + SerialPrint("I", F("FS"), "blockSize=" + String(blockSize)); + SerialPrint("I", F("FS"), "pageSize=" + String(pageSize)); SerialPrint("I", F("FS"), "maxPathLength=" + String(maxPathLength)); SerialPrint("I", F("FS"), "freeBytes=" + String(freeBytes)); SerialPrint("I", F("FS"), "freePer=" + String(freePer)); From 6573b2c145ca1584170211dfcc36949755810859 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 19 Dec 2020 15:27:45 +0100 Subject: [PATCH 63/94] =?UTF-8?q?=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D0=BB=20li?= =?UTF-8?q?ttle=20fs=20=D0=B4=D0=BB=D1=8F=20esp32=20=D0=B8=D0=B7=20lib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 49 - lib/LITTLEFS/LICENSE | 339 -- lib/LITTLEFS/README.md | 94 - .../examples/LITTLEFS_PlatformIO/.gitignore | 4 - .../examples/LITTLEFS_PlatformIO/README.md | 68 - .../LITTLEFS_PlatformIO/data/file1.txt | 1 - .../data/testfolder/test2.txt | 1 - .../include/.placeholder.txt | 0 .../LITTLEFS_PlatformIO/lib/.placeholder.txt | 0 .../LITTLEFS_PlatformIO/littlefsbuilder.py | 2 - .../LITTLEFS_PlatformIO/partitions_custom.csv | 6 - .../LITTLEFS_PlatformIO/platformio.ini | 35 - .../examples/LITTLEFS_PlatformIO/src/main.cpp | 293 - .../examples/LITTLEFS_time/LITTLEFS_time.ino | 219 - .../examples/LittleFS_test/LittleFS_test.ino | 272 - lib/LITTLEFS/library.json | 22 - lib/LITTLEFS/library.properties | 9 - lib/LITTLEFS/src/LITTLEFS.cpp | 106 - lib/LITTLEFS/src/LITTLEFS.h | 38 - lib/LITTLEFS/src/esp_littlefs.c | 1513 ----- lib/LITTLEFS/src/esp_littlefs.h | 119 - lib/LITTLEFS/src/lfs.c | 4913 ----------------- lib/LITTLEFS/src/lfs.h | 655 --- lib/LITTLEFS/src/lfs_util.c | 33 - lib/LITTLEFS/src/lfs_util.h | 234 - lib/LITTLEFS/src/littlefs_api.c | 63 - lib/LITTLEFS/src/littlefs_api.h | 106 - platformio.ini | 2 + 28 files changed, 2 insertions(+), 9194 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 lib/LITTLEFS/LICENSE delete mode 100644 lib/LITTLEFS/README.md delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp delete mode 100644 lib/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino delete mode 100644 lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino delete mode 100644 lib/LITTLEFS/library.json delete mode 100644 lib/LITTLEFS/library.properties delete mode 100644 lib/LITTLEFS/src/LITTLEFS.cpp delete mode 100644 lib/LITTLEFS/src/LITTLEFS.h delete mode 100644 lib/LITTLEFS/src/esp_littlefs.c delete mode 100644 lib/LITTLEFS/src/esp_littlefs.h delete mode 100644 lib/LITTLEFS/src/lfs.c delete mode 100644 lib/LITTLEFS/src/lfs.h delete mode 100644 lib/LITTLEFS/src/lfs_util.c delete mode 100644 lib/LITTLEFS/src/lfs_util.h delete mode 100644 lib/LITTLEFS/src/littlefs_api.c delete mode 100644 lib/LITTLEFS/src/littlefs_api.h diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b1ad7d05..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "files.associations": { - "*.tcc": "cpp", - "string": "cpp", - "functional": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "cstdarg": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "list": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "numeric": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "system_error": "cpp", - "cinttypes": "cpp", - "regex": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "typeinfo": "cpp" - }, - "cmake.configureOnOpen": true -} \ No newline at end of file diff --git a/lib/LITTLEFS/LICENSE b/lib/LITTLEFS/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/lib/LITTLEFS/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/lib/LITTLEFS/README.md b/lib/LITTLEFS/README.md deleted file mode 100644 index 55309ba7..00000000 --- a/lib/LITTLEFS/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# LittleFS_esp32 - -## LittleFS library for arduino-esp32 - -- A LittleFS wrapper for Arduino ESP32 of [ARMmbed LittleFS](https://github.com/ARMmbed/littlefs) -- Based on [ESP-IDF port of joltwallet/esp_littlefs](https://github.com/joltwallet/esp_littlefs) , thank you Brian! -- As a reference, see [LillteFS library for ESP8266 core](https://github.com/esp8266/Arduino/tree/master/libraries/LittleFS) -- [PR at esp32 core development](https://github.com/espressif/arduino-esp32/pull/4096) -- [PR at esp-idf development](https://github.com/espressif/esp-idf/pull/5469) -- The functionality is similar to SPIFFS - -### Installation - -- Use **Arduino Library Manager** -- Or download / use **git** to have latest repository of **LITTLEFS** added to Arduino IDE **/libraries** folder -(File > Preferences > Sketchbook location). -- See ``` #define CONFIG_LITTLEFS_FOR_IDF_3_2 ``` in **esp_littlefs.c**. -When defined, code builds with any IDF 3.2 - 4.x. -On IDF newer than 3.2, it **can** be commented to enable file timestamp feature. -See LITTLEFS_time example. -- The other ``` #define CONFIG_LITTLEFS_xxxxx ``` are set to optimal default values. -Read [here](https://github.com/joltwallet/esp_littlefs/blob/master/Kconfig) and [here](https://github.com/ARMmbed/littlefs/blob/master/README.md) if you want to modify. - -### Usage - -- Use LITTLEFS same way as SPIFFS -- A quick startup based on your existing code you can re-define SPIFFS -``` -#define USE_LittleFS - -#include -#ifdef USE_LittleFS - #define SPIFFS LITTLEFS - #include -#else - #include -#endif - ``` - - Note, this may not work if your sketch uses other libraries that use SPIFFS themselves. - -### Differences with SPIFFS - -- LittleFS has folders, you need to iterate files in folders. See **To Do** below. -- At root a "/folder" = "folder" -- Requires a label for mount point, NULL will not work -- maxOpenFiles parameter is unused, kept for compatibility -- LITTLEFS.mkdir(path) and LITTLEFS.rmdir(path) work as expected for folders -- Speed comparison based on **LittleFS_test.ino** sketch (for a file 1048576 bytes): - -|Filesystem|Read time [ms]|Write time [ms]| -|----|----|----| -|FAT|276|14493| -|LITTLEFS|446*|16387| -|SPIFFS|767|65622| - -*The read speed improved by changing ```#define CONFIG_LITTLEFS_CACHE_SIZE``` from 128 to 512 - - -### Arduino ESP32 LittleFS filesystem upload tool - -- Use (replace if exists) [arduino-esp32fs-plugin](https://github.com/me-no-dev/arduino-esp32fs-plugin/pull/23 ) with [this variant](https://github.com/lorol/arduino-esp32fs-plugin), which supports SPIFFS, LittleFS and FatFS -- Requires [mklittlefs executable](https://github.com/earlephilhower/mklittlefs) which is available [in releases section here](https://github.com/lorol/arduino-esp32fs-plugin ) or download the zipped binary [here](https://github.com/earlephilhower/mklittlefs/releases) or from **esp-quick-toolchain** releases [here](https://github.com/earlephilhower/esp-quick-toolchain/releases) -- Copy it to **/tools** folder of esp32 platform where **espota** and **esptool** (.py or.exe) tools are located -- Restart Arduino IDE. - -### PlatformIO - -- See [LITTLEFS_PlatformIO example here](https://github.com/lorol/LITTLEFS/tree/master/examples/LITTLEFS_PlatformIO) - ( based on notes below from [BlueAndi](https://github.com/BlueAndi) ) -- Add to _platformio.ini_: - `extra_scripts = replace_fs.py` - -- Add _replace_fs.py_ to project root directory (where platformio.ini is located): - - ```python - Import("env") - print("Replace MKSPIFFSTOOL with mklittlefs.exe") - env.Replace (MKSPIFFSTOOL = "mklittlefs.exe") - ``` - -- Add _mklittlefs.exe_ to project root directory as well. - -## Credits and license - -- This work is based on [ARMmbed LittleFS](https://github.com/ARMmbed/littlefs) , [ESP-IDF port of joltwallet/esp_littlefs](https://github.com/joltwallet/esp_littlefs) , [Espressif Arduino core for the ESP32, the ESP-IDF - SPIFFS Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/SPIFFS) -- Licensed under GPL v2 ([text](LICENSE)) - -## To Do - - [x] Submit to be added to Arduino Library Manager - - [ ] Decide on more compatibility (or not) with SPIFFS' lack of folders, similar to esp8266' way: - - [recursive folders auto creation](https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.cpp#L60) when a new file is created at non-existing path - - [recursive folders auto deletion](https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.h#L149) on "last file" deletion (SPIFFS cannot have "folder w/o file") - - review other differences: opendir(), rmdir(), unlink() - - [ ] Follow-up / eventually retire this library if LittleFS gets implemented through IDF / esp32 Arduino core. diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore deleted file mode 100644 index 87515a62..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.pio -.vscode -mklittlefs.exe -mklittlefs \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md deleted file mode 100644 index 773445b6..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# How to run on PlatformIO IDE - -- Download and extract to this project root a **mklittlefs** executable for your OS [from a zipped binary here](https://github.com/earlephilhower/mklittlefs/releases) -- Open **LITTLEFS_PlatformIO** folder -- Run PlatformIO project task: **Upload Filesystem Image** -- Run PlatformIO project task: **Upload and Monitor** -- You will see a Serial output like: -``` ---- Miniterm on COM5 115200,8,N,1 --- ---- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- -ets Jun 8 2016 00:22:57 - -rst:0x1 (POWERON_RESET),boot:0x13 (Snfigsip: 0, SPIWP:0xee -clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 -mode:DIO, clock div:2 -load:0x3fff0018,len:4 -load:0x3fff001c,len:1044 -load:0x40078000,len:10044 -load:0x40080400,len:5872 -entry 0x400806ac -Listing directory: / - FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 - DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 -Creating Dir: /mydir -Dir created -Writing file: /mydir/hello2.txt -- file written -Listing directory: / - FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 - DIR : /mydir LAST WRITE: 1970-01-01 00:00:00 -Listing directory: /mydir - FILE: /mydir/hello2.txt SIZE: 6 LAST WRITE: 1970-01-01 00:00:00 - DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 -Listing directory: /testfolder - FILE: /testfolder/test2.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 -Deleting file: /mydir/hello2.txt -- file deleted -Removing Dir: /mydir -Dir removed -Listing directory: / - FILE: /file1.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 - DIR : /testfolder LAST WRITE: 2020-10-06 15:10:33 -Listing directory: /testfolder - FILE: /testfolder/test2.txt SIZE: 3 LAST WRITE: 2020-10-06 15:10:33 -Writing file: /hello.txt -- file written -Appending to file: /hello.txt -- message appended -Reading file: /hello.txt -- read from file: -Hello World! -Renaming file /hello.txt to /foo.txt -- file renamed -Reading file: /foo.txt -- read from file: -Hello World! -Deleting file: /foo.txt -- file deleted -Testing file I/O with /test.txt -- writing................................................................ - - 1048576 bytes written in 12006 ms -- reading................................................................ -- 1048576 bytes read in 547 ms -Deleting file: /test.txt -- file deleted -Test complete -``` -- If you have a module with more than 4MB flash, you can uncomment **partitions_custom.csv** in **platformio.ini** and modify the csv file accordingly \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt deleted file mode 100644 index 7c4a013e..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/file1.txt +++ /dev/null @@ -1 +0,0 @@ -aaa \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt deleted file mode 100644 index 01f02e32..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/data/testfolder/test2.txt +++ /dev/null @@ -1 +0,0 @@ -bbb \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/include/.placeholder.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/lib/.placeholder.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py deleted file mode 100644 index 93937e29..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/littlefsbuilder.py +++ /dev/null @@ -1,2 +0,0 @@ -Import("env") -env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/mklittlefs' ) \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv deleted file mode 100644 index 97846fa5..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/partitions_custom.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -ota_0, app, ota_0, 0x10000, 0x1A0000, -ota_1, app, ota_1, , 0x1A0000, -otadata, data, ota, 0x350000, 0x2000, -nvs, data, nvs, , 0x6000, -data, data, spiffs, , 0xA8000, diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini deleted file mode 100644 index 43e34ec0..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/platformio.ini +++ /dev/null @@ -1,35 +0,0 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - -[platformio] -default_envs = esp32 - -[env] -framework = arduino - -[env:esp32] -platform = espressif32 -;platform = https://github.com/platformio/platform-espressif32.git -;board_build.mcu = esp32 -platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git - -build_flags = - ${env.build_flags} - -D=${PIOENV} - ;-D CONFIG_LITTLEFS_FOR_IDF_3_2 - -lib_deps = https://github.com/lorol/LITTLEFS.git - -board = esp32dev -;board_build.partitions = partitions_custom.csv -monitor_filters = esp32_exception_decoder -monitor_speed = 115200 - -extra_scripts = ./littlefsbuilder.py diff --git a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp b/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp deleted file mode 100644 index 5ff2765b..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_PlatformIO/src/main.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include -#include "FS.h" -#include - -#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2 - #include -#endif - -/* You only need to format LITTLEFS the first time you run a - test or else use the LITTLEFS plugin to create a partition - https://github.com/lorol/arduino-esp32littlefs-plugin */ - -#define FORMAT_LITTLEFS_IF_FAILED true - -void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ - Serial.printf("Listing directory: %s\r\n", dirname); - - File root = fs.open(dirname); - if(!root){ - Serial.println("- failed to open directory"); - return; - } - if(!root.isDirectory()){ - Serial.println(" - not a directory"); - return; - } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - Serial.print(" DIR : "); - -#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2 - Serial.println(file.name()); -#else - Serial.print(file.name()); - time_t t= file.getLastWrite(); - struct tm * tmstruct = localtime(&t); - Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); -#endif - - if(levels){ - listDir(fs, file.name(), levels -1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print(" SIZE: "); - -#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2 - Serial.println(file.size()); -#else - Serial.print(file.size()); - time_t t= file.getLastWrite(); - struct tm * tmstruct = localtime(&t); - Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); -#endif - } - file = root.openNextFile(); - } -} - -void createDir(fs::FS &fs, const char * path){ - Serial.printf("Creating Dir: %s\n", path); - if(fs.mkdir(path)){ - Serial.println("Dir created"); - } else { - Serial.println("mkdir failed"); - } -} - -void removeDir(fs::FS &fs, const char * path){ - Serial.printf("Removing Dir: %s\n", path); - if(fs.rmdir(path)){ - Serial.println("Dir removed"); - } else { - Serial.println("rmdir failed"); - } -} - -void readFile(fs::FS &fs, const char * path){ - Serial.printf("Reading file: %s\r\n", path); - - File file = fs.open(path); - if(!file || file.isDirectory()){ - Serial.println("- failed to open file for reading"); - return; - } - - Serial.println("- read from file:"); - while(file.available()){ - Serial.write(file.read()); - } - file.close(); -} - -void writeFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Writing file: %s\r\n", path); - - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("- file written"); - } else { - Serial.println("- write failed"); - } - file.close(); -} - -void appendFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Appending to file: %s\r\n", path); - - File file = fs.open(path, FILE_APPEND); - if(!file){ - Serial.println("- failed to open file for appending"); - return; - } - if(file.print(message)){ - Serial.println("- message appended"); - } else { - Serial.println("- append failed"); - } - file.close(); -} - -void renameFile(fs::FS &fs, const char * path1, const char * path2){ - Serial.printf("Renaming file %s to %s\r\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("- file renamed"); - } else { - Serial.println("- rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char * path){ - Serial.printf("Deleting file: %s\r\n", path); - if(fs.remove(path)){ - Serial.println("- file deleted"); - } else { - Serial.println("- delete failed"); - } -} - -// SPIFFS-like write and delete file - -// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.cpp#L60 -void writeFile2(fs::FS &fs, const char * path, const char * message){ - if(!fs.exists(path)){ - if (strchr(path, '/')) { - Serial.printf("Create missing folders of: %s\r\n", path); - char *pathStr = strdup(path); - if (pathStr) { - char *ptr = strchr(pathStr, '/'); - while (ptr) { - *ptr = 0; - fs.mkdir(pathStr); - *ptr = '/'; - ptr = strchr(ptr+1, '/'); - } - } - free(pathStr); - } - } - - Serial.printf("Writing file to: %s\r\n", path); - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("- file written"); - } else { - Serial.println("- write failed"); - } - file.close(); -} - -// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.h#L149 -void deleteFile2(fs::FS &fs, const char * path){ - Serial.printf("Deleting file and empty folders on path: %s\r\n", path); - - if(fs.remove(path)){ - Serial.println("- file deleted"); - } else { - Serial.println("- delete failed"); - } - - char *pathStr = strdup(path); - if (pathStr) { - char *ptr = strrchr(pathStr, '/'); - if (ptr) { - Serial.printf("Removing all empty folders on path: %s\r\n", path); - } - while (ptr) { - *ptr = 0; - fs.rmdir(pathStr); - ptr = strrchr(pathStr, '/'); - } - free(pathStr); - } -} - -void testFileIO(fs::FS &fs, const char * path){ - Serial.printf("Testing file I/O with %s\r\n", path); - - static uint8_t buf[512]; - size_t len = 0; - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - - size_t i; - Serial.print("- writing" ); - uint32_t start = millis(); - for(i=0; i<2048; i++){ - if ((i & 0x001F) == 0x001F){ - Serial.print("."); - } - file.write(buf, 512); - } - Serial.println(""); - uint32_t end = millis() - start; - Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end); - file.close(); - - file = fs.open(path); - start = millis(); - end = start; - i = 0; - if(file && !file.isDirectory()){ - len = file.size(); - size_t flen = len; - start = millis(); - Serial.print("- reading" ); - while(len){ - size_t toRead = len; - if(toRead > 512){ - toRead = 512; - } - file.read(buf, toRead); - if ((i++ & 0x001F) == 0x001F){ - Serial.print("."); - } - len -= toRead; - } - Serial.println(""); - end = millis() - start; - Serial.printf("- %u bytes read in %u ms\r\n", flen, end); - file.close(); - } else { - Serial.println("- failed to open file for reading"); - } -} - -void setup(){ - Serial.begin(115200); - if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ - Serial.println("LITTLEFS Mount Failed"); - return; - } - - listDir(LITTLEFS, "/", 0); - createDir(LITTLEFS, "/mydir"); - writeFile(LITTLEFS, "/mydir/hello2.txt", "Hello2"); - //writeFile(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt", "Hello3"); - writeFile2(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt", "Hello3"); - listDir(LITTLEFS, "/", 3); - deleteFile(LITTLEFS, "/mydir/hello2.txt"); - //deleteFile(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt"); - deleteFile2(LITTLEFS, "/mydir/newdir2/newdir3/hello3.txt"); - removeDir(LITTLEFS, "/mydir"); - listDir(LITTLEFS, "/", 3); - writeFile(LITTLEFS, "/hello.txt", "Hello "); - appendFile(LITTLEFS, "/hello.txt", "World!\r\n"); - readFile(LITTLEFS, "/hello.txt"); - renameFile(LITTLEFS, "/hello.txt", "/foo.txt"); - readFile(LITTLEFS, "/foo.txt"); - deleteFile(LITTLEFS, "/foo.txt"); - testFileIO(LITTLEFS, "/test.txt"); - deleteFile(LITTLEFS, "/test.txt"); - - Serial.println( "Test complete" ); -} - -void loop(){ - -} diff --git a/lib/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino b/lib/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino deleted file mode 100644 index a88af28b..00000000 --- a/lib/LITTLEFS/examples/LITTLEFS_time/LITTLEFS_time.ino +++ /dev/null @@ -1,219 +0,0 @@ -#include "FS.h" -//#include "SPIFFS.h" -#include "LITTLEFS.h" -#include -#include - -#define SPIFFS LITTLEFS - -/* This examples uses "quick re-define" of SPIFFS to run - an existing sketch with LITTLEFS instead of SPIFFS - - To get time/date stamps by file.getLastWrite(), you need an - esp32 core on IDF 3.3 and comment a line in file esp_littlefs.c: - - //#define CONFIG_LITTLEFS_FOR_IDF_3_2 - - You only need to format LITTLEFS the first time you run a - test or else use the LITTLEFS plugin to create a partition - https://github.com/lorol/arduino-esp32littlefs-plugin */ - -#define FORMAT_LITTLEFS_IF_FAILED true - -const char* ssid = "yourssid"; -const char* password = "yourpass"; - -long timezone = 1; -byte daysavetime = 1; - -void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ - Serial.printf("Listing directory: %s\n", dirname); - - File root = fs.open(dirname); - if(!root){ - Serial.println("Failed to open directory"); - return; - } - if(!root.isDirectory()){ - Serial.println("Not a directory"); - return; - } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - Serial.print(" DIR : "); - Serial.print (file.name()); - time_t t= file.getLastWrite(); - struct tm * tmstruct = localtime(&t); - Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); - if(levels){ - listDir(fs, file.name(), levels -1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print(" SIZE: "); - Serial.print(file.size()); - time_t t= file.getLastWrite(); - struct tm * tmstruct = localtime(&t); - Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); - } - file = root.openNextFile(); - } -} - -void createDir(fs::FS &fs, const char * path){ - Serial.printf("Creating Dir: %s\n", path); - if(fs.mkdir(path)){ - Serial.println("Dir created"); - } else { - Serial.println("mkdir failed"); - } -} - -void removeDir(fs::FS &fs, const char * path){ - Serial.printf("Removing Dir: %s\n", path); - if(fs.rmdir(path)){ - Serial.println("Dir removed"); - } else { - Serial.println("rmdir failed"); - } -} - -void readFile(fs::FS &fs, const char * path){ - Serial.printf("Reading file: %s\n", path); - - File file = fs.open(path); - if(!file){ - Serial.println("Failed to open file for reading"); - return; - } - - Serial.print("Read from file: "); - while(file.available()){ - Serial.write(file.read()); - } - file.close(); -} - -void writeFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Writing file: %s\n", path); - - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("Failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("File written"); - } else { - Serial.println("Write failed"); - } - file.close(); -} - -void appendFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Appending to file: %s\n", path); - - File file = fs.open(path, FILE_APPEND); - if(!file){ - Serial.println("Failed to open file for appending"); - return; - } - if(file.print(message)){ - Serial.println("Message appended"); - } else { - Serial.println("Append failed"); - } - file.close(); -} - -void renameFile(fs::FS &fs, const char * path1, const char * path2){ - Serial.printf("Renaming file %s to %s\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("File renamed"); - } else { - Serial.println("Rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char * path){ - Serial.printf("Deleting file: %s\n", path); - if(fs.remove(path)){ - Serial.println("File deleted"); - } else { - Serial.println("Delete failed"); - } -} - -void setup(){ - Serial.begin(115200); - // We start by connecting to a WiFi network - Serial.println(); - Serial.println(); - Serial.print("Connecting to "); - Serial.println(ssid); - - WiFi.begin(ssid, password); - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println("Contacting Time Server"); - configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); - struct tm tmstruct ; - delay(2000); - tmstruct.tm_year = 0; - getLocalTime(&tmstruct, 5000); - Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct.tm_year)+1900,( tmstruct.tm_mon)+1, tmstruct.tm_mday,tmstruct.tm_hour , tmstruct.tm_min, tmstruct.tm_sec); - Serial.println(""); - - if(!SPIFFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ - Serial.println("LITTLEFS Mount Failed"); - return; - } - - Serial.println("----list 1----"); - listDir(SPIFFS, "/", 1); - - Serial.println("----remove old dir----"); - removeDir(SPIFFS, "/mydir"); - - Serial.println("----create a new dir----"); - createDir(SPIFFS, "/mydir"); - - Serial.println("----remove the new dir----"); - removeDir(SPIFFS, "/mydir"); - - Serial.println("----create the new again----"); - createDir(SPIFFS, "/mydir"); - - Serial.println("----create and work with file----"); - writeFile(SPIFFS, "/mydir/hello.txt", "Hello "); - appendFile(SPIFFS, "/mydir/hello.txt", "World!\n"); - - Serial.println("----list 2----"); - listDir(SPIFFS, "/", 1); - - Serial.println("----attempt to remove dir w/ file----"); - removeDir(SPIFFS, "/mydir"); - - Serial.println("----remove dir after deleting file----"); - deleteFile(SPIFFS, "/mydir/hello.txt"); - removeDir(SPIFFS, "/mydir"); - - Serial.println("----list 3----"); - listDir(SPIFFS, "/", 1); - - Serial.println( "Test complete" ); - -} - -void loop(){ - -} diff --git a/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino b/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino deleted file mode 100644 index 528bfd18..00000000 --- a/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include "FS.h" -#include - -/* You only need to format LITTLEFS the first time you run a - test or else use the LITTLEFS plugin to create a partition - https://github.com/lorol/arduino-esp32littlefs-plugin */ - -#define FORMAT_LITTLEFS_IF_FAILED true - -void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ - Serial.printf("Listing directory: %s\r\n", dirname); - - File root = fs.open(dirname); - if(!root){ - Serial.println("- failed to open directory"); - return; - } - if(!root.isDirectory()){ - Serial.println(" - not a directory"); - return; - } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - Serial.print(" DIR : "); - Serial.println(file.name()); - if(levels){ - listDir(fs, file.name(), levels -1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print("\tSIZE: "); - Serial.println(file.size()); - } - file = root.openNextFile(); - } -} - -void createDir(fs::FS &fs, const char * path){ - Serial.printf("Creating Dir: %s\n", path); - if(fs.mkdir(path)){ - Serial.println("Dir created"); - } else { - Serial.println("mkdir failed"); - } -} - -void removeDir(fs::FS &fs, const char * path){ - Serial.printf("Removing Dir: %s\n", path); - if(fs.rmdir(path)){ - Serial.println("Dir removed"); - } else { - Serial.println("rmdir failed"); - } -} - -void readFile(fs::FS &fs, const char * path){ - Serial.printf("Reading file: %s\r\n", path); - - File file = fs.open(path); - if(!file || file.isDirectory()){ - Serial.println("- failed to open file for reading"); - return; - } - - Serial.println("- read from file:"); - while(file.available()){ - Serial.write(file.read()); - } - file.close(); -} - -void writeFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Writing file: %s\r\n", path); - - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("- file written"); - } else { - Serial.println("- write failed"); - } - file.close(); -} - -void appendFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Appending to file: %s\r\n", path); - - File file = fs.open(path, FILE_APPEND); - if(!file){ - Serial.println("- failed to open file for appending"); - return; - } - if(file.print(message)){ - Serial.println("- message appended"); - } else { - Serial.println("- append failed"); - } - file.close(); -} - -void renameFile(fs::FS &fs, const char * path1, const char * path2){ - Serial.printf("Renaming file %s to %s\r\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("- file renamed"); - } else { - Serial.println("- rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char * path){ - Serial.printf("Deleting file: %s\r\n", path); - if(fs.remove(path)){ - Serial.println("- file deleted"); - } else { - Serial.println("- delete failed"); - } -} - -// SPIFFS-like write and delete file - -// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.cpp#L60 -void writeFile2(fs::FS &fs, const char * path, const char * message){ - if(!fs.exists(path)){ - if (strchr(path, '/')) { - Serial.printf("Create missing folders of: %s\r\n", path); - char *pathStr = strdup(path); - if (pathStr) { - char *ptr = strchr(pathStr, '/'); - while (ptr) { - *ptr = 0; - fs.mkdir(pathStr); - *ptr = '/'; - ptr = strchr(ptr+1, '/'); - } - } - free(pathStr); - } - } - - Serial.printf("Writing file to: %s\r\n", path); - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("- file written"); - } else { - Serial.println("- write failed"); - } - file.close(); -} - -// See: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/src/LittleFS.h#L149 -void deleteFile2(fs::FS &fs, const char * path){ - Serial.printf("Deleting file and empty folders on path: %s\r\n", path); - - if(fs.remove(path)){ - Serial.println("- file deleted"); - } else { - Serial.println("- delete failed"); - } - - char *pathStr = strdup(path); - if (pathStr) { - char *ptr = strrchr(pathStr, '/'); - if (ptr) { - Serial.printf("Removing all empty folders on path: %s\r\n", path); - } - while (ptr) { - *ptr = 0; - fs.rmdir(pathStr); - ptr = strrchr(pathStr, '/'); - } - free(pathStr); - } -} - -void testFileIO(fs::FS &fs, const char * path){ - Serial.printf("Testing file I/O with %s\r\n", path); - - static uint8_t buf[512]; - size_t len = 0; - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - - size_t i; - Serial.print("- writing" ); - uint32_t start = millis(); - for(i=0; i<2048; i++){ - if ((i & 0x001F) == 0x001F){ - Serial.print("."); - } - file.write(buf, 512); - } - Serial.println(""); - uint32_t end = millis() - start; - Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end); - file.close(); - - file = fs.open(path); - start = millis(); - end = start; - i = 0; - if(file && !file.isDirectory()){ - len = file.size(); - size_t flen = len; - start = millis(); - Serial.print("- reading" ); - while(len){ - size_t toRead = len; - if(toRead > 512){ - toRead = 512; - } - file.read(buf, toRead); - if ((i++ & 0x001F) == 0x001F){ - Serial.print("."); - } - len -= toRead; - } - Serial.println(""); - end = millis() - start; - Serial.printf("- %u bytes read in %u ms\r\n", flen, end); - file.close(); - } else { - Serial.println("- failed to open file for reading"); - } -} - -void setup(){ - Serial.begin(115200); - if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ - Serial.println("LITTLEFS Mount Failed"); - return; - } - Serial.println( "SPIFFS-like write file to new path and delete it w/folders" ); - writeFile2(LITTLEFS, "/new1/new2/new3/hello3.txt", "Hello3"); - listDir(LITTLEFS, "/", 3); - deleteFile2(LITTLEFS, "/new1/new2/new3/hello3.txt"); - - listDir(LITTLEFS, "/", 3); - createDir(LITTLEFS, "/mydir"); - writeFile(LITTLEFS, "/mydir/hello2.txt", "Hello2"); - listDir(LITTLEFS, "/", 1); - deleteFile(LITTLEFS, "/mydir/hello2.txt"); - removeDir(LITTLEFS, "/mydir"); - listDir(LITTLEFS, "/", 1); - writeFile(LITTLEFS, "/hello.txt", "Hello "); - appendFile(LITTLEFS, "/hello.txt", "World!\r\n"); - readFile(LITTLEFS, "/hello.txt"); - renameFile(LITTLEFS, "/hello.txt", "/foo.txt"); - readFile(LITTLEFS, "/foo.txt"); - deleteFile(LITTLEFS, "/foo.txt"); - testFileIO(LITTLEFS, "/test.txt"); - deleteFile(LITTLEFS, "/test.txt"); - - Serial.println( "Test complete" ); -} - -void loop(){ - -} diff --git a/lib/LITTLEFS/library.json b/lib/LITTLEFS/library.json deleted file mode 100644 index adb1e5b1..00000000 --- a/lib/LITTLEFS/library.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name":"LittleFS_esp32", - "description":"LittleFS for esp32", - "keywords":"littlefs, spiffs", - "authors": - { - "name": "lorol", - "maintainer": true - }, - "repository": - { - "type": "git", - "url": "https://github.com/lorol/LITTLEFS.git" - }, - "version": "1.0", - "license": "LGPL-2.0", - "frameworks": "arduino", - "platforms": "espressif32", - "build": { - "libCompatMode": 2 - } -} \ No newline at end of file diff --git a/lib/LITTLEFS/library.properties b/lib/LITTLEFS/library.properties deleted file mode 100644 index 179f6df0..00000000 --- a/lib/LITTLEFS/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=LittleFS_esp32 -version=1.0 -author=lorol -maintainer=lorol -sentence=LittleFS for esp32 -paragraph=LittleFS for esp32 -category=Data Storage -url=https://github.com/lorol/LITTLEFS -architectures=esp32 \ No newline at end of file diff --git a/lib/LITTLEFS/src/LITTLEFS.cpp b/lib/LITTLEFS/src/LITTLEFS.cpp deleted file mode 100644 index 32dce386..00000000 --- a/lib/LITTLEFS/src/LITTLEFS.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -static constexpr const char LFS_NAME[] = "spiffs"; - -#include "vfs_api.h" - -extern "C" { -#include -#include -#include -#include "esp_littlefs.h" -} - -#include "LITTLEFS.h" - -using namespace fs; - -LITTLEFSFS::LITTLEFSFS() : FS(FSImplPtr(new VFSImpl())) -{ - -} - -bool LITTLEFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles) -{ - if(esp_littlefs_mounted(LFS_NAME)){ - log_w("LITTLEFS Already Mounted!"); - return true; - } - - esp_vfs_littlefs_conf_t conf = { - .base_path = basePath, - .partition_label = LFS_NAME, - //.max_files = maxOpenFiles, - .format_if_mount_failed = false - }; - - esp_err_t err = esp_vfs_littlefs_register(&conf); - if(err == ESP_FAIL && formatOnFail){ - if(format()){ - err = esp_vfs_littlefs_register(&conf); - } - } - if(err != ESP_OK){ - log_e("Mounting LITTLEFS failed! Error: %d", err); - return false; - } - _impl->mountpoint(basePath); - return true; -} - -void LITTLEFSFS::end() -{ - if(esp_littlefs_mounted(LFS_NAME)){ - esp_err_t err = esp_vfs_littlefs_unregister(LFS_NAME); - if(err){ - log_e("Unmounting LITTLEFS failed! Error: %d", err); - return; - } - _impl->mountpoint(NULL); - } -} - -bool LITTLEFSFS::format() -{ - disableCore0WDT(); - esp_err_t err = esp_littlefs_format(LFS_NAME); - enableCore0WDT(); - if(err){ - log_e("Formatting LITTLEFS failed! Error: %d", err); - return false; - } - return true; -} - -size_t LITTLEFSFS::totalBytes() -{ - size_t total,used; - if(esp_littlefs_info(LFS_NAME, &total, &used)){ - return 0; - } - return total; -} - -size_t LITTLEFSFS::usedBytes() -{ - size_t total,used; - if(esp_littlefs_info(LFS_NAME, &total, &used)){ - return 0; - } - return used; -} - -LITTLEFSFS LITTLEFS; - diff --git a/lib/LITTLEFS/src/LITTLEFS.h b/lib/LITTLEFS/src/LITTLEFS.h deleted file mode 100644 index fbd6f09e..00000000 --- a/lib/LITTLEFS/src/LITTLEFS.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _LITTLEFS_H_ -#define _LITTLEFS_H_ - -#include "FS.h" - -namespace fs -{ - -class LITTLEFSFS : public FS -{ -public: - LITTLEFSFS(); - bool begin(bool formatOnFail=false, const char * basePath="/littlefs", uint8_t maxOpenFiles=5); - bool format(); - size_t totalBytes(); - size_t usedBytes(); - void end(); -}; - -} - -extern fs::LITTLEFSFS LITTLEFS; - - -#endif diff --git a/lib/LITTLEFS/src/esp_littlefs.c b/lib/LITTLEFS/src/esp_littlefs.c deleted file mode 100644 index 63a114a0..00000000 --- a/lib/LITTLEFS/src/esp_littlefs.c +++ /dev/null @@ -1,1513 +0,0 @@ -/** - * @file esp_littlefs.c - * @brief Maps LittleFS <-> ESP_VFS - * @author Brian Pugh - * - * @note Modified and used by lorol for Arduino esp32 core - * - * Copyright 2020 Brian Pugh - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -//#define LOG_LOCAL_LEVEL 4 -#define CONFIG_LITTLEFS_FOR_IDF_3_2 /* For old IDF - like in release 1.0.4 */ - -#include "esp_log.h" -#include "esp_spi_flash.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include -#include -#include -#include -#include -#include - -#if __has_include("esp32/rom/spi_flash.h") -#include "esp32/rom/spi_flash.h" -#else -#include "rom/spi_flash.h" -#endif - -#include "esp_system.h" - -#include "esp_littlefs.h" -#include "littlefs_api.h" - -static const char TAG[] = "esp_littlefs"; - -#define CONFIG_LITTLEFS_BLOCK_SIZE 4096 /* ESP32 can only operate at 4kb */ - -/* File Descriptor Caching Params */ -#define CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR 2 /* Amount to resize FD cache by */ -#define CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE 4 /* Minimum size of FD cache */ -#define CONFIG_LITTLEFS_FD_CACHE_HYST 4 /* When shrinking, leave this many trailing FD slots available */ - -#define CONFIG_LITTLEFS_MAX_PARTITIONS 3 -#define CONFIG_LITTLEFS_PAGE_SIZE 256 -#define CONFIG_LITTLEFS_OBJ_NAME_LEN 64 -#define CONFIG_LITTLEFS_READ_SIZE 128 -#define CONFIG_LITTLEFS_WRITE_SIZE 128 -#define CONFIG_LITTLEFS_LOOKAHEAD_SIZE 128 -#define CONFIG_LITTLEFS_CACHE_SIZE 512 -#define CONFIG_LITTLEFS_BLOCK_CYCLES 512 - -//#define CONFIG_SECURE_FLASH_ENC_ENABLED 1 /* For encrypted, in part.csv: flash_test, data, spiffs, , 512K, encrypted */ - -#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2 -#define CONFIG_LITTLEFS_USE_MTIME 0 -#else -#define CONFIG_LITTLEFS_USE_MTIME 1 -#define CONFIG_LITTLEFS_MTIME_USE_SECONDS 1 -#endif - - -/** - * @brief littlefs DIR structure - */ -typedef struct { - DIR dir; /*!< VFS DIR struct */ - lfs_dir_t d; /*!< littlefs DIR struct */ - struct dirent e; /*!< Last open dirent */ - long offset; /*!< Offset of the current dirent */ - char *path; /*!< Requested directory name */ -} vfs_littlefs_dir_t; - -static int vfs_littlefs_open(void* ctx, const char * path, int flags, int mode); -static ssize_t vfs_littlefs_write(void* ctx, int fd, const void * data, size_t size); -static ssize_t vfs_littlefs_read(void* ctx, int fd, void * dst, size_t size); -static int vfs_littlefs_close(void* ctx, int fd); -static off_t vfs_littlefs_lseek(void* ctx, int fd, off_t offset, int mode); -static int vfs_littlefs_stat(void* ctx, const char * path, struct stat * st); -static int vfs_littlefs_unlink(void* ctx, const char *path); -static int vfs_littlefs_rename(void* ctx, const char *src, const char *dst); -static DIR* vfs_littlefs_opendir(void* ctx, const char* name); -static int vfs_littlefs_closedir(void* ctx, DIR* pdir); -static struct dirent* vfs_littlefs_readdir(void* ctx, DIR* pdir); -static int vfs_littlefs_readdir_r(void* ctx, DIR* pdir, - struct dirent* entry, struct dirent** out_dirent); -static long vfs_littlefs_telldir(void* ctx, DIR* pdir); -static void vfs_littlefs_seekdir(void* ctx, DIR* pdir, long offset); -static int vfs_littlefs_mkdir(void* ctx, const char* name, mode_t mode); -static int vfs_littlefs_rmdir(void* ctx, const char* name); -static int vfs_littlefs_fsync(void* ctx, int fd); - -static esp_err_t esp_littlefs_init(const esp_vfs_littlefs_conf_t* conf); -static esp_err_t esp_littlefs_erase_partition(const char *partition_label); -static esp_err_t esp_littlefs_by_label(const char* label, int * index); -static esp_err_t esp_littlefs_get_empty(int *index); -static void esp_littlefs_free(esp_littlefs_t ** efs); -static void esp_littlefs_dir_free(vfs_littlefs_dir_t *dir); -static int esp_littlefs_flags_conv(int m); -#if CONFIG_LITTLEFS_USE_MTIME -static int vfs_littlefs_utime(void *ctx, const char *path, const struct utimbuf *times); -static void vfs_littlefs_update_mtime(esp_littlefs_t *efs, const char *path); -static int vfs_littlefs_update_mtime_value(esp_littlefs_t *efs, const char *path, time_t t); -static time_t vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path); -#endif - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH -/* The only way in LittleFS to get info is via a path (lfs_stat), so it cannot - * be done if the path isn't stored. */ -static int vfs_littlefs_fstat(void* ctx, int fd, struct stat * st); -#endif - -static int sem_take(esp_littlefs_t *efs); -static int sem_give(esp_littlefs_t *efs); - -static SemaphoreHandle_t _efs_lock = NULL; -static esp_littlefs_t * _efs[CONFIG_LITTLEFS_MAX_PARTITIONS] = { 0 }; - -/******************** - * Helper Functions * - ********************/ -void esp_littlefs_free_fds(esp_littlefs_t * efs) { - /* Need to free all files that were opened */ - while (efs->file) { - vfs_littlefs_file_t * next = efs->file->next; - free(efs->file); - efs->file = next; - } - free(efs->cache); - efs->cache = 0; - efs->cache_size = efs->fd_count = 0; -} - - -/******************** - * Public Functions * - ********************/ - -bool esp_littlefs_mounted(const char* partition_label) { - int index; - esp_err_t err; - - err = esp_littlefs_by_label(partition_label, &index); - if(err != ESP_OK) return false; - return _efs[index]->cache_size > 0; -} - -esp_err_t esp_littlefs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes){ - int index; - esp_err_t err; - esp_littlefs_t *efs = NULL; - - err = esp_littlefs_by_label(partition_label, &index); - if(err != ESP_OK) return false; - efs = _efs[index]; - - if(total_bytes) *total_bytes = efs->cfg.block_size * efs->cfg.block_count; - if(used_bytes) *used_bytes = efs->cfg.block_size * lfs_fs_size(efs->fs); - - return ESP_OK; -} - -esp_err_t esp_vfs_littlefs_register(const esp_vfs_littlefs_conf_t * conf) -{ - assert(conf->base_path); - const esp_vfs_t vfs = { - .flags = ESP_VFS_FLAG_CONTEXT_PTR, - .write_p = &vfs_littlefs_write, - .lseek_p = &vfs_littlefs_lseek, - .read_p = &vfs_littlefs_read, - .open_p = &vfs_littlefs_open, - .close_p = &vfs_littlefs_close, -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - .fstat_p = &vfs_littlefs_fstat, -#else - .fstat_p = NULL, /* Not supported */ -#endif - .stat_p = &vfs_littlefs_stat, - .link_p = NULL, /* Not Supported */ - .unlink_p = &vfs_littlefs_unlink, - .rename_p = &vfs_littlefs_rename, - .opendir_p = &vfs_littlefs_opendir, - .closedir_p = &vfs_littlefs_closedir, - .readdir_p = &vfs_littlefs_readdir, - .readdir_r_p = &vfs_littlefs_readdir_r, - .seekdir_p = &vfs_littlefs_seekdir, - .telldir_p = &vfs_littlefs_telldir, - .mkdir_p = &vfs_littlefs_mkdir, - .rmdir_p = &vfs_littlefs_rmdir, - .fsync_p = &vfs_littlefs_fsync, -#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2 -#if CONFIG_LITTLEFS_USE_MTIME - .utime_p = &vfs_littlefs_utime, -#else - .utime_p = NULL, -#endif // CONFIG_LITTLEFS_USE_MTIME -#endif //CONFIG_LITTLEFS_FOR_IDF_3_2 - }; - - esp_err_t err = esp_littlefs_init(conf); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to initialize LittleFS"); - return err; - } - - int index; - if (esp_littlefs_by_label(conf->partition_label, &index) != ESP_OK) { - ESP_LOGE(TAG, "Unable to find partition \"%s\"", conf->partition_label); - return ESP_ERR_NOT_FOUND; - } - - strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1); - err = esp_vfs_register(conf->base_path, &vfs, _efs[index]); - if (err != ESP_OK) { - esp_littlefs_free(&_efs[index]); - ESP_LOGE(TAG, "Failed to register Littlefs to \"%s\"", conf->base_path); - return err; - } - - ESP_LOGV(TAG, "Successfully registered LittleFS to \"%s\"", conf->base_path); - return ESP_OK; -} - -esp_err_t esp_vfs_littlefs_unregister(const char* partition_label) -{ - assert(partition_label); - int index; - if (esp_littlefs_by_label(partition_label, &index) != ESP_OK) { - ESP_LOGE(TAG, "Partition was never registered."); - return ESP_ERR_INVALID_STATE; - } - ESP_LOGV(TAG, "Unregistering \"%s\"", partition_label); - esp_err_t err = esp_vfs_unregister(_efs[index]->base_path); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to unregister \"%s\"", partition_label); - return err; - } - esp_littlefs_free(&_efs[index]); - _efs[index] = NULL; - return ESP_OK; -} - -esp_err_t esp_littlefs_format(const char* partition_label) { - assert( partition_label ); - - bool was_mounted = false; - bool efs_free = false; - int index = -1; - esp_err_t err; - esp_littlefs_t *efs = NULL; - - ESP_LOGV(TAG, "Formatting \"%s\"", partition_label); - - /* Get a context */ - err = esp_littlefs_by_label(partition_label, &index); - - if( err != ESP_OK ){ - /* Create a tmp context */ - ESP_LOGV(TAG, "Temporarily creating EFS context."); - efs_free = true; - const esp_vfs_littlefs_conf_t conf = { - /* base_name not necessary for initializing */ - .dont_mount = true, - .partition_label = partition_label, - }; - err = esp_littlefs_init(&conf); /* Internally MIGHT call esp_littlefs_format */ - if( err != ESP_OK ) { - ESP_LOGE(TAG, "Failed to initialize to format."); - goto exit; - } - - err = esp_littlefs_by_label(partition_label, &index); - if ( err != ESP_OK) { - ESP_LOGE(TAG, "Error obtaining context."); - goto exit; - } - } - - efs = _efs[index]; - assert( efs ); - - /* Unmount if mounted */ - if(efs->cache_size > 0){ - int res; - ESP_LOGV(TAG, "Partition was mounted. Unmounting..."); - was_mounted = true; - res = lfs_unmount(efs->fs); - if(res != LFS_ERR_OK){ - ESP_LOGE(TAG, "Failed to unmount."); - return ESP_FAIL; - } - esp_littlefs_free_fds(efs); - } - - /* Erase and Format */ - { - int res; - ESP_LOGV(TAG, "Formatting filesystem"); - esp_littlefs_erase_partition(partition_label); - res = lfs_format(efs->fs, &efs->cfg); - if( res != LFS_ERR_OK ) { - ESP_LOGE(TAG, "Failed to format filesystem"); - return ESP_FAIL; - } - } - - /* Mount filesystem */ - if( was_mounted ) { - int res; - /* Remount the partition */ - ESP_LOGV(TAG, "Remounting formatted partition"); - res = lfs_mount(efs->fs, &efs->cfg); - if( res != LFS_ERR_OK ) { - ESP_LOGE(TAG, "Failed to re-mount filesystem"); - return ESP_FAIL; - } - efs->cache_size = CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE; // Initial size of cache; will resize ondemand - efs->cache = calloc(sizeof(*efs->cache), efs->cache_size); - } - ESP_LOGV(TAG, "Format Success!"); - - err = ESP_OK; - -exit: - if(efs_free && index>=0) esp_littlefs_free(&_efs[index]); - return err; -} - -#if CONFIG_LITTLEFS_HUMAN_READABLE -/** - * @brief converts an enumerated lfs error into a string. - * @param lfs_error The littlefs error. - */ -const char * esp_littlefs_errno(enum lfs_error lfs_errno) { - switch(lfs_errno){ - case LFS_ERR_OK: return "LFS_ERR_OK"; - case LFS_ERR_IO: return "LFS_ERR_IO"; - case LFS_ERR_CORRUPT: return "LFS_ERR_CORRUPT"; - case LFS_ERR_NOENT: return "LFS_ERR_NOENT"; - case LFS_ERR_EXIST: return "LFS_ERR_EXIST"; - case LFS_ERR_NOTDIR: return "LFS_ERR_NOTDIR"; - case LFS_ERR_ISDIR: return "LFS_ERR_ISDIR"; - case LFS_ERR_NOTEMPTY: return "LFS_ERR_NOTEMPTY"; - case LFS_ERR_BADF: return "LFS_ERR_BADF"; - case LFS_ERR_FBIG: return "LFS_ERR_FBIG"; - case LFS_ERR_INVAL: return "LFS_ERR_INVAL"; - case LFS_ERR_NOSPC: return "LFS_ERR_NOSPC"; - case LFS_ERR_NOMEM: return "LFS_ERR_NOMEM"; - case LFS_ERR_NOATTR: return "LFS_ERR_NOATTR"; - case LFS_ERR_NAMETOOLONG: return "LFS_ERR_NAMETOOLONG"; - default: return "LFS_ERR_UNDEFINED"; - } - return ""; -} -#else -#define esp_littlefs_errno(x) "" -#endif - -/******************** - * Static Functions * - ********************/ - -/*** Helpers ***/ - -/** - * @brief Free and clear a littlefs definition structure. - * @param efs Pointer to pointer to struct. Done this way so we can also zero - * out the pointer. - */ -static void esp_littlefs_free(esp_littlefs_t ** efs) -{ - esp_littlefs_t * e = *efs; - if (e == NULL) return; - *efs = NULL; - - if (e->fs) { - if(e->cache_size > 0) lfs_unmount(e->fs); - free(e->fs); - } - if(e->lock) vSemaphoreDelete(e->lock); - esp_littlefs_free_fds(e); - free(e); -} - -/** - * @brief Free a vfs_littlefs_dir_t struct. - */ -static void esp_littlefs_dir_free(vfs_littlefs_dir_t *dir){ - if(dir == NULL) return; - if(dir->path) free(dir->path); - free(dir); -} - -/** - * Get a mounted littlefs filesystem by label. - * @param[in] label - * @param[out] index index into _efs - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_by_label(const char* label, int * index){ - int i; - esp_littlefs_t * p; - - if(!label || !index) return ESP_ERR_INVALID_ARG; - - ESP_LOGV(TAG, "Searching for existing filesystem for partition \"%s\"", label); - - for (i = 0; i < CONFIG_LITTLEFS_MAX_PARTITIONS; i++) { - p = _efs[i]; - if (p) { - if (strncmp(label, p->partition->label, 17) == 0) { - *index = i; - ESP_LOGV(TAG, "Found existing filesystem \"%s\" at index %d", label, *index); - return ESP_OK; - } - } - } - - ESP_LOGV(TAG, "Existing filesystem \%s\" not found", label); - return ESP_ERR_NOT_FOUND; -} - -/** - * @brief Get the index of an unallocated LittleFS slot. - * @param[out] index Indexd of free LittleFS slot - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_get_empty(int *index) { - assert(index); - for(uint8_t i=0; i < CONFIG_LITTLEFS_MAX_PARTITIONS; i++){ - if( _efs[i] == NULL ){ - *index = i; - return ESP_OK; - } - } - ESP_LOGE(TAG, "No more free partitions available."); - return ESP_FAIL; -} - -/** - * @brief erase a partition; make sure LittleFS is unmounted first. - * @param partition_label NULL-terminated string of partition to erase - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_erase_partition(const char *partition_label) { - ESP_LOGV(TAG, "Erasing partition..."); - - const esp_partition_t* partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, - partition_label); - if (!partition) { - ESP_LOGE(TAG, "partition \"%s\" could not be found", partition_label); - return ESP_ERR_NOT_FOUND; - } - - if( esp_partition_erase_range(partition, 0, partition->size) != ESP_OK ) { - ESP_LOGE(TAG, "Failed to erase partition"); - return ESP_FAIL; - } - - return ESP_OK; -} - -/** - * @brief Convert fcntl flags to littlefs flags - * @param m fcntl flags - * @return lfs flags - */ -static int esp_littlefs_flags_conv(int m) { - int lfs_flags = 0; - if (m == O_APPEND) {ESP_LOGV(TAG, "O_APPEND"); lfs_flags |= LFS_O_APPEND;} - if (m == O_RDONLY) {ESP_LOGV(TAG, "O_RDONLY"); lfs_flags |= LFS_O_RDONLY;} - if (m & O_WRONLY) {ESP_LOGV(TAG, "O_WRONLY"); lfs_flags |= LFS_O_WRONLY;} - if (m & O_RDWR) {ESP_LOGV(TAG, "O_RDWR"); lfs_flags |= LFS_O_RDWR;} - if (m & O_EXCL) {ESP_LOGV(TAG, "O_EXCL"); lfs_flags |= LFS_O_EXCL;} - if (m & O_CREAT) {ESP_LOGV(TAG, "O_CREAT"); lfs_flags |= LFS_O_CREAT;} - if (m & O_TRUNC) {ESP_LOGV(TAG, "O_TRUNC"); lfs_flags |= LFS_O_TRUNC;} - return lfs_flags; -} - -/** - * @brief Initialize and mount littlefs - * @param[in] conf Filesystem Configuration - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_init(const esp_vfs_littlefs_conf_t* conf) -{ - int index = -1; - esp_err_t err = ESP_FAIL; - const esp_partition_t* partition = NULL; - esp_littlefs_t * efs = NULL; - - if( _efs_lock == NULL ){ - static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - portENTER_CRITICAL(&mux); - if( _efs_lock == NULL ){ - _efs_lock = xSemaphoreCreateMutex(); - assert(_efs_lock); - } - portEXIT_CRITICAL(&mux); - } - - xSemaphoreTake(_efs_lock, portMAX_DELAY); - - if (esp_littlefs_get_empty(&index) != ESP_OK) { - ESP_LOGE(TAG, "max mounted partitions reached"); - err = ESP_ERR_INVALID_STATE; - goto exit; - } - - /* Input and Environment Validation */ - if (esp_littlefs_by_label(conf->partition_label, &index) == ESP_OK) { - ESP_LOGE(TAG, "Partition already used"); - err = ESP_ERR_INVALID_STATE; - goto exit; - } - - { - uint32_t flash_page_size = g_rom_flashchip.page_size; - uint32_t log_page_size = CONFIG_LITTLEFS_PAGE_SIZE; - if (log_page_size % flash_page_size != 0) { - ESP_LOGE(TAG, "LITTLEFS_PAGE_SIZE is not multiple of flash chip page size (%d)", - flash_page_size); - err = ESP_ERR_INVALID_ARG; - goto exit; - } - } - - if ( NULL == conf->partition_label ) { - ESP_LOGE(TAG, "Partition label must be provided."); - err = ESP_ERR_INVALID_ARG; - goto exit; - } - - partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, - conf->partition_label); - - if (!partition) { - ESP_LOGE(TAG, "partition \"%s\" could not be found", conf->partition_label); - err = ESP_ERR_NOT_FOUND; - goto exit; - } - - /* Allocate Context */ - efs = calloc(1, sizeof(esp_littlefs_t)); - if (efs == NULL) { - ESP_LOGE(TAG, "esp_littlefs could not be malloced"); - err = ESP_ERR_NO_MEM; - goto exit; - } - efs->partition = partition; - - { /* LittleFS Configuration */ - efs->cfg.context = efs; - - // block device operations - efs->cfg.read = littlefs_api_read; - efs->cfg.prog = littlefs_api_prog; - efs->cfg.erase = littlefs_api_erase; - efs->cfg.sync = littlefs_api_sync; - - // block device configuration - efs->cfg.read_size = CONFIG_LITTLEFS_READ_SIZE; - efs->cfg.prog_size = CONFIG_LITTLEFS_WRITE_SIZE; - efs->cfg.block_size = CONFIG_LITTLEFS_BLOCK_SIZE;; - efs->cfg.block_count = efs->partition->size / efs->cfg.block_size; - efs->cfg.cache_size = CONFIG_LITTLEFS_CACHE_SIZE; - efs->cfg.lookahead_size = CONFIG_LITTLEFS_LOOKAHEAD_SIZE; - efs->cfg.block_cycles = CONFIG_LITTLEFS_BLOCK_CYCLES; - } - - efs->lock = xSemaphoreCreateRecursiveMutex(); - if (efs->lock == NULL) { - ESP_LOGE(TAG, "mutex lock could not be created"); - err = ESP_ERR_NO_MEM; - goto exit; - } - - efs->fs = calloc(1, sizeof(lfs_t)); - if (efs->fs == NULL) { - ESP_LOGE(TAG, "littlefs could not be malloced"); - err = ESP_ERR_NO_MEM; - goto exit; - } - - // Mount and Error Check - _efs[index] = efs; - if(!conf->dont_mount){ - int res = lfs_mount(efs->fs, &efs->cfg); - - if (conf->format_if_mount_failed && res != LFS_ERR_OK) { - esp_err_t err; - ESP_LOGW(TAG, "mount failed, %s (%i). formatting...", esp_littlefs_errno(res), res); - err = esp_littlefs_format(efs->partition->label); - if(err != ESP_OK) { - ESP_LOGE(TAG, "format failed"); - err = ESP_FAIL; - goto exit; - } - res = lfs_mount(efs->fs, &efs->cfg); - } - if (res != LFS_ERR_OK) { - ESP_LOGE(TAG, "mount failed, %s (%i)", esp_littlefs_errno(res), res); - err = ESP_FAIL; - goto exit; - } - efs->cache_size = 4; - efs->cache = calloc(sizeof(*efs->cache), efs->cache_size); - } - - err = ESP_OK; - -exit: - if(err != ESP_OK){ - if( index >= 0 ) { - esp_littlefs_free(&_efs[index]); - } - else{ - esp_littlefs_free(&efs); - } - } - xSemaphoreGive(_efs_lock); - return err; -} - -/** - * @brief - * @parameter efs file system context - */ -static inline int sem_take(esp_littlefs_t *efs) { - int res; -#if LOG_LOCAL_LEVEL >= 5 - ESP_LOGV(TAG, "------------------------ Sem Taking [%s]", pcTaskGetTaskName(NULL)); -#endif - res = xSemaphoreTakeRecursive(efs->lock, portMAX_DELAY); -#if LOG_LOCAL_LEVEL >= 5 - ESP_LOGV(TAG, "--------------------->>> Sem Taken [%s]", pcTaskGetTaskName(NULL)); -#endif - return res; -} - -/** - * @brief - * @parameter efs file system context - */ -static inline int sem_give(esp_littlefs_t *efs) { -#if LOG_LOCAL_LEVEL >= 5 - ESP_LOGV(TAG, "---------------------<<< Sem Give [%s]", pcTaskGetTaskName(NULL)); -#endif - return xSemaphoreGiveRecursive(efs->lock); -} - - -/* We are using a double allocation system here, which an array and a linked list. - The array contains the pointer to the file descriptor (the index in the array is what's returned to the user). - The linked list is used for file descriptors. - This means that position of nodes in the list must stay consistent: - - Allocation is obvious (append to the list from the head, and realloc the pointers array) - There is still a O(N) search in the cache for a free position to store - - Searching is a O(1) process (good) - - Deallocation is more tricky. That is, for example, - if you need to remove node 5 in a 12 nodes list, you'll have to: - 1) Mark the 5th position as freed (if it's the last position of the array realloc smaller) - 2) Walk the list until finding the pointer to the node O(N) and scrub the node so the chained list stays consistent - 3) Deallocate the node -*/ - -/** - * @brief Get a file descriptor - * @param[in,out] efs file system context - * @param[out] file pointer to a file that'll be filled with a file object - * @param[in] path_len the length of the filepath in bytes (including terminating zero byte) - * @return integer file descriptor. Returns -1 if a FD cannot be obtained. - * @warning This must be called with lock taken - */ -static int esp_littlefs_allocate_fd(esp_littlefs_t *efs, vfs_littlefs_file_t ** file -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - , const size_t path_len -#endif - ) -{ - int i = -1; - - assert( efs->fd_count < UINT16_MAX ); - assert( efs->cache_size < UINT16_MAX ); - - /* Make sure there is enough space in the cache to store new fd */ - if (efs->fd_count + 1 > efs->cache_size) { - uint16_t new_size = (uint16_t)MIN(UINT16_MAX, CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR * efs->cache_size); - /* Resize the cache */ - vfs_littlefs_file_t ** new_cache = realloc(efs->cache, new_size * sizeof(*efs->cache)); - if (!new_cache) { - ESP_LOGE(TAG, "Unable to allocate file cache"); - return -1; /* If it fails here, no harm is done to the filesystem, so it's safe */ - } - /* Zero out the new portions of the cache */ - memset(&new_cache[efs->cache_size], 0, (new_size - efs->cache_size) * sizeof(*efs->cache)); - efs->cache = new_cache; - efs->cache_size = new_size; - } - - - /* Allocate file descriptor here now */ -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - *file = calloc(1, sizeof(**file) + path_len); -#else - *file = calloc(1, sizeof(**file)); -#endif - - if (*file == NULL) { - /* If it fails here, the file system might have a larger cache, but it's harmless, no need to reverse it */ - ESP_LOGE(TAG, "Unable to allocate FD"); - return -1; - } - - /* Starting from here, nothing can fail anymore */ - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - /* The trick here is to avoid dual allocation so the path pointer - should point to the next byte after it: - file => [ lfs_file | # | next | path | free_space ] - | /\ - |__/ - */ - (*file)->path = (char*)(*file) + sizeof(**file); -#endif - - /* Now find a free place in cache */ - for(i=0; i < efs->cache_size; i++) { - if (efs->cache[i] == NULL) { - efs->cache[i] = *file; - break; - } - } - /* Save file in the list */ - (*file)->next = efs->file; - efs->file = *file; - efs->fd_count++; - return i; -} - -/** - * @brief Release a file descriptor - * @param[in,out] efs file system context - * @param[in] fd File Descriptor to release - * @return 0 on success. -1 if a FD cannot be obtained. - * @warning This must be called with lock taken - */ -static int esp_littlefs_free_fd(esp_littlefs_t *efs, int fd){ - vfs_littlefs_file_t * file, * head; - - if((uint32_t)fd >= efs->cache_size) { - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return -1; - } - - /* Get the file descriptor to free it */ - file = efs->cache[fd]; - head = efs->file; - /* Search for file in SLL to remove it */ - if (file == head) { - /* Last file, can't fail */ - efs->file = efs->file->next; - } else { - while (head && head->next != file) { - head = head->next; - } - if (!head) { - ESP_LOGE(TAG, "Inconsistent list"); - return -1; - } - /* Transaction starts here and can't fail anymore */ - head->next = file->next; - } - efs->cache[fd] = NULL; - efs->fd_count--; - - ESP_LOGV(TAG, "Clearing FD"); - free(file); - -#if 0 - /* Realloc smaller if its possible - * * Find and realloc based on number of trailing NULL ptrs in cache - * * Leave some hysteris to prevent thrashing around resize points - * This is disabled for now because it adds unnecessary complexity - * and binary size increase that outweights its ebenfits. - */ - if(efs->cache_size > CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE) { - uint16_t n_free; - uint16_t new_size = efs->cache_size / CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR; - - if(new_size >= CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE) { - /* Count number of trailing NULL ptrs */ - for(n_free=0; n_free < efs->cache_size; n_free++) { - if(efs->cache[efs->cache_size - n_free - 1] != NULL) { - break; - } - } - - if(n_free >= (efs->cache_size - new_size)){ - new_size += CONFIG_LITTLEFS_FD_CACHE_HYST; - ESP_LOGV(TAG, "Reallocating cache %i -> %i", efs->cache_size, new_size); - vfs_littlefs_file_t ** new_cache; - new_cache = realloc(efs->cache, new_size * sizeof(*efs->cache)); - /* No harm on realloc failure, continue using the oversized cache */ - if(new_cache) { - efs->cache = new_cache; - efs->cache_size = new_size; - } - } - } - } -#endif - - return 0; -} - -/** - * @brief Compute the 32bit DJB2 hash of the given string. - * @param[in] path the path to hash - * @returns the hash for this path - */ -static uint32_t compute_hash(const char * path) { - uint32_t hash = 5381; - char c; - - while ((c = *path++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - return hash; -} - -/** - * @brief finds an open file descriptor by file name. - * @param[in,out] efs file system context - * @param[in] path File path to check. - * @returns integer file descriptor. Returns -1 if not found. - * @warning This must be called with lock taken - * @warning if CONFIG_LITTLEFS_USE_ONLY_HASH, there is a slim chance an - * erroneous FD may be returned on hash collision. - */ -static int esp_littlefs_get_fd_by_name(esp_littlefs_t *efs, const char *path){ - uint32_t hash = compute_hash(path); - - for(uint16_t i=0, j=0; i < efs->cache_size && j < efs->fd_count; i++){ - if (efs->cache[i]) { - ++j; - - if ( - efs->cache[i]->hash == hash // Faster than strcmp -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - && strcmp(path, efs->cache[i]->path) == 0 // May as well check incase of hash collision. Usually short-circuited. -#endif - ) { - ESP_LOGV(TAG, "Found \"%s\" at FD %d.", path, i); - return i; - } - } - } - ESP_LOGV(TAG, "Unable to get a find FD for \"%s\"", path); - return -1; -} - -/*** Filesystem Hooks ***/ - -static int vfs_littlefs_open(void* ctx, const char * path, int flags, int mode) { - /* Note: mode is currently unused */ - int fd=-1, lfs_flags, res; - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - vfs_littlefs_file_t *file = NULL; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - size_t path_len = strlen(path) + 1; // include NULL terminator -#endif - assert(path); - - ESP_LOGV(TAG, "Opening %s", path); - - /* Convert flags to lfs flags */ - lfs_flags = esp_littlefs_flags_conv(flags); - - /* Get a FD */ - sem_take(efs); - fd = esp_littlefs_allocate_fd(efs, &file -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - , path_len -#endif - ); - if(fd < 0) { - errno = -fd; - sem_give(efs); - ESP_LOGV(TAG, "Error obtaining FD"); - return LFS_ERR_INVAL; - } - /* Open File */ - res = lfs_file_open(efs->fs, &file->file, path, lfs_flags); - - if( res < 0 ) { - errno = -res; - esp_littlefs_free_fd(efs, fd); - sem_give(efs); - ESP_LOGV(TAG, "Failed to open file. Error %s (%d)", - esp_littlefs_errno(res), res); - return LFS_ERR_INVAL; - } - - file->hash = compute_hash(path); -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - memcpy(file->path, path, path_len); -#endif - -#if CONFIG_LITTLEFS_USE_MTIME - if (lfs_flags != LFS_O_RDONLY) { - /* If this is being opened as not read-only */ - vfs_littlefs_update_mtime(efs, path); - } -#endif - - sem_give(efs); - ESP_LOGV(TAG, "Done opening %s", path); - return fd; -} - -static ssize_t vfs_littlefs_write(void* ctx, int fd, const void * data, size_t size) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_write(efs->fs, &file->file, data, size); - sem_give(efs); - - if(res < 0){ - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to write FD %d; path \"%s\". Error %s (%d)", - fd, file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to write FD %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - - return res; -} - -static ssize_t vfs_littlefs_read(void* ctx, int fd, void * dst, size_t size) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_read(efs->fs, &file->file, dst, size); - sem_give(efs); - - if(res < 0){ - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to read file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to read FD %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - - return res; -} - -static int vfs_littlefs_close(void* ctx, int fd) { - // TODO update mtime on close? SPIFFS doesn't do this - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - int res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_close(efs->fs, &file->file); - if(res < 0){ - errno = -res; - sem_give(efs); -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to close file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to close Fd %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - esp_littlefs_free_fd(efs, fd); - sem_give(efs); - return res; -} - -static off_t vfs_littlefs_lseek(void* ctx, int fd, off_t offset, int mode) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - lfs_soff_t res; - vfs_littlefs_file_t *file = NULL; - int whence; - - switch (mode) { - case SEEK_SET: whence = LFS_SEEK_SET; break; - case SEEK_CUR: whence = LFS_SEEK_CUR; break; - case SEEK_END: whence = LFS_SEEK_END; break; - default: - ESP_LOGE(TAG, "Invalid mode"); - return -1; - } - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_seek(efs->fs, &file->file, offset, whence); - sem_give(efs); - - if(res < 0){ - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to seek file \"%s\" to offset %08x. Error %s (%d)", - file->path, (unsigned int)offset, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to seek FD %d to offset %08x. Error (%d)", - fd, (unsigned int)offset, res); -#endif - return res; - } - - return res; -} - -static int vfs_littlefs_fsync(void* ctx, int fd) -{ - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_sync(efs->fs, &file->file); - sem_give(efs); - - if(res < 0){ - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to sync file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to sync file %d. Error %d", fd, res); -#endif - return res; - } - - return res; -} - - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH -static int vfs_littlefs_fstat(void* ctx, int fd, struct stat * st) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - vfs_littlefs_file_t *file = NULL; - - memset(st, 0, sizeof(struct stat)); - st->st_blksize = efs->cfg.block_size; - - sem_take(efs); - if((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD must be <%d.", efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_stat(efs->fs, file->path, &info); - if (res < 0) { - errno = -res; - sem_give(efs); - ESP_LOGV(TAG, "Failed to stat file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); - return res; - } - -#if CONFIG_LITTLEFS_USE_MTIME - st->st_mtime = vfs_littlefs_get_mtime(efs, file->path); -#endif - - sem_give(efs); - - st->st_size = info.size; - st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR); - return 0; -} -#endif - -static int vfs_littlefs_stat(void* ctx, const char * path, struct stat * st) { - assert(path); - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - memset(st, 0, sizeof(struct stat)); - st->st_blksize = efs->cfg.block_size; - - sem_take(efs); - res = lfs_stat(efs->fs, path, &info); - if (res < 0) { - errno = -res; - sem_give(efs); - /* Not strictly an error, since stat can be used to check - * if a file exists */ - ESP_LOGV(TAG, "Failed to stat path \"%s\". Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } -#if CONFIG_LITTLEFS_USE_MTIME - st->st_mtime = vfs_littlefs_get_mtime(efs, path); -#endif - sem_give(efs); - st->st_size = info.size; - st->st_mode = ((info.type==LFS_TYPE_REG)?S_IFREG:S_IFDIR); - return 0; -} - -static int vfs_littlefs_unlink(void* ctx, const char *path) { -#define fail_str_1 "Failed to unlink path \"%s\"." - assert(path); - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - sem_take(efs); - res = lfs_stat(efs->fs, path, &info); - if (res < 0) { - errno = -res; - sem_give(efs); - ESP_LOGV(TAG, fail_str_1 " Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } - - if(esp_littlefs_get_fd_by_name(efs, path) >= 0) { - sem_give(efs); - ESP_LOGE(TAG, fail_str_1 " Has open FD.", path); - return -1; - } - - //if (info.type == LFS_TYPE_DIR) { - // sem_give(efs); - // ESP_LOGV(TAG, "Cannot unlink a directory."); - // return LFS_ERR_ISDIR; - //} - - res = lfs_remove(efs->fs, path); - if (res < 0) { - errno = -res; - sem_give(efs); - ESP_LOGV(TAG, fail_str_1 " Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } - - sem_give(efs); - - return 0; -#undef fail_str_1 -} - -static int vfs_littlefs_rename(void* ctx, const char *src, const char *dst) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - int res; - - sem_take(efs); - - if(esp_littlefs_get_fd_by_name(efs, src) >= 0){ - sem_give(efs); - ESP_LOGE(TAG, "Cannot rename; src \"%s\" is open.", src); - return -1; - } - else if(esp_littlefs_get_fd_by_name(efs, dst) >= 0){ - sem_give(efs); - ESP_LOGE(TAG, "Cannot rename; dst \"%s\" is open.", dst); - return -1; - } - - res = lfs_rename(efs->fs, src, dst); - sem_give(efs); - if (res < 0) { - errno = -res; - ESP_LOGV(TAG, "Failed to rename \"%s\" -> \"%s\". Error %s (%d)", - src, dst, esp_littlefs_errno(res), res); - return res; - } - - return 0; -} - -static DIR* vfs_littlefs_opendir(void* ctx, const char* name) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - int res; - vfs_littlefs_dir_t *dir = NULL; - - dir = calloc(1, sizeof(vfs_littlefs_dir_t)); - if( dir == NULL ) { - ESP_LOGE(TAG, "dir struct could not be malloced"); - goto exit; - } - - dir->path = strdup(name); - if(dir->path == NULL){ - ESP_LOGE(TAG, "dir path name could not be malloced"); - goto exit; - } - - sem_take(efs); - res = lfs_dir_open(efs->fs, &dir->d, dir->path); - sem_give(efs); - if (res < 0) { - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to opendir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to opendir \"%s\". Error %d", dir->path, res); -#endif - goto exit; - } - - return (DIR *)dir; - -exit: - esp_littlefs_dir_free(dir); - return NULL; -} - -static int vfs_littlefs_closedir(void* ctx, DIR* pdir) { - assert(pdir); - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t * dir = (vfs_littlefs_dir_t *) pdir; - int res; - - sem_take(efs); - res = lfs_dir_close(efs->fs, &dir->d); - sem_give(efs); - if (res < 0) { - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to closedir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to closedir \"%s\". Error %d", dir->path, res); -#endif - return res; - } - - esp_littlefs_dir_free(dir); - return 0; -} - -static struct dirent* vfs_littlefs_readdir(void* ctx, DIR* pdir) { - assert(pdir); - vfs_littlefs_dir_t * dir = (vfs_littlefs_dir_t *) pdir; - int res; - struct dirent* out_dirent; - - res = vfs_littlefs_readdir_r(ctx, pdir, &dir->e, &out_dirent); - if (res != 0) return NULL; - return out_dirent; -} - -static int vfs_littlefs_readdir_r(void* ctx, DIR* pdir, - struct dirent* entry, struct dirent** out_dirent) { - assert(pdir); - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t * dir = (vfs_littlefs_dir_t *) pdir; - int res; - struct lfs_info info = { 0 }; - - sem_take(efs); - do{ /* Read until we get a real object name */ - res = lfs_dir_read(efs->fs, &dir->d, &info); - }while( res>0 && (strcmp(info.name, ".") == 0 || strcmp(info.name, "..") == 0)); - sem_give(efs); - if (res < 0) { - errno = -res; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to readdir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGV(TAG, "Failed to readdir \"%s\". Error %d", dir->path, res); -#endif - return -1; - } - - if(info.type == LFS_TYPE_REG) { - ESP_LOGV(TAG, "readdir a file of size %d named \"%s\"", - info.size, info.name); - } - else { - ESP_LOGV(TAG, "readdir a dir named \"%s\"", info.name); - } - - if(res == 0) { - /* End of Objs */ - ESP_LOGV(TAG, "Reached the end of the directory."); - *out_dirent = NULL; - } - else { - entry->d_ino = 0; - entry->d_type = info.type == LFS_TYPE_REG ? DT_REG : DT_DIR; - strncpy(entry->d_name, info.name, sizeof(entry->d_name)); - *out_dirent = entry; - } - dir->offset++; - - return 0; -} - -static long vfs_littlefs_telldir(void* ctx, DIR* pdir) { - assert(pdir); - vfs_littlefs_dir_t * dir = (vfs_littlefs_dir_t *) pdir; - return dir->offset; -} - -static void vfs_littlefs_seekdir(void* ctx, DIR* pdir, long offset) { - assert(pdir); - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t * dir = (vfs_littlefs_dir_t *) pdir; - int res; - - if (offset < dir->offset) { - /* close and re-open dir to rewind to beginning */ - sem_take(efs); - res = lfs_dir_rewind(efs->fs, &dir->d); - sem_give(efs); - if (res < 0) { - errno = -res; - ESP_LOGV(TAG, "Failed to rewind dir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); - return; - } - dir->offset = 0; - } - - while(dir->offset < offset){ - struct dirent *out_dirent; - res = vfs_littlefs_readdir_r(ctx, pdir, &dir->e, &out_dirent); - if( res != 0 ){ - ESP_LOGE(TAG, "Error readdir_r"); - return; - } - } -} - -static int vfs_littlefs_mkdir(void* ctx, const char* name, mode_t mode) { - /* Note: mode is currently unused */ - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - int res; - ESP_LOGV(TAG, "mkdir \"%s\"", name); - - sem_take(efs); - res = lfs_mkdir(efs->fs, name); - sem_give(efs); - if (res < 0) { - errno = -res; - ESP_LOGV(TAG, "Failed to mkdir \"%s\". Error %s (%d)", - name, esp_littlefs_errno(res), res); - return res; - } - return 0; -} - -static int vfs_littlefs_rmdir(void* ctx, const char* name) { - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - /* Error Checking */ - sem_take(efs); - res = lfs_stat(efs->fs, name, &info); - if (res < 0) { - errno = -res; - sem_give(efs); - ESP_LOGV(TAG, "\"%s\" doesn't exist.", name); - return -1; - } - - if (info.type != LFS_TYPE_DIR) { - sem_give(efs); - ESP_LOGV(TAG, "\"%s\" is not a directory.", name); - return -1; - } - - /* Unlink the dir */ - res = lfs_remove(efs->fs, name); - sem_give(efs); - if ( res < 0) { - errno = -res; - ESP_LOGV(TAG, "Failed to unlink path \"%s\". Error %s (%d)", - name, esp_littlefs_errno(res), res); - return -1; - } - - return 0; -} - -#if CONFIG_LITTLEFS_USE_MTIME -/** - * Sets the mtime attr to t. - */ -static int vfs_littlefs_update_mtime_value(esp_littlefs_t *efs, const char *path, time_t t) -{ - int res; - res = lfs_setattr(efs->fs, path, LITTLEFS_ATTR_MTIME, - &t, sizeof(t)); - if( res < 0 ) { - errno = -res; - ESP_LOGV(TAG, "Failed to update mtime (%d)", res); - } - - return res; -} - -/** - * Sets the mtime attr to an appropriate value - */ -static void vfs_littlefs_update_mtime(esp_littlefs_t *efs, const char *path) -{ - vfs_littlefs_utime(efs, path, NULL); -} - - -static int vfs_littlefs_utime(void *ctx, const char *path, const struct utimbuf *times) -{ - esp_littlefs_t * efs = (esp_littlefs_t *)ctx; - time_t t; - - assert(path); - - if (times) { - t = times->modtime; - } else { -#if CONFIG_LITTLEFS_MTIME_USE_SECONDS - // use current time - t = time(NULL); -#elif CONFIG_LITTLEFS_MTIME_USE_NONCE - assert( sizeof(time_t) == 4 ); - t = vfs_littlefs_get_mtime(efs, path); - if( 0 == t ) t = esp_random(); - else t += 1; - - if( 0 == t ) t = 1; -#else -#error "Invalid MTIME configuration" -#endif - } - - int ret = vfs_littlefs_update_mtime_value(efs, path, t); - return ret; -} - -static time_t vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path) -{ - time_t t = 0; - int size; - size = lfs_getattr(efs->fs, path, LITTLEFS_ATTR_MTIME, - &t, sizeof(t)); - if( size < 0 ) { - errno = -size; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGV(TAG, "Failed to get mtime attribute %s (%d)", - esp_littlefs_errno(size), size); -#else - ESP_LOGV(TAG, "Failed to get mtime attribute %d", size); -#endif - } - return t; -} -#endif //CONFIG_LITTLEFS_USE_MTIME diff --git a/lib/LITTLEFS/src/esp_littlefs.h b/lib/LITTLEFS/src/esp_littlefs.h deleted file mode 100644 index d65e9547..00000000 --- a/lib/LITTLEFS/src/esp_littlefs.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef ESP_LITTLEFS_H__ -#define ESP_LITTLEFS_H__ - -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include -#include -#include -#include -//#include -#include -#include -#include -#include "sdkconfig.h" - -#include "lfs.h" //#include "littlefs/lfs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Last Modified Time - * - * Use 't' for LITTLEFS_ATTR_MTIME to match example: - * https://github.com/ARMmbed/littlefs/issues/23#issuecomment-482293539 - * And to match other external tools such as: - * https://github.com/earlephilhower/mklittlefs - */ -#define LITTLEFS_ATTR_MTIME ((uint8_t) 't') - -/** - *Configuration structure for esp_vfs_littlefs_register. - */ -typedef struct { - const char *base_path; /**< Mounting point. */ - const char *partition_label; /**< Label of partition to use. */ - uint8_t format_if_mount_failed:1; /**< Format the file system if it fails to mount. */ - uint8_t dont_mount:1; /**< Don't attempt to mount or format. Overrides format_if_mount_failed */ -} esp_vfs_littlefs_conf_t; - -/** - * Register and mount littlefs to VFS with given path prefix. - * - * @param conf Pointer to esp_vfs_littlefs_conf_t configuration structure - * - * @return - * - ESP_OK if success - * - ESP_ERR_NO_MEM if objects could not be allocated - * - ESP_ERR_INVALID_STATE if already mounted or partition is encrypted - * - ESP_ERR_NOT_FOUND if partition for littlefs was not found - * - ESP_FAIL if mount or format fails - */ -esp_err_t esp_vfs_littlefs_register(const esp_vfs_littlefs_conf_t * conf); - -/** - * Unregister and unmount littlefs from VFS - * - * @param partition_label Label of the partition to unregister. - * - * @return - * - ESP_OK if successful - * - ESP_ERR_INVALID_STATE already unregistered - */ -esp_err_t esp_vfs_littlefs_unregister(const char* partition_label); - -/** - * Check if littlefs is mounted - * - * @param partition_label Label of the partition to check. - * - * @return - * - true if mounted - * - false if not mounted - */ -bool esp_littlefs_mounted(const char* partition_label); - -/** - * Format the littlefs partition - * - * @param partition_label Label of the partition to format. - * @return - * - ESP_OK if successful - * - ESP_FAIL on error - */ -esp_err_t esp_littlefs_format(const char* partition_label); - -/** - * Get information for littlefs - * - * @param partition_label Optional, label of the partition to get info for. - * @param[out] total_bytes Size of the file system - * @param[out] used_bytes Current used bytes in the file system - * - * @return - * - ESP_OK if success - * - ESP_ERR_INVALID_STATE if not mounted - */ -esp_err_t esp_littlefs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes); - -#if CONFIG_LITTLEFS_HUMAN_READABLE -/** - * @brief converts an enumerated lfs error into a string. - * @param lfs_errno The enumerated littlefs error. - */ -const char * esp_littlefs_errno(enum lfs_error lfs_errno); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/lib/LITTLEFS/src/lfs.c b/lib/LITTLEFS/src/lfs.c deleted file mode 100644 index eb832fa0..00000000 --- a/lib/LITTLEFS/src/lfs.c +++ /dev/null @@ -1,4913 +0,0 @@ -/* - * The little filesystem - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "lfs.h" -#include "lfs_util.h" - -#define LFS_BLOCK_NULL ((lfs_block_t)-1) -#define LFS_BLOCK_INLINE ((lfs_block_t)-2) - -/// Caching block device operations /// -static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { - // do not zero, cheaper if cache is readonly or only going to be - // written with identical data (during relocates) - (void)lfs; - rcache->block = LFS_BLOCK_NULL; -} - -static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { - // zero to avoid information leak - memset(pcache->buffer, 0xff, lfs->cfg->cache_size); - pcache->block = LFS_BLOCK_NULL; -} - -static int lfs_bd_read(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - if (block >= lfs->cfg->block_count || - off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && block == pcache->block && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (block == rcache->block && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - if (size >= hint && off % lfs->cfg->read_size == 0 && - size >= lfs->cfg->read_size) { - // bypass cache? - diff = lfs_aligndown(diff, lfs->cfg->read_size); - int err = lfs->cfg->read(lfs->cfg, block, off, data, diff); - if (err) { - return err; - } - - data += diff; - off += diff; - size -= diff; - continue; - } - - // load to cache, first condition can no longer fail - LFS_ASSERT(block < lfs->cfg->block_count); - rcache->block = block; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min( - lfs_min( - lfs_alignup(off+hint, lfs->cfg->read_size), - lfs->cfg->block_size) - - rcache->off, - lfs->cfg->cache_size); - int err = lfs->cfg->read(lfs->cfg, rcache->block, - rcache->off, rcache->buffer, rcache->size); - LFS_ASSERT(err <= 0); - if (err) { - return err; - } - } - - return 0; -} - -enum { - LFS_CMP_EQ = 0, - LFS_CMP_LT = 1, - LFS_CMP_GT = 2, -}; - -static int lfs_bd_cmp(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - - for (lfs_off_t i = 0; i < size; i++) { - uint8_t dat; - int err = lfs_bd_read(lfs, - pcache, rcache, hint-i, - block, off+i, &dat, 1); - if (err) { - return err; - } - - if (dat != data[i]) { - return (dat < data[i]) ? LFS_CMP_LT : LFS_CMP_GT; - } - } - - return LFS_CMP_EQ; -} - -static int lfs_bd_flush(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) { - LFS_ASSERT(pcache->block < lfs->cfg->block_count); - lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); - int err = lfs->cfg->prog(lfs->cfg, pcache->block, - pcache->off, pcache->buffer, diff); - LFS_ASSERT(err <= 0); - if (err) { - return err; - } - - if (validate) { - // check data on disk - lfs_cache_drop(lfs, rcache); - int res = lfs_bd_cmp(lfs, - NULL, rcache, diff, - pcache->block, pcache->off, pcache->buffer, diff); - if (res < 0) { - return res; - } - - if (res != LFS_CMP_EQ) { - return LFS_ERR_CORRUPT; - } - } - - lfs_cache_zero(lfs, pcache); - } - - return 0; -} - -static int lfs_bd_sync(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - lfs_cache_drop(lfs, rcache); - - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - - err = lfs->cfg->sync(lfs->cfg); - LFS_ASSERT(err <= 0); - return err; -} - -static int lfs_bd_prog(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count); - LFS_ASSERT(off + size <= lfs->cfg->block_size); - - while (size > 0) { - if (block == pcache->block && - off >= pcache->off && - off < pcache->off + lfs->cfg->cache_size) { - // already fits in pcache? - lfs_size_t diff = lfs_min(size, - lfs->cfg->cache_size - (off-pcache->off)); - memcpy(&pcache->buffer[off-pcache->off], data, diff); - - data += diff; - off += diff; - size -= diff; - - pcache->size = lfs_max(pcache->size, off - pcache->off); - if (pcache->size == lfs->cfg->cache_size) { - // eagerly flush out pcache if we fill up - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - } - - continue; - } - - // pcache must have been flushed, either by programming and - // entire block or manually flushing the pcache - LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); - - // prepare pcache, first condition can no longer fail - pcache->block = block; - pcache->off = lfs_aligndown(off, lfs->cfg->prog_size); - pcache->size = 0; - } - - return 0; -} - -static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { - LFS_ASSERT(block < lfs->cfg->block_count); - int err = lfs->cfg->erase(lfs->cfg, block); - LFS_ASSERT(err <= 0); - return err; -} - - -/// Small type-level utilities /// -// operations on block pairs -static inline void lfs_pair_swap(lfs_block_t pair[2]) { - lfs_block_t t = pair[0]; - pair[0] = pair[1]; - pair[1] = t; -} - -static inline bool lfs_pair_isnull(const lfs_block_t pair[2]) { - return pair[0] == LFS_BLOCK_NULL || pair[1] == LFS_BLOCK_NULL; -} - -static inline int lfs_pair_cmp( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return !(paira[0] == pairb[0] || paira[1] == pairb[1] || - paira[0] == pairb[1] || paira[1] == pairb[0]); -} - -static inline bool lfs_pair_sync( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return (paira[0] == pairb[0] && paira[1] == pairb[1]) || - (paira[0] == pairb[1] && paira[1] == pairb[0]); -} - -static inline void lfs_pair_fromle32(lfs_block_t pair[2]) { - pair[0] = lfs_fromle32(pair[0]); - pair[1] = lfs_fromle32(pair[1]); -} - -static inline void lfs_pair_tole32(lfs_block_t pair[2]) { - pair[0] = lfs_tole32(pair[0]); - pair[1] = lfs_tole32(pair[1]); -} - -// operations on 32-bit entry tags -typedef uint32_t lfs_tag_t; -typedef int32_t lfs_stag_t; - -#define LFS_MKTAG(type, id, size) \ - (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size)) - -#define LFS_MKTAG_IF(cond, type, id, size) \ - ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0)) - -#define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \ - ((cond) ? LFS_MKTAG(type1, id1, size1) : LFS_MKTAG(type2, id2, size2)) - -static inline bool lfs_tag_isvalid(lfs_tag_t tag) { - return !(tag & 0x80000000); -} - -static inline bool lfs_tag_isdelete(lfs_tag_t tag) { - return ((int32_t)(tag << 22) >> 22) == -1; -} - -static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { - return (tag & 0x70000000) >> 20; -} - -static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { - return (tag & 0x7ff00000) >> 20; -} - -static inline uint8_t lfs_tag_chunk(lfs_tag_t tag) { - return (tag & 0x0ff00000) >> 20; -} - -static inline int8_t lfs_tag_splice(lfs_tag_t tag) { - return (int8_t)lfs_tag_chunk(tag); -} - -static inline uint16_t lfs_tag_id(lfs_tag_t tag) { - return (tag & 0x000ffc00) >> 10; -} - -static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { - return tag & 0x000003ff; -} - -static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { - return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); -} - -// operations on attributes in attribute lists -struct lfs_mattr { - lfs_tag_t tag; - const void *buffer; -}; - -struct lfs_diskoff { - lfs_block_t block; - lfs_off_t off; -}; - -#define LFS_MKATTRS(...) \ - (struct lfs_mattr[]){__VA_ARGS__}, \ - sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) - -// operations on global state -static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) { - for (int i = 0; i < 3; i++) { - ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; - } -} - -static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) { - for (int i = 0; i < 3; i++) { - if (((uint32_t*)a)[i] != 0) { - return false; - } - } - return true; -} - -static inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) { - return lfs_tag_size(a->tag); -} - -static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) { - return lfs_tag_size(a->tag); -} - -static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) { - return lfs_tag_type1(a->tag); -} - -static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a, - const lfs_block_t *pair) { - return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; -} - -static inline void lfs_gstate_fromle32(lfs_gstate_t *a) { - a->tag = lfs_fromle32(a->tag); - a->pair[0] = lfs_fromle32(a->pair[0]); - a->pair[1] = lfs_fromle32(a->pair[1]); -} - -static inline void lfs_gstate_tole32(lfs_gstate_t *a) { - a->tag = lfs_tole32(a->tag); - a->pair[0] = lfs_tole32(a->pair[0]); - a->pair[1] = lfs_tole32(a->pair[1]); -} - -// other endianness operations -static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { - ctz->head = lfs_fromle32(ctz->head); - ctz->size = lfs_fromle32(ctz->size); -} - -static void lfs_ctz_tole32(struct lfs_ctz *ctz) { - ctz->head = lfs_tole32(ctz->head); - ctz->size = lfs_tole32(ctz->size); -} - -static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) { - superblock->version = lfs_fromle32(superblock->version); - superblock->block_size = lfs_fromle32(superblock->block_size); - superblock->block_count = lfs_fromle32(superblock->block_count); - superblock->name_max = lfs_fromle32(superblock->name_max); - superblock->file_max = lfs_fromle32(superblock->file_max); - superblock->attr_max = lfs_fromle32(superblock->attr_max); -} - -static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { - superblock->version = lfs_tole32(superblock->version); - superblock->block_size = lfs_tole32(superblock->block_size); - superblock->block_count = lfs_tole32(superblock->block_count); - superblock->name_max = lfs_tole32(superblock->name_max); - superblock->file_max = lfs_tole32(superblock->file_max); - superblock->attr_max = lfs_tole32(superblock->attr_max); -} - - -/// Internal operations predeclared here /// -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount); -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end); -static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]); -static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *pdir); -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *parent); -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]); -int lfs_fs_traverseraw(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data, - bool includeorphans); -static int lfs_fs_forceconsistency(lfs_t *lfs); -static int lfs_deinit(lfs_t *lfs); -#ifdef LFS_MIGRATE -static int lfs1_traverse(lfs_t *lfs, - int (*cb)(void*, lfs_block_t), void *data); -#endif - -/// Block allocator /// -static int lfs_alloc_lookahead(void *p, lfs_block_t block) { - lfs_t *lfs = (lfs_t*)p; - lfs_block_t off = ((block - lfs->free.off) - + lfs->cfg->block_count) % lfs->cfg->block_count; - - if (off < lfs->free.size) { - lfs->free.buffer[off / 32] |= 1U << (off % 32); - } - - return 0; -} - -static void lfs_alloc_ack(lfs_t *lfs) { - lfs->free.ack = lfs->cfg->block_count; -} - -// Invalidate the lookahead buffer. This is done during mounting and -// failed traversals -static void lfs_alloc_reset(lfs_t *lfs) { - lfs->free.off = lfs->seed % lfs->cfg->block_size; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); -} - -static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { - while (true) { - while (lfs->free.i != lfs->free.size) { - lfs_block_t off = lfs->free.i; - lfs->free.i += 1; - lfs->free.ack -= 1; - - if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { - // found a free block - *block = (lfs->free.off + off) % lfs->cfg->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->free.i != lfs->free.size && - (lfs->free.buffer[lfs->free.i / 32] - & (1U << (lfs->free.i % 32)))) { - lfs->free.i += 1; - lfs->free.ack -= 1; - } - - return 0; - } - } - - // check if we have looked at all blocks since last ack - if (lfs->free.ack == 0) { - LFS_ERROR("No more free space %"PRIu32, - lfs->free.i + lfs->free.off); - return LFS_ERR_NOSPC; - } - - lfs->free.off = (lfs->free.off + lfs->free.size) - % lfs->cfg->block_count; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack); - lfs->free.i = 0; - - // find mask of free blocks from tree - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); - if (err) { - lfs_alloc_reset(lfs); - return err; - } - } -} - -/// Metadata pair and directory operations /// -static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t goff, void *gbuffer, lfs_size_t gsize) { - lfs_off_t off = dir->off; - lfs_tag_t ntag = dir->etag; - lfs_stag_t gdiff = 0; - - if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair) && - lfs_tag_id(gmask) != 0 && - lfs_tag_id(lfs->gdisk.tag) <= lfs_tag_id(gtag)) { - // synthetic moves - gdiff -= LFS_MKTAG(0, 1, 0); - } - - // iterate over dir block backwards (for faster lookups) - while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { - off -= lfs_tag_dsize(ntag); - lfs_tag_t tag = ntag; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(ntag), - dir->pair[0], off, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff; - - if (lfs_tag_id(gmask) != 0 && - lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) { - if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { - // found where we were created - return LFS_ERR_NOENT; - } - - // move around splices - gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - if ((gmask & tag) == (gmask & (gtag - gdiff))) { - if (lfs_tag_isdelete(tag)) { - return LFS_ERR_NOENT; - } - - lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize); - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, diff, - dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); - if (err) { - return err; - } - - memset((uint8_t*)gbuffer + diff, 0, gsize - diff); - - return tag + gdiff; - } - } - - return LFS_ERR_NOENT; -} - -static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) { - return lfs_dir_getslice(lfs, dir, - gmask, gtag, - 0, buffer, lfs_tag_size(gtag)); -} - -static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t off, void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - if (off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && pcache->block == LFS_BLOCK_INLINE && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (rcache->block == LFS_BLOCK_INLINE && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - // load to cache, first condition can no longer fail - rcache->block = LFS_BLOCK_INLINE; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size), - lfs->cfg->cache_size); - int err = lfs_dir_getslice(lfs, dir, gmask, gtag, - rcache->off, rcache->buffer, rcache->size); - if (err < 0) { - return err; - } - } - - return 0; -} - -static int lfs_dir_traverse_filter(void *p, - lfs_tag_t tag, const void *buffer) { - lfs_tag_t *filtertag = p; - (void)buffer; - - // which mask depends on unique bit in tag structure - uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0)) - ? LFS_MKTAG(0x7ff, 0x3ff, 0) - : LFS_MKTAG(0x700, 0x3ff, 0); - - // check for redundancy - if ((mask & tag) == (mask & *filtertag) || - lfs_tag_isdelete(*filtertag) || - (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == ( - LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { - return true; - } - - // check if we need to adjust for created/deleted tags - if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) { - *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - return false; -} - -static int lfs_dir_traverse(lfs_t *lfs, - const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, - const struct lfs_mattr *attrs, int attrcount, - lfs_tag_t tmask, lfs_tag_t ttag, - uint16_t begin, uint16_t end, int16_t diff, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // iterate over directory and attrs - while (true) { - lfs_tag_t tag; - const void *buffer; - struct lfs_diskoff disk; - if (off+lfs_tag_dsize(ptag) < dir->off) { - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - return err; - } - - tag = (lfs_frombe32(tag) ^ ptag) | 0x80000000; - disk.block = dir->pair[0]; - disk.off = off+sizeof(lfs_tag_t); - buffer = &disk; - ptag = tag; - } else if (attrcount > 0) { - tag = attrs[0].tag; - buffer = attrs[0].buffer; - attrs += 1; - attrcount -= 1; - } else { - return 0; - } - - lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); - if ((mask & tmask & tag) != (mask & tmask & ttag)) { - continue; - } - - // do we need to filter? inlining the filtering logic here allows - // for some minor optimizations - if (lfs_tag_id(tmask) != 0) { - // scan for duplicates and update tag based on creates/deletes - int filter = lfs_dir_traverse(lfs, - dir, off, ptag, attrs, attrcount, - 0, 0, 0, 0, 0, - lfs_dir_traverse_filter, &tag); - if (filter < 0) { - return filter; - } - - if (filter) { - continue; - } - - // in filter range? - if (!(lfs_tag_id(tag) >= begin && lfs_tag_id(tag) < end)) { - continue; - } - } - - // handle special cases for mcu-side operations - if (lfs_tag_type3(tag) == LFS_FROM_NOOP) { - // do nothing - } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { - uint16_t fromid = lfs_tag_size(tag); - uint16_t toid = lfs_tag_id(tag); - int err = lfs_dir_traverse(lfs, - buffer, 0, 0xffffffff, NULL, 0, - LFS_MKTAG(0x600, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), - fromid, fromid+1, toid-fromid+diff, - cb, data); - if (err) { - return err; - } - } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { - for (unsigned i = 0; i < lfs_tag_size(tag); i++) { - const struct lfs_attr *a = buffer; - int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type, - lfs_tag_id(tag) + diff, a[i].size), a[i].buffer); - if (err) { - return err; - } - } - } else { - int err = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer); - if (err) { - return err; - } - } - } -} - -static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2], - lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // we can find tag very efficiently during a fetch, since we're already - // scanning the entire directory - lfs_stag_t besttag = -1; - - // if either block address is invalid we return LFS_ERR_CORRUPT here, - // otherwise later writes to the pair could fail - if (pair[0] >= lfs->cfg->block_count || pair[1] >= lfs->cfg->block_count) { - return LFS_ERR_CORRUPT; - } - - // find the block with the most recent revision - uint32_t revs[2] = {0, 0}; - int r = 0; - for (int i = 0; i < 2; i++) { - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(revs[i]), - pair[i], 0, &revs[i], sizeof(revs[i])); - revs[i] = lfs_fromle32(revs[i]); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err != LFS_ERR_CORRUPT && - lfs_scmp(revs[i], revs[(i+1)%2]) > 0) { - r = i; - } - } - - dir->pair[0] = pair[(r+0)%2]; - dir->pair[1] = pair[(r+1)%2]; - dir->rev = revs[(r+0)%2]; - dir->off = 0; // nonzero = found some commits - - // now scan tags to fetch the actual dir and find possible match - for (int i = 0; i < 2; i++) { - lfs_off_t off = 0; - lfs_tag_t ptag = 0xffffffff; - - uint16_t tempcount = 0; - lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; - bool tempsplit = false; - lfs_stag_t tempbesttag = besttag; - - dir->rev = lfs_tole32(dir->rev); - uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - - while (true) { - // extract next tag - lfs_tag_t tag; - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - // can't continue? - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &tag, sizeof(tag)); - tag = lfs_frombe32(tag) ^ ptag; - - // next commit not yet programmed or we're not in valid range - if (!lfs_tag_isvalid(tag)) { - dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC && - dir->off % lfs->cfg->prog_size == 0); - break; - } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { - dir->erased = false; - break; - } - - ptag = tag; - - if (lfs_tag_type1(tag) == LFS_TYPE_CRC) { - // check the crc attr - uint32_t dcrc; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - dcrc = lfs_fromle32(dcrc); - - if (crc != dcrc) { - dir->erased = false; - break; - } - - // reset the next bit if we need to - ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31; - - // toss our crc into the filesystem seed for - // pseudorandom numbers - lfs->seed ^= crc; - - // update with what's found so far - besttag = tempbesttag; - dir->off = off + lfs_tag_dsize(tag); - dir->etag = ptag; - dir->count = tempcount; - dir->tail[0] = temptail[0]; - dir->tail[1] = temptail[1]; - dir->split = tempsplit; - - // reset crc - crc = 0xffffffff; - continue; - } - - // crc the entry first, hopefully leaving it in the cache - for (lfs_off_t j = sizeof(tag); j < lfs_tag_dsize(tag); j++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+j, &dat, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - // directory modification tags? - if (lfs_tag_type1(tag) == LFS_TYPE_NAME) { - // increase count of files if necessary - if (lfs_tag_id(tag) >= tempcount) { - tempcount = lfs_tag_id(tag) + 1; - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) { - tempcount += lfs_tag_splice(tag); - - if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { - tempbesttag |= 0x80000000; - } else if (tempbesttag != -1 && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { - tempsplit = (lfs_tag_chunk(tag) & 1); - - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &temptail, 8); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - } - lfs_pair_fromle32(temptail); - } - - // found a match for our fetcher? - if ((fmask & tag) == (fmask & ftag)) { - int res = cb(data, tag, &(struct lfs_diskoff){ - dir->pair[0], off+sizeof(tag)}); - if (res < 0) { - if (res == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return res; - } - - if (res == LFS_CMP_EQ) { - // found a match - tempbesttag = tag; - } else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == - (LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) { - // found an identical tag, but contents didn't match - // this must mean that our besttag has been overwritten - tempbesttag = -1; - } else if (res == LFS_CMP_GT && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - // found a greater match, keep track to keep things sorted - tempbesttag = tag | 0x80000000; - } - } - } - - // consider what we have good enough - if (dir->off > 0) { - // synthetic move - if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) { - if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) { - besttag |= 0x80000000; - } else if (besttag != -1 && - lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) { - besttag -= LFS_MKTAG(0, 1, 0); - } - } - - // found tag? or found best id? - if (id) { - *id = lfs_min(lfs_tag_id(besttag), dir->count); - } - - if (lfs_tag_isvalid(besttag)) { - return besttag; - } else if (lfs_tag_id(besttag) < dir->count) { - return LFS_ERR_NOENT; - } else { - return 0; - } - } - - // failed, try the other block? - lfs_pair_swap(dir->pair); - dir->rev = revs[(r+1)%2]; - } - - LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", - dir->pair[0], dir->pair[1]); - return LFS_ERR_CORRUPT; -} - -static int lfs_dir_fetch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2]) { - // note, mask=-1, tag=-1 can never match a tag since this - // pattern has the invalid bit set - return (int)lfs_dir_fetchmatch(lfs, dir, pair, - (lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL); -} - -static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_gstate_t *gstate) { - lfs_gstate_t temp; - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); - if (res < 0 && res != LFS_ERR_NOENT) { - return res; - } - - if (res != LFS_ERR_NOENT) { - // xor together to find resulting gstate - lfs_gstate_fromle32(&temp); - lfs_gstate_xor(gstate, &temp); - } - - return 0; -} - -static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, - uint16_t id, struct lfs_info *info) { - if (id == 0x3ff) { - // special case for root - strcpy(info->name, "/"); - info->type = LFS_TYPE_DIR; - return 0; - } - - lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); - if (tag < 0) { - return (int)tag; - } - - info->type = lfs_tag_type3(tag); - - struct lfs_ctz ctz; - tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - return (int)tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - info->size = ctz.size; - } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - info->size = lfs_tag_size(tag); - } - - return 0; -} - -struct lfs_dir_find_match { - lfs_t *lfs; - const void *name; - lfs_size_t size; -}; - -static int lfs_dir_find_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_dir_find_match *name = data; - lfs_t *lfs = name->lfs; - const struct lfs_diskoff *disk = buffer; - - // compare with disk - lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag)); - int res = lfs_bd_cmp(lfs, - NULL, &lfs->rcache, diff, - disk->block, disk->off, name->name, diff); - if (res != LFS_CMP_EQ) { - return res; - } - - // only equal if our size is still the same - if (name->size != lfs_tag_size(tag)) { - return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT; - } - - // found a match! - return LFS_CMP_EQ; -} - -static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, - const char **path, uint16_t *id) { - // we reduce path to a single name if we can find it - const char *name = *path; - if (id) { - *id = 0x3ff; - } - - // default to root dir - lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); - dir->tail[0] = lfs->root[0]; - dir->tail[1] = lfs->root[1]; - - while (true) { -nextname: - // skip slashes - name += strspn(name, "/"); - lfs_size_t namelen = strcspn(name, "/"); - - // skip '.' and root '..' - if ((namelen == 1 && memcmp(name, ".", 1) == 0) || - (namelen == 2 && memcmp(name, "..", 2) == 0)) { - name += namelen; - goto nextname; - } - - // skip if matched by '..' in name - const char *suffix = name + namelen; - lfs_size_t sufflen; - int depth = 1; - while (true) { - suffix += strspn(suffix, "/"); - sufflen = strcspn(suffix, "/"); - if (sufflen == 0) { - break; - } - - if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { - depth -= 1; - if (depth == 0) { - name = suffix + sufflen; - goto nextname; - } - } else { - depth += 1; - } - - suffix += sufflen; - } - - // found path - if (name[0] == '\0') { - return tag; - } - - // update what we've found so far - *path = name; - - // only continue if we hit a directory - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - return LFS_ERR_NOTDIR; - } - - // grab the entry data - if (lfs_tag_id(tag) != 0x3ff) { - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); - if (res < 0) { - return res; - } - lfs_pair_fromle32(dir->tail); - } - - // find entry matching name - while (true) { - tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, - LFS_MKTAG(0x780, 0, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, name, namelen}); - if (tag < 0) { - return tag; - } - - if (tag) { - break; - } - - if (!dir->split) { - return LFS_ERR_NOENT; - } - } - - // to next name - name += namelen; - } -} - -// commit logic -struct lfs_commit { - lfs_block_t block; - lfs_off_t off; - lfs_tag_t ptag; - uint32_t crc; - - lfs_off_t begin; - lfs_off_t end; -}; - -static int lfs_dir_commitprog(lfs_t *lfs, struct lfs_commit *commit, - const void *buffer, lfs_size_t size) { - int err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off , - (const uint8_t*)buffer, size); - if (err) { - return err; - } - - commit->crc = lfs_crc(commit->crc, buffer, size); - commit->off += size; - return 0; -} - -static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, - lfs_tag_t tag, const void *buffer) { - // check if we fit - lfs_size_t dsize = lfs_tag_dsize(tag); - if (commit->off + dsize > commit->end) { - return LFS_ERR_NOSPC; - } - - // write out tag - lfs_tag_t ntag = lfs_tobe32((tag & 0x7fffffff) ^ commit->ptag); - int err = lfs_dir_commitprog(lfs, commit, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - if (!(tag & 0x80000000)) { - // from memory - err = lfs_dir_commitprog(lfs, commit, buffer, dsize-sizeof(tag)); - if (err) { - return err; - } - } else { - // from disk - const struct lfs_diskoff *disk = buffer; - for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { - // rely on caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dsize-sizeof(tag)-i, - disk->block, disk->off+i, &dat, 1); - if (err) { - return err; - } - - err = lfs_dir_commitprog(lfs, commit, &dat, 1); - if (err) { - return err; - } - } - } - - commit->ptag = tag & 0x7fffffff; - return 0; -} - -static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { - const lfs_off_t off1 = commit->off; - const uint32_t crc1 = commit->crc; - // align to program units - const lfs_off_t end = lfs_alignup(off1 + 2*sizeof(uint32_t), - lfs->cfg->prog_size); - - // create crc tags to fill up remainder of commit, note that - // padding is not crced, which lets fetches skip padding but - // makes committing a bit more complicated - while (commit->off < end) { - lfs_off_t off = commit->off + sizeof(lfs_tag_t); - lfs_off_t noff = lfs_min(end - off, 0x3fe) + off; - if (noff < end) { - noff = lfs_min(noff, end - 2*sizeof(uint32_t)); - } - - // read erased state from next program unit - lfs_tag_t tag = 0xffffffff; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - commit->block, noff, &tag, sizeof(tag)); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // build crc tag - bool reset = ~lfs_frombe32(tag) >> 31; - tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off); - - // write out crc - uint32_t footer[2]; - footer[0] = lfs_tobe32(tag ^ commit->ptag); - commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs_tole32(commit->crc); - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); - if (err) { - return err; - } - - commit->off += sizeof(tag)+lfs_tag_size(tag); - commit->ptag = tag ^ ((lfs_tag_t)reset << 31); - commit->crc = 0xffffffff; // reset crc for next "commit" - } - - // flush buffers - int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); - if (err) { - return err; - } - - // successful commit, check checksums to make sure - lfs_off_t off = commit->begin; - lfs_off_t noff = off1 + sizeof(uint32_t); - while (off < end) { - uint32_t crc = 0xffffffff; - for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) { - // check against written crc, may catch blocks that - // become readonly and match our commit size exactly - if (i == off1 && crc != crc1) { - return LFS_ERR_CORRUPT; - } - - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, noff+sizeof(uint32_t)-i, - commit->block, i, &dat, 1); - if (err) { - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - // detected write error? - if (crc != 0) { - return LFS_ERR_CORRUPT; - } - - // skip padding - off = lfs_min(end - noff, 0x3fe) + noff; - if (off < end) { - off = lfs_min(off, end - 2*sizeof(uint32_t)); - } - noff = off + sizeof(uint32_t); - } - - return 0; -} - -static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { - // allocate pair of dir blocks (backwards, so we write block 1 first) - for (int i = 0; i < 2; i++) { - int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); - if (err) { - return err; - } - } - - // zero for reproducability in case initial block is unreadable - dir->rev = 0; - - // rather than clobbering one of the blocks we just pretend - // the revision may be valid - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(dir->rev), - dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // make sure we don't immediately evict - dir->rev += dir->rev & 1; - - // set defaults - dir->off = sizeof(dir->rev); - dir->etag = 0xffffffff; - dir->count = 0; - dir->tail[0] = LFS_BLOCK_NULL; - dir->tail[1] = LFS_BLOCK_NULL; - dir->erased = false; - dir->split = false; - - // don't write out yet, let caller take care of that - return 0; -} - -static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { - // steal state - int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); - if (err) { - return err; - } - - // steal tail - lfs_pair_tole32(tail->tail); - err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); - lfs_pair_fromle32(tail->tail); - if (err) { - return err; - } - - return 0; -} - -static int lfs_dir_split(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t split, uint16_t end) { - // create tail directory - lfs_alloc_ack(lfs); - lfs_mdir_t tail; - int err = lfs_dir_alloc(lfs, &tail); - if (err) { - return err; - } - - tail.split = dir->split; - tail.tail[0] = dir->tail[0]; - tail.tail[1] = dir->tail[1]; - - err = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end); - if (err) { - return err; - } - - dir->tail[0] = tail.pair[0]; - dir->tail[1] = tail.pair[1]; - dir->split = true; - - // update root if needed - if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && split == 0) { - lfs->root[0] = tail.pair[0]; - lfs->root[1] = tail.pair[1]; - } - - return 0; -} - -static int lfs_dir_commit_size(void *p, lfs_tag_t tag, const void *buffer) { - lfs_size_t *size = p; - (void)buffer; - - *size += lfs_tag_dsize(tag); - return 0; -} - -struct lfs_dir_commit_commit { - lfs_t *lfs; - struct lfs_commit *commit; -}; - -static int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) { - struct lfs_dir_commit_commit *commit = p; - return lfs_dir_commitattr(commit->lfs, commit->commit, tag, buffer); -} - -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end) { - // save some state in case block is bad - const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]}; - bool relocated = false; - bool tired = false; - - // should we split? - while (end - begin > 1) { - // find size - lfs_size_t size = 0; - int err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_size, &size); - if (err) { - return err; - } - - // space is complicated, we need room for tail, crc, gstate, - // cleanup delete, and we cap at half a block to give room - // for metadata updates. - if (end - begin < 0xff && - size <= lfs_min(lfs->cfg->block_size - 36, - lfs_alignup(lfs->cfg->block_size/2, - lfs->cfg->prog_size))) { - break; - } - - // can't fit, need to split, we should really be finding the - // largest size that fits with a small binary search, but right now - // it's not worth the code size - uint16_t split = (end - begin) / 2; - err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin+split, end); - if (err) { - // if we fail to split, we may be able to overcompact, unless - // we're too big for even the full block, in which case our - // only option is to error - if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) { - break; - } - return err; - } - - end = begin + split; - } - - // increment revision count - dir->rev += 1; - // If our revision count == n * block_cycles, we should force a relocation, - // this is how littlefs wear-levels at the metadata-pair level. Note that we - // actually use (block_cycles+1)|1, this is to avoid two corner cases: - // 1. block_cycles = 1, which would prevent relocations from terminating - // 2. block_cycles = 2n, which, due to aliasing, would only ever relocate - // one metadata block in the pair, effectively making this useless - if (lfs->cfg->block_cycles > 0 && - (dir->rev % ((lfs->cfg->block_cycles+1)|1) == 0)) { - if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { - // oh no! we're writing too much to the superblock, - // should we expand? - lfs_ssize_t res = lfs_fs_size(lfs); - if (res < 0) { - return res; - } - - // do we have extra space? littlefs can't reclaim this space - // by itself, so expand cautiously - if ((lfs_size_t)res < lfs->cfg->block_count/2) { - LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); - int err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin, end); - if (err && err != LFS_ERR_NOSPC) { - return err; - } - - // welp, we tried, if we ran out of space there's not much - // we can do, we'll error later if we've become frozen - if (!err) { - end = begin; - } - } -#ifdef LFS_MIGRATE - } else if (lfs->lfs1) { - // do not proactively relocate blocks during migrations, this - // can cause a number of failure states such: clobbering the - // v1 superblock if we relocate root, and invalidating directory - // pointers if we relocate the head of a directory. On top of - // this, relocations increase the overall complexity of - // lfs_migration, which is already a delicate operation. -#endif - } else { - // we're writing too much, time to relocate - tired = true; - goto relocate; - } - } - - // begin loop to commit compaction to blocks until a compact sticks - while (true) { - { - // setup commit state - struct lfs_commit commit = { - .block = dir->pair[1], - .off = 0, - .ptag = 0xffffffff, - .crc = 0xffffffff, - - .begin = 0, - .end = lfs->cfg->block_size - 8, - }; - - // erase block to write to - int err = lfs_bd_erase(lfs, dir->pair[1]); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // write out header - dir->rev = lfs_tole32(dir->rev); - err = lfs_dir_commitprog(lfs, &commit, - &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // traverse the directory, this time writing out all unique tags - err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // commit tail, which may be new after last size check - if (!lfs_pair_isnull(dir->tail)) { - lfs_pair_tole32(dir->tail); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), - dir->tail); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // bring over gstate? - lfs_gstate_t delta = {0}; - if (!relocated) { - lfs_gstate_xor(&delta, &lfs->gdisk); - lfs_gstate_xor(&delta, &lfs->gstate); - } - lfs_gstate_xor(&delta, &lfs->gdelta); - delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); - - err = lfs_dir_getgstate(lfs, dir, &delta); - if (err) { - return err; - } - - if (!lfs_gstate_iszero(&delta)) { - lfs_gstate_tole32(&delta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(delta)), &delta); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // complete commit with crc - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // successful compaction, swap dir pair to indicate most recent - LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); - lfs_pair_swap(dir->pair); - dir->count = end - begin; - dir->off = commit.off; - dir->etag = commit.ptag; - // update gstate - lfs->gdelta = (lfs_gstate_t){0}; - if (!relocated) { - lfs->gdisk = lfs->gstate; - } - } - break; - -relocate: - // commit was corrupted, drop caches and prepare to relocate block - relocated = true; - lfs_cache_drop(lfs, &lfs->pcache); - if (!tired) { - LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]); - } - - // can't relocate superblock, filesystem is now frozen - if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { - LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", - dir->pair[1]); - return LFS_ERR_NOSPC; - } - - // relocate half of pair - int err = lfs_alloc(lfs, &dir->pair[1]); - if (err && (err != LFS_ERR_NOSPC || !tired)) { - return err; - } - - tired = false; - continue; - } - - if (relocated) { - // update references if we relocated - LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); - int err = lfs_fs_relocate(lfs, oldpair, dir->pair); - if (err) { - return err; - } - } - - return 0; -} - -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount) { - // check for any inline files that aren't RAM backed and - // forcefully evict them, needed for filesystem consistency - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 && - f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && - f->ctz.size > lfs->cfg->cache_size) { - int err = lfs_file_outline(lfs, f); - if (err) { - return err; - } - - err = lfs_file_flush(lfs, f); - if (err) { - return err; - } - } - } - - // calculate changes to the directory - lfs_mdir_t olddir = *dir; - bool hasdelete = false; - for (int i = 0; i < attrcount; i++) { - if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { - dir->count += 1; - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) { - LFS_ASSERT(dir->count > 0); - dir->count -= 1; - hasdelete = true; - } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { - dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; - dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; - dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); - lfs_pair_fromle32(dir->tail); - } - } - - // should we actually drop the directory block? - if (hasdelete && dir->count == 0) { - lfs_mdir_t pdir; - int err = lfs_fs_pred(lfs, dir->pair, &pdir); - if (err && err != LFS_ERR_NOENT) { - *dir = olddir; - return err; - } - - if (err != LFS_ERR_NOENT && pdir.split) { - err = lfs_dir_drop(lfs, &pdir, dir); - if (err) { - *dir = olddir; - return err; - } - } - } - - if (dir->erased || dir->count >= 0xff) { - // try to commit - struct lfs_commit commit = { - .block = dir->pair[0], - .off = dir->off, - .ptag = dir->etag, - .crc = 0xffffffff, - - .begin = dir->off, - .end = lfs->cfg->block_size - 8, - }; - - // traverse attrs that need to be written out - lfs_pair_tole32(dir->tail); - int err = lfs_dir_traverse(lfs, - dir, dir->off, dir->etag, attrs, attrcount, - 0, 0, 0, 0, 0, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - - // commit any global diffs if we have any - lfs_gstate_t delta = {0}; - lfs_gstate_xor(&delta, &lfs->gstate); - lfs_gstate_xor(&delta, &lfs->gdisk); - lfs_gstate_xor(&delta, &lfs->gdelta); - delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); - if (!lfs_gstate_iszero(&delta)) { - err = lfs_dir_getgstate(lfs, dir, &delta); - if (err) { - *dir = olddir; - return err; - } - - lfs_gstate_tole32(&delta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(delta)), &delta); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - } - - // finalize commit with the crc - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - - // successful commit, update dir - LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); - dir->off = commit.off; - dir->etag = commit.ptag; - // and update gstate - lfs->gdisk = lfs->gstate; - lfs->gdelta = (lfs_gstate_t){0}; - } else { -compact: - // fall back to compaction - lfs_cache_drop(lfs, &lfs->pcache); - - int err = lfs_dir_compact(lfs, dir, attrs, attrcount, - dir, 0, dir->count); - if (err) { - *dir = olddir; - return err; - } - } - - // this complicated bit of logic is for fixing up any active - // metadata-pairs that we may have affected - // - // note we have to make two passes since the mdir passed to - // lfs_dir_commit could also be in this list, and even then - // we need to copy the pair so they don't get clobbered if we refetch - // our mdir. - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (&d->m != dir && lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { - d->m = *dir; - for (int i = 0; i < attrcount; i++) { - if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && - d->id == lfs_tag_id(attrs[i].tag)) { - d->m.pair[0] = LFS_BLOCK_NULL; - d->m.pair[1] = LFS_BLOCK_NULL; - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && - d->id > lfs_tag_id(attrs[i].tag)) { - d->id -= 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos -= 1; - } - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE && - d->id >= lfs_tag_id(attrs[i].tag)) { - d->id += 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos += 1; - } - } - } - } - } - - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { - while (d->id >= d->m.count && d->m.split) { - // we split and id is on tail now - d->id -= d->m.count; - int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); - if (err) { - return err; - } - } - } - } - - return 0; -} - - -/// Top level directory operations /// -int lfs_mkdir(lfs_t *lfs, const char *path) { - LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path); - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - struct lfs_mlist cwd; - cwd.next = lfs->mlist; - uint16_t id; - err = lfs_dir_find(lfs, &cwd.m, &path, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - LFS_TRACE("lfs_mkdir -> %d", (err < 0) ? err : LFS_ERR_EXIST); - return (err < 0) ? err : LFS_ERR_EXIST; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - LFS_TRACE("lfs_mkdir -> %d", LFS_ERR_NAMETOOLONG); - return LFS_ERR_NAMETOOLONG; - } - - // build up new directory - lfs_alloc_ack(lfs); - lfs_mdir_t dir; - err = lfs_dir_alloc(lfs, &dir); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - // find end of list - lfs_mdir_t pred = cwd.m; - while (pred.split) { - err = lfs_dir_fetch(lfs, &pred, pred.tail); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - } - - // setup dir - lfs_pair_tole32(pred.tail); - err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); - lfs_pair_fromle32(pred.tail); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - // current block end of list? - if (cwd.m.split) { - // update tails, this creates a desync - lfs_fs_preporphans(lfs, +1); - - // it's possible our predecessor has to be relocated, and if - // our parent is our predecessor's predecessor, this could have - // caused our parent to go out of date, fortunately we can hook - // ourselves into littlefs to catch this - cwd.type = 0; - cwd.id = 0; - lfs->mlist = &cwd; - - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - lfs->mlist = cwd.next; - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - lfs->mlist = cwd.next; - lfs_fs_preporphans(lfs, -1); - } - - // now insert into our parent block - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, - {LFS_MKTAG_IF(!cwd.m.split, - LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - LFS_TRACE("lfs_mkdir -> %d", 0); - return 0; -} - -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { - LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path); - lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_dir_open -> %"PRId32, tag); - return tag; - } - - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - LFS_TRACE("lfs_dir_open -> %d", LFS_ERR_NOTDIR); - return LFS_ERR_NOTDIR; - } - - lfs_block_t pair[2]; - if (lfs_tag_id(tag) == 0x3ff) { - // handle root dir separately - pair[0] = lfs->root[0]; - pair[1] = lfs->root[1]; - } else { - // get dir pair from parent - lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - LFS_TRACE("lfs_dir_open -> %"PRId32, res); - return res; - } - lfs_pair_fromle32(pair); - } - - // fetch first pair - int err = lfs_dir_fetch(lfs, &dir->m, pair); - if (err) { - LFS_TRACE("lfs_dir_open -> %d", err); - return err; - } - - // setup entry - dir->head[0] = dir->m.pair[0]; - dir->head[1] = dir->m.pair[1]; - dir->id = 0; - dir->pos = 0; - - // add to list of mdirs - dir->type = LFS_TYPE_DIR; - dir->next = (lfs_dir_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)dir; - - LFS_TRACE("lfs_dir_open -> %d", 0); - return 0; -} - -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir); - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)dir) { - *p = (*p)->next; - break; - } - } - - LFS_TRACE("lfs_dir_close -> %d", 0); - return 0; -} - -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { - LFS_TRACE("lfs_dir_read(%p, %p, %p)", - (void*)lfs, (void*)dir, (void*)info); - memset(info, 0, sizeof(*info)); - - // special offset for '.' and '..' - if (dir->pos == 0) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, "."); - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; - } else if (dir->pos == 1) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, ".."); - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; - } - - while (true) { - if (dir->id == dir->m.count) { - if (!dir->m.split) { - LFS_TRACE("lfs_dir_read -> %d", false); - return false; - } - - int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - LFS_TRACE("lfs_dir_read -> %d", err); - return err; - } - - dir->id = 0; - } - - int err = lfs_dir_getinfo(lfs, &dir->m, dir->id, info); - if (err && err != LFS_ERR_NOENT) { - LFS_TRACE("lfs_dir_read -> %d", err); - return err; - } - - dir->id += 1; - if (err != LFS_ERR_NOENT) { - break; - } - } - - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; -} - -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { - LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")", - (void*)lfs, (void*)dir, off); - // simply walk from head dir - int err = lfs_dir_rewind(lfs, dir); - if (err) { - LFS_TRACE("lfs_dir_seek -> %d", err); - return err; - } - - // first two for ./.. - dir->pos = lfs_min(2, off); - off -= dir->pos; - - // skip superblock entry - dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0); - - while (off > 0) { - int diff = lfs_min(dir->m.count - dir->id, off); - dir->id += diff; - dir->pos += diff; - off -= diff; - - if (dir->id == dir->m.count) { - if (!dir->m.split) { - LFS_TRACE("lfs_dir_seek -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - LFS_TRACE("lfs_dir_seek -> %d", err); - return err; - } - - dir->id = 0; - } - } - - LFS_TRACE("lfs_dir_seek -> %d", 0); - return 0; -} - -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir); - (void)lfs; - LFS_TRACE("lfs_dir_tell -> %"PRId32, dir->pos); - return dir->pos; -} - -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir); - // reload the head dir - int err = lfs_dir_fetch(lfs, &dir->m, dir->head); - if (err) { - LFS_TRACE("lfs_dir_rewind -> %d", err); - return err; - } - - dir->id = 0; - dir->pos = 0; - LFS_TRACE("lfs_dir_rewind -> %d", 0); - return 0; -} - - -/// File index list operations /// -static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) { - lfs_off_t size = *off; - lfs_off_t b = lfs->cfg->block_size - 2*4; - lfs_off_t i = size / b; - if (i == 0) { - return 0; - } - - i = (size - 4*(lfs_popc(i-1)+2)) / b; - *off = size - b*i - 4*lfs_popc(i); - return i; -} - -static int lfs_ctz_find(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) { - if (size == 0) { - *block = LFS_BLOCK_NULL; - *off = 0; - return 0; - } - - lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - lfs_off_t target = lfs_ctz_index(lfs, &pos); - - while (current > target) { - lfs_size_t skip = lfs_min( - lfs_npw2(current-target+1) - 1, - lfs_ctz(current)); - - int err = lfs_bd_read(lfs, - pcache, rcache, sizeof(head), - head, 4*skip, &head, sizeof(head)); - head = lfs_fromle32(head); - if (err) { - return err; - } - - current -= 1 << skip; - } - - *block = head; - *off = pos; - return 0; -} - -static int lfs_ctz_extend(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_block_t *block, lfs_off_t *off) { - while (true) { - // go ahead and grab a block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - - { - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (size == 0) { - *block = nblock; - *off = 0; - return 0; - } - - lfs_size_t noff = size - 1; - lfs_off_t index = lfs_ctz_index(lfs, &noff); - noff = noff + 1; - - // just copy out the last block if it is incomplete - if (noff != lfs->cfg->block_size) { - for (lfs_off_t i = 0; i < noff; i++) { - uint8_t data; - err = lfs_bd_read(lfs, - NULL, rcache, noff-i, - head, i, &data, 1); - if (err) { - return err; - } - - err = lfs_bd_prog(lfs, - pcache, rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - *block = nblock; - *off = noff; - return 0; - } - - // append block - index += 1; - lfs_size_t skips = lfs_ctz(index) + 1; - lfs_block_t nhead = head; - for (lfs_off_t i = 0; i < skips; i++) { - nhead = lfs_tole32(nhead); - err = lfs_bd_prog(lfs, pcache, rcache, true, - nblock, 4*i, &nhead, 4); - nhead = lfs_fromle32(nhead); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (i != skips-1) { - err = lfs_bd_read(lfs, - NULL, rcache, sizeof(nhead), - nhead, 4*i, &nhead, sizeof(nhead)); - nhead = lfs_fromle32(nhead); - if (err) { - return err; - } - } - } - - *block = nblock; - *off = 4*skips; - return 0; - } - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, pcache); - } -} - -static int lfs_ctz_traverse(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - int (*cb)(void*, lfs_block_t), void *data) { - if (size == 0) { - return 0; - } - - lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - - while (true) { - int err = cb(data, head); - if (err) { - return err; - } - - if (index == 0) { - return 0; - } - - lfs_block_t heads[2]; - int count = 2 - (index & 1); - err = lfs_bd_read(lfs, - pcache, rcache, count*sizeof(head), - head, 0, &heads, count*sizeof(head)); - heads[0] = lfs_fromle32(heads[0]); - heads[1] = lfs_fromle32(heads[1]); - if (err) { - return err; - } - - for (int i = 0; i < count-1; i++) { - err = cb(data, heads[i]); - if (err) { - return err; - } - } - - head = heads[count-1]; - index -= count; - } -} - - -/// Top level file operations /// -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags, - const struct lfs_file_config *cfg) { - LFS_TRACE("lfs_file_opencfg(%p, %p, \"%s\", %x, %p {" - ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", - (void*)lfs, (void*)file, path, flags, - (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); - - // deorphan if we haven't yet, needed at most once after poweron - if ((flags & 3) != LFS_O_RDONLY) { - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_file_opencfg -> %d", err); - return err; - } - } - - // setup simple file details - int err; - file->cfg = cfg; - file->flags = flags | LFS_F_OPENED; - file->pos = 0; - file->off = 0; - file->cache.buffer = NULL; - - // allocate entry for file if it doesn't exist - lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { - err = tag; - goto cleanup; - } - - // get id, add to list of mdirs to catch update changes - file->type = LFS_TYPE_REG; - file->next = (lfs_file_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)file; - - if (tag == LFS_ERR_NOENT) { - if (!(flags & LFS_O_CREAT)) { - err = LFS_ERR_NOENT; - goto cleanup; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - // get next slot and create entry to remember name - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL})); - if (err) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0); - } else if (flags & LFS_O_EXCL) { - err = LFS_ERR_EXIST; - goto cleanup; - } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) { - err = LFS_ERR_ISDIR; - goto cleanup; - } else if (flags & LFS_O_TRUNC) { - // truncate if requested - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0); - file->flags |= LFS_F_DIRTY; - } else { - // try to load what's on disk, if it's inlined we'll fix it later - tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_ctz_fromle32(&file->ctz); - } - - // fetch attrs - for (unsigned i = 0; i < file->cfg->attr_count; i++) { - if ((file->flags & 3) != LFS_O_WRONLY) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type, - file->id, file->cfg->attrs[i].size), - file->cfg->attrs[i].buffer); - if (res < 0 && res != LFS_ERR_NOENT) { - err = res; - goto cleanup; - } - } - - if ((file->flags & 3) != LFS_O_RDONLY) { - if (file->cfg->attrs[i].size > lfs->attr_max) { - err = LFS_ERR_NOSPC; - goto cleanup; - } - - file->flags |= LFS_F_DIRTY; - } - } - - // allocate buffer if needed - if (file->cfg->buffer) { - file->cache.buffer = file->cfg->buffer; - } else { - file->cache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!file->cache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leak - lfs_cache_zero(lfs, &file->cache); - - if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - // load inline files - file->ctz.head = LFS_BLOCK_INLINE; - file->ctz.size = lfs_tag_size(tag); - file->flags |= LFS_F_INLINE; - file->cache.block = file->ctz.head; - file->cache.off = 0; - file->cache.size = lfs->cfg->cache_size; - - // don't always read (may be new/trunc file) - if (file->ctz.size > 0) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, - lfs_min(file->cache.size, 0x3fe)), - file->cache.buffer); - if (res < 0) { - err = res; - goto cleanup; - } - } - } - - LFS_TRACE("lfs_file_opencfg -> %d", 0); - return 0; - -cleanup: - // clean up lingering resources - file->flags |= LFS_F_ERRED; - lfs_file_close(lfs, file); - LFS_TRACE("lfs_file_opencfg -> %d", err); - return err; -} - -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags) { - LFS_TRACE("lfs_file_open(%p, %p, \"%s\", %x)", - (void*)lfs, (void*)file, path, flags); - static const struct lfs_file_config defaults = {0}; - int err = lfs_file_opencfg(lfs, file, path, flags, &defaults); - LFS_TRACE("lfs_file_open -> %d", err); - return err; -} - -int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - int err = lfs_file_sync(lfs, file); - - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)file) { - *p = (*p)->next; - break; - } - } - - // clean up memory - if (!file->cfg->buffer) { - lfs_free(file->cache.buffer); - } - - file->flags &= ~LFS_F_OPENED; - LFS_TRACE("lfs_file_close -> %d", err); - return err; -} - -static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { - LFS_ASSERT(file->flags & LFS_F_OPENED); - - while (true) { - // just relocate what exists into new block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // either read from dirty cache or disk - for (lfs_off_t i = 0; i < file->off; i++) { - uint8_t data; - if (file->flags & LFS_F_INLINE) { - err = lfs_dir_getread(lfs, &file->m, - // note we evict inline files before they can be dirty - NULL, &file->cache, file->off-i, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - i, &data, 1); - if (err) { - return err; - } - } else { - err = lfs_bd_read(lfs, - &file->cache, &lfs->rcache, file->off-i, - file->block, i, &data, 1); - if (err) { - return err; - } - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // copy over new state of file - memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->cache_size); - file->cache.block = lfs->pcache.block; - file->cache.off = lfs->pcache.off; - file->cache.size = lfs->pcache.size; - lfs_cache_zero(lfs, &lfs->pcache); - - file->block = nblock; - file->flags |= LFS_F_WRITING; - return 0; - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, &lfs->pcache); - } -} - -static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) { - file->off = file->pos; - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, file); - if (err) { - return err; - } - - file->flags &= ~LFS_F_INLINE; - return 0; -} - -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { - LFS_ASSERT(file->flags & LFS_F_OPENED); - - if (file->flags & LFS_F_READING) { - if (!(file->flags & LFS_F_INLINE)) { - lfs_cache_drop(lfs, &file->cache); - } - file->flags &= ~LFS_F_READING; - } - - if (file->flags & LFS_F_WRITING) { - lfs_off_t pos = file->pos; - - if (!(file->flags & LFS_F_INLINE)) { - // copy over anything after current branch - lfs_file_t orig = { - .ctz.head = file->ctz.head, - .ctz.size = file->ctz.size, - .flags = LFS_O_RDONLY | LFS_F_OPENED, - .pos = file->pos, - .cache = lfs->rcache, - }; - lfs_cache_drop(lfs, &lfs->rcache); - - while (file->pos < file->ctz.size) { - // copy over a byte at a time, leave it up to caching - // to make this efficient - uint8_t data; - lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1); - if (res < 0) { - return res; - } - - res = lfs_file_write(lfs, file, &data, 1); - if (res < 0) { - return res; - } - - // keep our reference to the rcache in sync - if (lfs->rcache.block != LFS_BLOCK_NULL) { - lfs_cache_drop(lfs, &orig.cache); - lfs_cache_drop(lfs, &lfs->rcache); - } - } - - // write out what we have - while (true) { - int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - break; - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, file->block); - err = lfs_file_relocate(lfs, file); - if (err) { - return err; - } - } - } else { - file->pos = lfs_max(file->pos, file->ctz.size); - } - - // actual file updates - file->ctz.head = file->block; - file->ctz.size = file->pos; - file->flags &= ~LFS_F_WRITING; - file->flags |= LFS_F_DIRTY; - - file->pos = pos; - } - - return 0; -} - -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - if (file->flags & LFS_F_ERRED) { - // it's not safe to do anything if our file errored - LFS_TRACE("lfs_file_sync -> %d", 0); - return 0; - } - - int err = lfs_file_flush(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_sync -> %d", err); - return err; - } - - if ((file->flags & LFS_F_DIRTY) && - !lfs_pair_isnull(file->m.pair)) { - // update dir entry - uint16_t type; - const void *buffer; - lfs_size_t size; - struct lfs_ctz ctz; - if (file->flags & LFS_F_INLINE) { - // inline the whole file - type = LFS_TYPE_INLINESTRUCT; - buffer = file->cache.buffer; - size = file->ctz.size; - } else { - // update the ctz reference - type = LFS_TYPE_CTZSTRUCT; - // copy ctz so alloc will work during a relocate - ctz = file->ctz; - lfs_ctz_tole32(&ctz); - buffer = &ctz; - size = sizeof(ctz); - } - - // commit file data and attributes - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(type, file->id, size), buffer}, - {LFS_MKTAG(LFS_FROM_USERATTRS, file->id, - file->cfg->attr_count), file->cfg->attrs})); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_sync -> %d", err); - return err; - } - - file->flags &= ~LFS_F_DIRTY; - } - - LFS_TRACE("lfs_file_sync -> %d", 0); - return 0; -} - -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")", - (void*)lfs, (void*)file, buffer, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_WRONLY); - - uint8_t *data = buffer; - lfs_size_t nsize = size; - - if (file->flags & LFS_F_WRITING) { - // flush out any writes - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } - - if (file->pos >= file->ctz.size) { - // eof if past end - LFS_TRACE("lfs_file_read -> %d", 0); - return 0; - } - - size = lfs_min(size, file->ctz.size - file->pos); - nsize = size; - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_READING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos, &file->block, &file->off); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } else { - file->block = LFS_BLOCK_INLINE; - file->off = file->pos; - } - - file->flags |= LFS_F_READING; - } - - // read as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - if (file->flags & LFS_F_INLINE) { - int err = lfs_dir_getread(lfs, &file->m, - NULL, &file->cache, lfs->cfg->block_size, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - file->off, data, diff); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } else { - int err = lfs_bd_read(lfs, - NULL, &file->cache, lfs->cfg->block_size, - file->block, file->off, data, diff); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - } - - LFS_TRACE("lfs_file_read -> %"PRId32, size); - return size; -} - -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")", - (void*)lfs, (void*)file, buffer, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); - - const uint8_t *data = buffer; - lfs_size_t nsize = size; - - if (file->flags & LFS_F_READING) { - // drop any reads - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - if ((file->flags & LFS_O_APPEND) && file->pos < file->ctz.size) { - file->pos = file->ctz.size; - } - - if (file->pos + size > lfs->file_max) { - // Larger than file limit? - LFS_TRACE("lfs_file_write -> %d", LFS_ERR_FBIG); - return LFS_ERR_FBIG; - } - - if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) { - // fill with zeros - lfs_off_t pos = file->pos; - file->pos = file->ctz.size; - - while (file->pos < pos) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - LFS_TRACE("lfs_file_write -> %"PRId32, res); - return res; - } - } - } - - if ((file->flags & LFS_F_INLINE) && - lfs_max(file->pos+nsize, file->ctz.size) > - lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, lfs->cfg->block_size/8))) { - // inline file doesn't fit anymore - int err = lfs_file_outline(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_WRITING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - if (!(file->flags & LFS_F_WRITING) && file->pos > 0) { - // find out which block we're extending from - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos-1, &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - - // mark cache as dirty since we may have read data into it - lfs_cache_zero(lfs, &file->cache); - } - - // extend file with new blocks - lfs_alloc_ack(lfs); - int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache, - file->block, file->pos, - &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } else { - file->block = LFS_BLOCK_INLINE; - file->off = file->pos; - } - - file->flags |= LFS_F_WRITING; - } - - // program as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - while (true) { - int err = lfs_bd_prog(lfs, &file->cache, &lfs->rcache, true, - file->block, file->off, data, diff); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - - break; -relocate: - err = lfs_file_relocate(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - - lfs_alloc_ack(lfs); - } - - file->flags &= ~LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %"PRId32, size); - return size; -} - -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence) { - LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)", - (void*)lfs, (void*)file, off, whence); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - // write out everything beforehand, may be noop if rdonly - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_seek -> %d", err); - return err; - } - - // find new pos - lfs_off_t npos = file->pos; - if (whence == LFS_SEEK_SET) { - npos = off; - } else if (whence == LFS_SEEK_CUR) { - npos = file->pos + off; - } else if (whence == LFS_SEEK_END) { - npos = file->ctz.size + off; - } - - if (npos > lfs->file_max) { - // file position out of range - LFS_TRACE("lfs_file_seek -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - // update pos - file->pos = npos; - LFS_TRACE("lfs_file_seek -> %"PRId32, npos); - return npos; -} - -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { - LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")", - (void*)lfs, (void*)file, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); - - if (size > LFS_FILE_MAX) { - LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - lfs_off_t pos = file->pos; - lfs_off_t oldsize = lfs_file_size(lfs, file); - if (size < oldsize) { - // need to flush since directly changing metadata - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_truncate -> %d", err); - return err; - } - - // lookup new head in ctz skip list - err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - size, &file->block, &file->off); - if (err) { - LFS_TRACE("lfs_file_truncate -> %d", err); - return err; - } - - file->ctz.head = file->block; - file->ctz.size = size; - file->flags |= LFS_F_DIRTY | LFS_F_READING; - } else if (size > oldsize) { - // flush+seek if not already at end - if (file->pos != oldsize) { - lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - } - - // fill with zeros - while (file->pos < size) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - } - } - - // restore pos - lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - - LFS_TRACE("lfs_file_truncate -> %d", 0); - return 0; -} - -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - (void)lfs; - LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos); - return file->pos; -} - -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file); - lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET); - if (res < 0) { - LFS_TRACE("lfs_file_rewind -> %"PRId32, res); - return (int)res; - } - - LFS_TRACE("lfs_file_rewind -> %d", 0); - return 0; -} - -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - (void)lfs; - if (file->flags & LFS_F_WRITING) { - LFS_TRACE("lfs_file_size -> %"PRId32, - lfs_max(file->pos, file->ctz.size)); - return lfs_max(file->pos, file->ctz.size); - } else { - LFS_TRACE("lfs_file_size -> %"PRId32, file->ctz.size); - return file->ctz.size; - } -} - - -/// General fs operations /// -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { - LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info); - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_stat -> %"PRId32, tag); - return (int)tag; - } - - int err = lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info); - LFS_TRACE("lfs_stat -> %d", err); - return err; -} - -int lfs_remove(lfs_t *lfs, const char *path) { - LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path); - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0 || lfs_tag_id(tag) == 0x3ff) { - LFS_TRACE("lfs_remove -> %"PRId32, (tag < 0) ? tag : LFS_ERR_INVAL); - return (tag < 0) ? (int)tag : LFS_ERR_INVAL; - } - - struct lfs_mlist dir; - dir.next = lfs->mlist; - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - LFS_TRACE("lfs_remove -> %"PRId32, res); - return (int)res; - } - lfs_pair_fromle32(pair); - - err = lfs_dir_fetch(lfs, &dir.m, pair); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - if (dir.m.count > 0 || dir.m.split) { - LFS_TRACE("lfs_remove -> %d", LFS_ERR_NOTEMPTY); - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - - // I know it's crazy but yes, dir can be changed by our parent's - // commit (if predecessor is child) - dir.type = 0; - dir.id = 0; - lfs->mlist = &dir; - } - - // delete the entry - err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL})); - if (err) { - lfs->mlist = dir.next; - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - lfs->mlist = dir.next; - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, dir.m.pair, &cwd); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - err = lfs_dir_drop(lfs, &cwd, &dir.m); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - } - - LFS_TRACE("lfs_remove -> %d", 0); - return 0; -} - -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { - LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath); - - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - // find old entry - lfs_mdir_t oldcwd; - lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL); - if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) { - LFS_TRACE("lfs_rename -> %"PRId32, - (oldtag < 0) ? oldtag : LFS_ERR_INVAL); - return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL; - } - - // find new entry - lfs_mdir_t newcwd; - uint16_t newid; - lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); - if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { - LFS_TRACE("lfs_rename -> %"PRId32, - (prevtag < 0) ? prevtag : LFS_ERR_INVAL); - return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL; - } - - // if we're in the same pair there's a few special cases... - bool samepair = (lfs_pair_cmp(oldcwd.pair, newcwd.pair) == 0); - uint16_t newoldid = lfs_tag_id(oldtag); - - struct lfs_mlist prevdir; - prevdir.next = lfs->mlist; - if (prevtag == LFS_ERR_NOENT) { - // check that name fits - lfs_size_t nlen = strlen(newpath); - if (nlen > lfs->name_max) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_NAMETOOLONG); - return LFS_ERR_NAMETOOLONG; - } - - // there is a small chance we are being renamed in the same - // directory/ to an id less than our old id, the global update - // to handle this is a bit messy - if (samepair && newid <= newoldid) { - newoldid += 1; - } - } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_ISDIR); - return LFS_ERR_ISDIR; - } else if (samepair && newid == newoldid) { - // we're renaming to ourselves?? - LFS_TRACE("lfs_rename -> %d", 0); - return 0; - } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t prevpair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); - if (res < 0) { - LFS_TRACE("lfs_rename -> %"PRId32, res); - return (int)res; - } - lfs_pair_fromle32(prevpair); - - // must be empty before removal - err = lfs_dir_fetch(lfs, &prevdir.m, prevpair); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - if (prevdir.m.count > 0 || prevdir.m.split) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_NOTEMPTY); - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - - // I know it's crazy but yes, dir can be changed by our parent's - // commit (if predecessor is child) - prevdir.type = 0; - prevdir.id = 0; - lfs->mlist = &prevdir; - } - - if (!samepair) { - lfs_fs_prepmove(lfs, newoldid, oldcwd.pair); - } - - // move over all attributes - err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( - {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, - LFS_TYPE_DELETE, newid, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, - {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath}, - {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd}, - {LFS_MKTAG_IF(samepair, - LFS_TYPE_DELETE, newoldid, 0), NULL})); - if (err) { - lfs->mlist = prevdir.next; - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - // let commit clean up after move (if we're different! otherwise move - // logic already fixed it for us) - if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) { - // prep gstate and delete move id - lfs_fs_prepmove(lfs, 0x3ff, NULL); - err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0), NULL})); - if (err) { - lfs->mlist = prevdir.next; - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - } - - lfs->mlist = prevdir.next; - if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, prevdir.m.pair, &newcwd); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - err = lfs_dir_drop(lfs, &newcwd, &prevdir.m); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - } - - LFS_TRACE("lfs_rename -> %d", 0); - return 0; -} - -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs, path, type, buffer, size); - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_getattr -> %"PRId32, tag); - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - LFS_TRACE("lfs_getattr -> %d", err); - return err; - } - } - - tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + type, - id, lfs_min(size, lfs->attr_max)), - buffer); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - LFS_TRACE("lfs_getattr -> %d", LFS_ERR_NOATTR); - return LFS_ERR_NOATTR; - } - - LFS_TRACE("lfs_getattr -> %"PRId32, tag); - return tag; - } - - size = lfs_tag_size(tag); - LFS_TRACE("lfs_getattr -> %"PRId32, size); - return size; -} - -static int lfs_commitattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - } - - return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer})); -} - -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs, path, type, buffer, size); - if (size > lfs->attr_max) { - LFS_TRACE("lfs_setattr -> %d", LFS_ERR_NOSPC); - return LFS_ERR_NOSPC; - } - - int err = lfs_commitattr(lfs, path, type, buffer, size); - LFS_TRACE("lfs_setattr -> %d", err); - return err; -} - -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { - LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type); - int err = lfs_commitattr(lfs, path, type, NULL, 0x3ff); - LFS_TRACE("lfs_removeattr -> %d", err); - return err; -} - - -/// Filesystem operations /// -static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { - lfs->cfg = cfg; - int err = 0; - - // validate that the lfs-cfg sizes were initiated properly before - // performing any arithmetic logics with them - LFS_ASSERT(lfs->cfg->read_size != 0); - LFS_ASSERT(lfs->cfg->prog_size != 0); - LFS_ASSERT(lfs->cfg->cache_size != 0); - - // check that block size is a multiple of cache size is a multiple - // of prog and read sizes - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0); - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0); - LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0); - - // check that the block size is large enough to fit ctz pointers - LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) - <= lfs->cfg->block_size); - - // block_cycles = 0 is no longer supported. - // - // block_cycles is the number of erase cycles before littlefs evicts - // metadata logs as a part of wear leveling. Suggested values are in the - // range of 100-1000, or set block_cycles to -1 to disable block-level - // wear-leveling. - LFS_ASSERT(lfs->cfg->block_cycles != 0); - - - // setup read cache - if (lfs->cfg->read_buffer) { - lfs->rcache.buffer = lfs->cfg->read_buffer; - } else { - lfs->rcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->rcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // setup program cache - if (lfs->cfg->prog_buffer) { - lfs->pcache.buffer = lfs->cfg->prog_buffer; - } else { - lfs->pcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->pcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leaks - lfs_cache_zero(lfs, &lfs->rcache); - lfs_cache_zero(lfs, &lfs->pcache); - - // setup lookahead, must be multiple of 64-bits, 32-bit aligned - LFS_ASSERT(lfs->cfg->lookahead_size > 0); - LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0); - if (lfs->cfg->lookahead_buffer) { - lfs->free.buffer = lfs->cfg->lookahead_buffer; - } else { - lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size); - if (!lfs->free.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // check that the size limits are sane - LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX); - lfs->name_max = lfs->cfg->name_max; - if (!lfs->name_max) { - lfs->name_max = LFS_NAME_MAX; - } - - LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX); - lfs->file_max = lfs->cfg->file_max; - if (!lfs->file_max) { - lfs->file_max = LFS_FILE_MAX; - } - - LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX); - lfs->attr_max = lfs->cfg->attr_max; - if (!lfs->attr_max) { - lfs->attr_max = LFS_ATTR_MAX; - } - - // setup default state - lfs->root[0] = LFS_BLOCK_NULL; - lfs->root[1] = LFS_BLOCK_NULL; - lfs->mlist = NULL; - lfs->seed = 0; - lfs->gdisk = (lfs_gstate_t){0}; - lfs->gstate = (lfs_gstate_t){0}; - lfs->gdelta = (lfs_gstate_t){0}; -#ifdef LFS_MIGRATE - lfs->lfs1 = NULL; -#endif - - return 0; - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs_deinit(lfs_t *lfs) { - // free allocated memory - if (!lfs->cfg->read_buffer) { - lfs_free(lfs->rcache.buffer); - } - - if (!lfs->cfg->prog_buffer) { - lfs_free(lfs->pcache.buffer); - } - - if (!lfs->cfg->lookahead_buffer) { - lfs_free(lfs->free.buffer); - } - - return 0; -} - -int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_format(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - LFS_TRACE("lfs_format -> %d", err); - return err; - } - - // create free lookahead - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - lfs->free.off = 0; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, - lfs->cfg->block_count); - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // create root dir - lfs_mdir_t root; - err = lfs_dir_alloc(lfs, &root); - if (err) { - goto cleanup; - } - - // write one superblock - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - // force compaction to prevent accidentally mounting any - // older version of littlefs that may live on disk - root.erased = false; - err = lfs_dir_commit(lfs, &root, NULL, 0); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs_deinit(lfs); - LFS_TRACE("lfs_format -> %d", err); - return err; -} - -int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_mount(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - int err = lfs_init(lfs, cfg); - if (err) { - LFS_TRACE("lfs_mount -> %d", err); - return err; - } - - // scan directory blocks for superblock and any global updates - lfs_mdir_t dir = {.tail = {0, 1}}; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(dir.tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - err = LFS_ERR_CORRUPT; - goto cleanup; - } - cycle += 1; - - // fetch next block in tail list - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), - NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, "littlefs", 8}); - if (tag < 0) { - err = tag; - goto cleanup; - } - - // has superblock? - if (tag && !lfs_tag_isdelete(tag)) { - // update root - lfs->root[0] = dir.pair[0]; - lfs->root[1] = dir.pair[1]; - - // grab superblock - lfs_superblock_t superblock; - tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_superblock_fromle32(&superblock); - - // check version - uint16_t major_version = (0xffff & (superblock.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.version >> 0)); - if ((major_version != LFS_DISK_VERSION_MAJOR || - minor_version > LFS_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16, - major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - // check superblock configuration - if (superblock.name_max) { - if (superblock.name_max > lfs->name_max) { - LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", - superblock.name_max, lfs->name_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->name_max = superblock.name_max; - } - - if (superblock.file_max) { - if (superblock.file_max > lfs->file_max) { - LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", - superblock.file_max, lfs->file_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->file_max = superblock.file_max; - } - - if (superblock.attr_max) { - if (superblock.attr_max > lfs->attr_max) { - LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", - superblock.attr_max, lfs->attr_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->attr_max = superblock.attr_max; - } - } - - // has gstate? - err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate); - if (err) { - goto cleanup; - } - } - - // found superblock? - if (lfs_pair_isnull(lfs->root)) { - err = LFS_ERR_INVAL; - goto cleanup; - } - - // update littlefs with gstate - if (!lfs_gstate_iszero(&lfs->gstate)) { - LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, - lfs->gstate.tag, - lfs->gstate.pair[0], - lfs->gstate.pair[1]); - } - lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag); - lfs->gdisk = lfs->gstate; - - // setup free lookahead - lfs_alloc_reset(lfs); - - LFS_TRACE("lfs_mount -> %d", 0); - return 0; - -cleanup: - lfs_unmount(lfs); - LFS_TRACE("lfs_mount -> %d", err); - return err; -} - -int lfs_unmount(lfs_t *lfs) { - LFS_TRACE("lfs_unmount(%p)", (void*)lfs); - int err = lfs_deinit(lfs); - LFS_TRACE("lfs_unmount -> %d", err); - return err; -} - - -/// Filesystem filesystem operations /// -int lfs_fs_traverseraw(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data, - bool includeorphans) { - // iterate over metadata pairs - lfs_mdir_t dir = {.tail = {0, 1}}; - -#ifdef LFS_MIGRATE - // also consider v1 blocks during migration - if (lfs->lfs1) { - int err = lfs1_traverse(lfs, cb, data); - if (err) { - return err; - } - - dir.tail[0] = lfs->root[0]; - dir.tail[1] = lfs->root[1]; - } -#endif - - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(dir.tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - for (int i = 0; i < 2; i++) { - int err = cb(data, dir.tail[i]); - if (err) { - return err; - } - } - - // iterate through ids in directory - int err = lfs_dir_fetch(lfs, &dir, dir.tail); - if (err) { - return err; - } - - for (uint16_t id = 0; id < dir.count; id++) { - struct lfs_ctz ctz; - lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - continue; - } - return tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - ctz.head, ctz.size, cb, data); - if (err) { - return err; - } - } else if (includeorphans && - lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { - for (int i = 0; i < 2; i++) { - err = cb(data, (&ctz.head)[i]); - if (err) { - return err; - } - } - } - } - } - - // iterate over any open files - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (f->type != LFS_TYPE_REG) { - continue; - } - - if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->ctz.head, f->ctz.size, cb, data); - if (err) { - return err; - } - } - - if ((f->flags & LFS_F_WRITING) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->block, f->pos, cb, data); - if (err) { - return err; - } - } - } - - return 0; -} - -int lfs_fs_traverse(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data) { - LFS_TRACE("lfs_fs_traverse(%p, %p, %p)", - (void*)lfs, (void*)(uintptr_t)cb, data); - int err = lfs_fs_traverseraw(lfs, cb, data, true); - LFS_TRACE("lfs_fs_traverse -> %d", 0); - return err; -} - -static int lfs_fs_pred(lfs_t *lfs, - const lfs_block_t pair[2], lfs_mdir_t *pdir) { - // iterate over all directory directory entries - pdir->tail[0] = 0; - pdir->tail[1] = 1; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(pdir->tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - if (lfs_pair_cmp(pdir->tail, pair) == 0) { - return 0; - } - - int err = lfs_dir_fetch(lfs, pdir, pdir->tail); - if (err) { - return err; - } - } - - return LFS_ERR_NOENT; -} - -struct lfs_fs_parent_match { - lfs_t *lfs; - const lfs_block_t pair[2]; -}; - -static int lfs_fs_parent_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_fs_parent_match *find = data; - lfs_t *lfs = find->lfs; - const struct lfs_diskoff *disk = buffer; - (void)tag; - - lfs_block_t child[2]; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - disk->block, disk->off, &child, sizeof(child)); - if (err) { - return err; - } - - lfs_pair_fromle32(child); - return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; -} - -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], - lfs_mdir_t *parent) { - // use fetchmatch with callback to find pairs - parent->tail[0] = 0; - parent->tail[1] = 1; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(parent->tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, - LFS_MKTAG(0x7ff, 0, 0x3ff), - LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), - NULL, - lfs_fs_parent_match, &(struct lfs_fs_parent_match){ - lfs, {pair[0], pair[1]}}); - if (tag && tag != LFS_ERR_NOENT) { - return tag; - } - } - - return LFS_ERR_NOENT; -} - -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { - // update internal root - if (lfs_pair_cmp(oldpair, lfs->root) == 0) { - lfs->root[0] = newpair[0]; - lfs->root[1] = newpair[1]; - } - - // update internally tracked dirs - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(oldpair, d->m.pair) == 0) { - d->m.pair[0] = newpair[0]; - d->m.pair[1] = newpair[1]; - } - - if (d->type == LFS_TYPE_DIR && - lfs_pair_cmp(oldpair, ((lfs_dir_t*)d)->head) == 0) { - ((lfs_dir_t*)d)->head[0] = newpair[0]; - ((lfs_dir_t*)d)->head[1] = newpair[1]; - } - } - - // find parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, oldpair, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag != LFS_ERR_NOENT) { - // update disk, this creates a desync - lfs_fs_preporphans(lfs, +1); - - // fix pending move in this pair? this looks like an optimization but - // is in fact _required_ since relocating may outdate the move. - uint16_t moveid = 0x3ff; - if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { - moveid = lfs_tag_id(lfs->gstate.tag); - LFS_DEBUG("Fixing move while relocating " - "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", - parent.pair[0], parent.pair[1], moveid); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - if (moveid < lfs_tag_id(tag)) { - tag -= LFS_MKTAG(0, 1, 0); - } - } - - lfs_pair_tole32(newpair); - int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( - {LFS_MKTAG_IF(moveid != 0x3ff, - LFS_TYPE_DELETE, moveid, 0), NULL}, - {tag, newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - - // next step, clean up orphans - lfs_fs_preporphans(lfs, -1); - } - - // find pred - int err = lfs_fs_pred(lfs, oldpair, &parent); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - // if we can't find dir, it must be new - if (err != LFS_ERR_NOENT) { - // fix pending move in this pair? this looks like an optimization but - // is in fact _required_ since relocating may outdate the move. - uint16_t moveid = 0x3ff; - if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { - moveid = lfs_tag_id(lfs->gstate.tag); - LFS_DEBUG("Fixing move while relocating " - "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", - parent.pair[0], parent.pair[1], moveid); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - } - - // replace bad pair, either we clean up desync, or no desync occured - lfs_pair_tole32(newpair); - err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( - {LFS_MKTAG_IF(moveid != 0x3ff, - LFS_TYPE_DELETE, moveid, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - } - - return 0; -} - -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { - LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0 || orphans >= 0); - lfs->gstate.tag += orphans; - lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) | - ((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31)); -} - -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]) { - lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x7ff, 0x3ff, 0)) | - ((id != 0x3ff) ? LFS_MKTAG(LFS_TYPE_DELETE, id, 0) : 0)); - lfs->gstate.pair[0] = (id != 0x3ff) ? pair[0] : 0; - lfs->gstate.pair[1] = (id != 0x3ff) ? pair[1] : 0; -} - -static int lfs_fs_demove(lfs_t *lfs) { - if (!lfs_gstate_hasmove(&lfs->gdisk)) { - return 0; - } - - // Fix bad moves - LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16, - lfs->gdisk.pair[0], - lfs->gdisk.pair[1], - lfs_tag_id(lfs->gdisk.tag)); - - // fetch and delete the moved entry - lfs_mdir_t movedir; - int err = lfs_dir_fetch(lfs, &movedir, lfs->gdisk.pair); - if (err) { - return err; - } - - // prep gstate and delete move id - uint16_t moveid = lfs_tag_id(lfs->gdisk.tag); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0), NULL})); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_deorphan(lfs_t *lfs) { - if (!lfs_gstate_hasorphans(&lfs->gstate)) { - return 0; - } - - // Fix any orphans - lfs_mdir_t pdir = {.split = true, .tail = {0, 1}}; - lfs_mdir_t dir; - - // iterate over all directory directory entries - while (!lfs_pair_isnull(pdir.tail)) { - int err = lfs_dir_fetch(lfs, &dir, pdir.tail); - if (err) { - return err; - } - - // check head blocks for orphans - if (!pdir.split) { - // check if we have a parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag == LFS_ERR_NOENT) { - // we are an orphan - LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", - pdir.tail[0], pdir.tail[1]); - - err = lfs_dir_drop(lfs, &pdir, &dir); - if (err) { - return err; - } - - // refetch tail - continue; - } - - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &parent, - LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(pair); - - if (!lfs_pair_sync(pair, pdir.tail)) { - // we have desynced - LFS_DEBUG("Fixing half-orphan {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - pdir.tail[0], pdir.tail[1], pair[0], pair[1]); - - lfs_pair_tole32(pair); - err = lfs_dir_commit(lfs, &pdir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pair})); - lfs_pair_fromle32(pair); - if (err) { - return err; - } - - // refetch tail - continue; - } - } - - pdir = dir; - } - - // mark orphans as fixed - lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); - return 0; -} - -static int lfs_fs_forceconsistency(lfs_t *lfs) { - int err = lfs_fs_demove(lfs); - if (err) { - return err; - } - - err = lfs_fs_deorphan(lfs); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_size_count(void *p, lfs_block_t block) { - (void)block; - lfs_size_t *size = p; - *size += 1; - return 0; -} - -lfs_ssize_t lfs_fs_size(lfs_t *lfs) { - LFS_TRACE("lfs_fs_size(%p)", (void*)lfs); - lfs_size_t size = 0; - int err = lfs_fs_traverseraw(lfs, lfs_fs_size_count, &size, false); - if (err) { - LFS_TRACE("lfs_fs_size -> %d", err); - return err; - } - - LFS_TRACE("lfs_fs_size -> %d", err); - return size; -} - -#ifdef LFS_MIGRATE -////// Migration from littelfs v1 below this ////// - -/// Version info /// - -// Software library version -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_VERSION 0x00010007 -#define LFS1_VERSION_MAJOR (0xffff & (LFS1_VERSION >> 16)) -#define LFS1_VERSION_MINOR (0xffff & (LFS1_VERSION >> 0)) - -// Version of On-disk data structures -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_DISK_VERSION 0x00010001 -#define LFS1_DISK_VERSION_MAJOR (0xffff & (LFS1_DISK_VERSION >> 16)) -#define LFS1_DISK_VERSION_MINOR (0xffff & (LFS1_DISK_VERSION >> 0)) - - -/// v1 Definitions /// - -// File types -enum lfs1_type { - LFS1_TYPE_REG = 0x11, - LFS1_TYPE_DIR = 0x22, - LFS1_TYPE_SUPERBLOCK = 0x2e, -}; - -typedef struct lfs1 { - lfs_block_t root[2]; -} lfs1_t; - -typedef struct lfs1_entry { - lfs_off_t off; - - struct lfs1_disk_entry { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - union { - struct { - lfs_block_t head; - lfs_size_t size; - } file; - lfs_block_t dir[2]; - } u; - } d; -} lfs1_entry_t; - -typedef struct lfs1_dir { - struct lfs1_dir *next; - lfs_block_t pair[2]; - lfs_off_t off; - - lfs_block_t head[2]; - lfs_off_t pos; - - struct lfs1_disk_dir { - uint32_t rev; - lfs_size_t size; - lfs_block_t tail[2]; - } d; -} lfs1_dir_t; - -typedef struct lfs1_superblock { - lfs_off_t off; - - struct lfs1_disk_superblock { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - lfs_block_t root[2]; - uint32_t block_size; - uint32_t block_count; - uint32_t version; - char magic[8]; - } d; -} lfs1_superblock_t; - - -/// Low-level wrappers v1->v2 /// -static void lfs1_crc(uint32_t *crc, const void *buffer, size_t size) { - *crc = lfs_crc(*crc, buffer, size); -} - -static int lfs1_bd_read(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - // if we ever do more than writes to alternating pairs, - // this may need to consider pcache - return lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, size, - block, off, buffer, size); -} - -static int lfs1_bd_crc(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, lfs_size_t size, uint32_t *crc) { - for (lfs_off_t i = 0; i < size; i++) { - uint8_t c; - int err = lfs1_bd_read(lfs, block, off+i, &c, 1); - if (err) { - return err; - } - - lfs1_crc(crc, &c, 1); - } - - return 0; -} - - -/// Endian swapping functions /// -static void lfs1_dir_fromle32(struct lfs1_disk_dir *d) { - d->rev = lfs_fromle32(d->rev); - d->size = lfs_fromle32(d->size); - d->tail[0] = lfs_fromle32(d->tail[0]); - d->tail[1] = lfs_fromle32(d->tail[1]); -} - -static void lfs1_dir_tole32(struct lfs1_disk_dir *d) { - d->rev = lfs_tole32(d->rev); - d->size = lfs_tole32(d->size); - d->tail[0] = lfs_tole32(d->tail[0]); - d->tail[1] = lfs_tole32(d->tail[1]); -} - -static void lfs1_entry_fromle32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_fromle32(d->u.dir[0]); - d->u.dir[1] = lfs_fromle32(d->u.dir[1]); -} - -static void lfs1_entry_tole32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_tole32(d->u.dir[0]); - d->u.dir[1] = lfs_tole32(d->u.dir[1]); -} - -static void lfs1_superblock_fromle32(struct lfs1_disk_superblock *d) { - d->root[0] = lfs_fromle32(d->root[0]); - d->root[1] = lfs_fromle32(d->root[1]); - d->block_size = lfs_fromle32(d->block_size); - d->block_count = lfs_fromle32(d->block_count); - d->version = lfs_fromle32(d->version); -} - - -///// Metadata pair and directory operations /// -static inline lfs_size_t lfs1_entry_size(const lfs1_entry_t *entry) { - return 4 + entry->d.elen + entry->d.alen + entry->d.nlen; -} - -static int lfs1_dir_fetch(lfs_t *lfs, - lfs1_dir_t *dir, const lfs_block_t pair[2]) { - // copy out pair, otherwise may be aliasing dir - const lfs_block_t tpair[2] = {pair[0], pair[1]}; - bool valid = false; - - // check both blocks for the most recent revision - for (int i = 0; i < 2; i++) { - struct lfs1_disk_dir test; - int err = lfs1_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) { - continue; - } - - if ((0x7fffffff & test.size) < sizeof(test)+4 || - (0x7fffffff & test.size) > lfs->cfg->block_size) { - continue; - } - - uint32_t crc = 0xffffffff; - lfs1_dir_tole32(&test); - lfs1_crc(&crc, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - err = lfs1_bd_crc(lfs, tpair[i], sizeof(test), - (0x7fffffff & test.size) - sizeof(test), &crc); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (crc != 0) { - continue; - } - - valid = true; - - // setup dir in case it's valid - dir->pair[0] = tpair[(i+0) % 2]; - dir->pair[1] = tpair[(i+1) % 2]; - dir->off = sizeof(dir->d); - dir->d = test; - } - - if (!valid) { - LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", - tpair[0], tpair[1]); - return LFS_ERR_CORRUPT; - } - - return 0; -} - -static int lfs1_dir_next(lfs_t *lfs, lfs1_dir_t *dir, lfs1_entry_t *entry) { - while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { - if (!(0x80000000 & dir->d.size)) { - entry->off = dir->off; - return LFS_ERR_NOENT; - } - - int err = lfs1_dir_fetch(lfs, dir, dir->d.tail); - if (err) { - return err; - } - - dir->off = sizeof(dir->d); - dir->pos += sizeof(dir->d) + 4; - } - - int err = lfs1_bd_read(lfs, dir->pair[0], dir->off, - &entry->d, sizeof(entry->d)); - lfs1_entry_fromle32(&entry->d); - if (err) { - return err; - } - - entry->off = dir->off; - dir->off += lfs1_entry_size(entry); - dir->pos += lfs1_entry_size(entry); - return 0; -} - -/// littlefs v1 specific operations /// -int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // iterate over metadata pairs - lfs1_dir_t dir; - lfs1_entry_t entry; - lfs_block_t cwd[2] = {0, 1}; - - while (true) { - for (int i = 0; i < 2; i++) { - int err = cb(data, cwd[i]); - if (err) { - return err; - } - } - - int err = lfs1_dir_fetch(lfs, &dir, cwd); - if (err) { - return err; - } - - // iterate over contents - while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { - err = lfs1_bd_read(lfs, dir.pair[0], dir.off, - &entry.d, sizeof(entry.d)); - lfs1_entry_fromle32(&entry.d); - if (err) { - return err; - } - - dir.off += lfs1_entry_size(&entry); - if ((0x70 & entry.d.type) == (0x70 & LFS1_TYPE_REG)) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - entry.d.u.file.head, entry.d.u.file.size, cb, data); - if (err) { - return err; - } - } - } - - // we also need to check if we contain a threaded v2 directory - lfs_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}}; - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - break; - } - - for (int i = 0; i < 2; i++) { - err = cb(data, dir2.pair[i]); - if (err) { - return err; - } - } - } - - cwd[0] = dir.d.tail[0]; - cwd[1] = dir.d.tail[1]; - - if (lfs_pair_isnull(cwd)) { - break; - } - } - - return 0; -} - -static int lfs1_moved(lfs_t *lfs, const void *e) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // skip superblock - lfs1_dir_t cwd; - int err = lfs1_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); - if (err) { - return err; - } - - // iterate over all directory directory entries - lfs1_entry_t entry; - while (!lfs_pair_isnull(cwd.d.tail)) { - err = lfs1_dir_fetch(lfs, &cwd, cwd.d.tail); - if (err) { - return err; - } - - while (true) { - err = lfs1_dir_next(lfs, &cwd, &entry); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - if (!(0x80 & entry.d.type) && - memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { - return true; - } - } - } - - return false; -} - -/// Filesystem operations /// -static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, - const struct lfs_config *cfg) { - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - return err; - } - - lfs->lfs1 = lfs1; - lfs->lfs1->root[0] = LFS_BLOCK_NULL; - lfs->lfs1->root[1] = LFS_BLOCK_NULL; - - // setup free lookahead - lfs->free.off = 0; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // load superblock - lfs1_dir_t dir; - lfs1_superblock_t superblock; - err = lfs1_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); - if (err && err != LFS_ERR_CORRUPT) { - goto cleanup; - } - - if (!err) { - err = lfs1_bd_read(lfs, dir.pair[0], sizeof(dir.d), - &superblock.d, sizeof(superblock.d)); - lfs1_superblock_fromle32(&superblock.d); - if (err) { - goto cleanup; - } - - lfs->lfs1->root[0] = superblock.d.root[0]; - lfs->lfs1->root[1] = superblock.d.root[1]; - } - - if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { - LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}", - 0, 1); - err = LFS_ERR_CORRUPT; - goto cleanup; - } - - uint16_t major_version = (0xffff & (superblock.d.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); - if ((major_version != LFS1_DISK_VERSION_MAJOR || - minor_version > LFS1_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version v%d.%d", major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - return 0; - } - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs1_unmount(lfs_t *lfs) { - return lfs_deinit(lfs); -} - -/// v1 migration /// -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_migrate(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - struct lfs1 lfs1; - int err = lfs1_mount(lfs, &lfs1, cfg); - if (err) { - LFS_TRACE("lfs_migrate -> %d", err); - return err; - } - - { - // iterate through each directory, copying over entries - // into new directory - lfs1_dir_t dir1; - lfs_mdir_t dir2; - dir1.d.tail[0] = lfs->lfs1->root[0]; - dir1.d.tail[1] = lfs->lfs1->root[1]; - while (!lfs_pair_isnull(dir1.d.tail)) { - // iterate old dir - err = lfs1_dir_fetch(lfs, &dir1, dir1.d.tail); - if (err) { - goto cleanup; - } - - // create new dir and bind as temporary pretend root - err = lfs_dir_alloc(lfs, &dir2); - if (err) { - goto cleanup; - } - - dir2.rev = dir1.d.rev; - dir1.head[0] = dir1.pair[0]; - dir1.head[1] = dir1.pair[1]; - lfs->root[0] = dir2.pair[0]; - lfs->root[1] = dir2.pair[1]; - - err = lfs_dir_commit(lfs, &dir2, NULL, 0); - if (err) { - goto cleanup; - } - - while (true) { - lfs1_entry_t entry1; - err = lfs1_dir_next(lfs, &dir1, &entry1); - if (err && err != LFS_ERR_NOENT) { - goto cleanup; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - // check that entry has not been moved - if (entry1.d.type & 0x80) { - int moved = lfs1_moved(lfs, &entry1.d.u); - if (moved < 0) { - err = moved; - goto cleanup; - } - - if (moved) { - continue; - } - - entry1.d.type &= ~0x80; - } - - // also fetch name - char name[LFS_NAME_MAX+1]; - memset(name, 0, sizeof(name)); - err = lfs1_bd_read(lfs, dir1.pair[0], - entry1.off + 4+entry1.d.elen+entry1.d.alen, - name, entry1.d.nlen); - if (err) { - goto cleanup; - } - - bool isdir = (entry1.d.type == LFS1_TYPE_DIR); - - // create entry in new dir - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - uint16_t id; - err = lfs_dir_find(lfs, &dir2, &(const char*){name}, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - err = (err < 0) ? err : LFS_ERR_EXIST; - goto cleanup; - } - - lfs1_entry_tole32(&entry1.d); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0)}, - {LFS_MKTAG_IF_ELSE(isdir, - LFS_TYPE_DIR, id, entry1.d.nlen, - LFS_TYPE_REG, id, entry1.d.nlen), - name}, - {LFS_MKTAG_IF_ELSE(isdir, - LFS_TYPE_DIRSTRUCT, id, sizeof(entry1.d.u), - LFS_TYPE_CTZSTRUCT, id, sizeof(entry1.d.u)), - &entry1.d.u})); - lfs1_entry_fromle32(&entry1.d); - if (err) { - goto cleanup; - } - } - - if (!lfs_pair_isnull(dir1.d.tail)) { - // find last block and update tail to thread into fs - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - goto cleanup; - } - } - - lfs_pair_tole32(dir2.pair); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir1.d.tail})); - lfs_pair_fromle32(dir2.pair); - if (err) { - goto cleanup; - } - } - - // Copy over first block to thread into fs. Unfortunately - // if this fails there is not much we can do. - LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); - - err = lfs_bd_erase(lfs, dir1.head[1]); - if (err) { - goto cleanup; - } - - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - for (lfs_off_t i = 0; i < dir2.off; i++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dir2.off, - dir2.pair[0], i, &dat, 1); - if (err) { - goto cleanup; - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - dir1.head[1], i, &dat, 1); - if (err) { - goto cleanup; - } - } - - err = lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, true); - if (err) { - goto cleanup; - } - } - - // Create new superblock. This marks a successful migration! - err = lfs1_dir_fetch(lfs, &dir1, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - dir2.pair[0] = dir1.pair[0]; - dir2.pair[1] = dir1.pair[1]; - dir2.rev = dir1.d.rev; - dir2.off = sizeof(dir2.rev); - dir2.etag = 0xffffffff; - dir2.count = 0; - dir2.tail[0] = lfs->lfs1->root[0]; - dir2.tail[1] = lfs->lfs1->root[1]; - dir2.erased = false; - dir2.split = true; - - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0)}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &dir2, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - // force compaction to prevent accidentally mounting v1 - dir2.erased = false; - err = lfs_dir_commit(lfs, &dir2, NULL, 0); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs1_unmount(lfs); - LFS_TRACE("lfs_migrate -> %d", err); - return err; -} - -#endif diff --git a/lib/LITTLEFS/src/lfs.h b/lib/LITTLEFS/src/lfs.h deleted file mode 100644 index 35bbbabf..00000000 --- a/lib/LITTLEFS/src/lfs.h +++ /dev/null @@ -1,655 +0,0 @@ -/* - * The little filesystem - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef LFS_H -#define LFS_H - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - -/// Version info /// - -// Software library version -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS_VERSION 0x00020002 -#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) -#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) - -// Version of On-disk data structures -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS_DISK_VERSION 0x00020000 -#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) -#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) - - -/// Definitions /// - -// Type definitions -typedef uint32_t lfs_size_t; -typedef uint32_t lfs_off_t; - -typedef int32_t lfs_ssize_t; -typedef int32_t lfs_soff_t; - -typedef uint32_t lfs_block_t; - -// Maximum name size in bytes, may be redefined to reduce the size of the -// info struct. Limited to <= 1022. Stored in superblock and must be -// respected by other littlefs drivers. -#ifndef LFS_NAME_MAX -#define LFS_NAME_MAX 255 -#endif - -// Maximum size of a file in bytes, may be redefined to limit to support other -// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return -// incorrect values due to using signed integers. Stored in superblock and -// must be respected by other littlefs drivers. -#ifndef LFS_FILE_MAX -#define LFS_FILE_MAX 2147483647 -#endif - -// Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. -#ifndef LFS_ATTR_MAX -#define LFS_ATTR_MAX 1022 -#endif - -// Possible error codes, these are negative to allow -// valid positive return values -enum lfs_error { - LFS_ERR_OK = 0, // No error - LFS_ERR_IO = -5, // Error during device operation - LFS_ERR_CORRUPT = -84, // Corrupted - LFS_ERR_NOENT = -2, // No directory entry - LFS_ERR_EXIST = -17, // Entry already exists - LFS_ERR_NOTDIR = -20, // Entry is not a dir - LFS_ERR_ISDIR = -21, // Entry is a dir - LFS_ERR_NOTEMPTY = -39, // Dir is not empty - LFS_ERR_BADF = -9, // Bad file number - LFS_ERR_FBIG = -27, // File too large - LFS_ERR_INVAL = -22, // Invalid parameter - LFS_ERR_NOSPC = -28, // No space left on device - LFS_ERR_NOMEM = -12, // No more memory available - LFS_ERR_NOATTR = -61, // No data/attr available - LFS_ERR_NAMETOOLONG = -36, // File name too long -}; - -// File types -enum lfs_type { - // file types - LFS_TYPE_REG = 0x001, - LFS_TYPE_DIR = 0x002, - - // internally used types - LFS_TYPE_SPLICE = 0x400, - LFS_TYPE_NAME = 0x000, - LFS_TYPE_STRUCT = 0x200, - LFS_TYPE_USERATTR = 0x300, - LFS_TYPE_FROM = 0x100, - LFS_TYPE_TAIL = 0x600, - LFS_TYPE_GLOBALS = 0x700, - LFS_TYPE_CRC = 0x500, - - // internally used type specializations - LFS_TYPE_CREATE = 0x401, - LFS_TYPE_DELETE = 0x4ff, - LFS_TYPE_SUPERBLOCK = 0x0ff, - LFS_TYPE_DIRSTRUCT = 0x200, - LFS_TYPE_CTZSTRUCT = 0x202, - LFS_TYPE_INLINESTRUCT = 0x201, - LFS_TYPE_SOFTTAIL = 0x600, - LFS_TYPE_HARDTAIL = 0x601, - LFS_TYPE_MOVESTATE = 0x7ff, - - // internal chip sources - LFS_FROM_NOOP = 0x000, - LFS_FROM_MOVE = 0x101, - LFS_FROM_USERATTRS = 0x102, -}; - -// File open flags -enum lfs_open_flags { - // open flags - LFS_O_RDONLY = 1, // Open a file as read only - LFS_O_WRONLY = 2, // Open a file as write only - LFS_O_RDWR = 3, // Open a file as read and write - LFS_O_CREAT = 0x0100, // Create a file if it does not exist - LFS_O_EXCL = 0x0200, // Fail if a file already exists - LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size - LFS_O_APPEND = 0x0800, // Move to end of file on every write - - // internally used flags - LFS_F_DIRTY = 0x010000, // File does not match storage - LFS_F_WRITING = 0x020000, // File has been written since last flush - LFS_F_READING = 0x040000, // File has been read since last flush - LFS_F_ERRED = 0x080000, // An error occured during write - LFS_F_INLINE = 0x100000, // Currently inlined in directory entry - LFS_F_OPENED = 0x200000, // File has been opened -}; - -// File seek flags -enum lfs_whence_flags { - LFS_SEEK_SET = 0, // Seek relative to an absolute position - LFS_SEEK_CUR = 1, // Seek relative to the current file position - LFS_SEEK_END = 2, // Seek relative to the end of the file -}; - - -// Configuration provided during initialization of the littlefs -struct lfs_config { - // Opaque user provided context that can be used to pass - // information to the block device operations - void *context; - - // Read a region in a block. Negative error codes are propogated - // to the user. - int (*read)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); - - // Program a region in a block. The block must have previously - // been erased. Negative error codes are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*prog)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); - - // Erase a block. A block must be erased before being programmed. - // The state of an erased block is undefined. Negative error codes - // are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*erase)(const struct lfs_config *c, lfs_block_t block); - - // Sync the state of the underlying block device. Negative error codes - // are propogated to the user. - int (*sync)(const struct lfs_config *c); - - // Minimum size of a block read. All read operations will be a - // multiple of this value. - lfs_size_t read_size; - - // Minimum size of a block program. All program operations will be a - // multiple of this value. - lfs_size_t prog_size; - - // Size of an erasable block. This does not impact ram consumption and - // may be larger than the physical erase size. However, non-inlined files - // take up at minimum one block. Must be a multiple of the read - // and program sizes. - lfs_size_t block_size; - - // Number of erasable blocks on the device. - lfs_size_t block_count; - - // Number of erase cycles before littlefs evicts metadata logs and moves - // the metadata to another block. Suggested values are in the - // range 100-1000, with large values having better performance at the cost - // of less consistent wear distribution. - // - // Set to -1 to disable block-level wear-leveling. - int32_t block_cycles; - - // Size of block caches. Each cache buffers a portion of a block in RAM. - // The littlefs needs a read cache, a program cache, and one additional - // cache per file. Larger caches can improve performance by storing more - // data and reducing the number of disk accesses. Must be a multiple of - // the read and program sizes, and a factor of the block size. - lfs_size_t cache_size; - - // Size of the lookahead buffer in bytes. A larger lookahead buffer - // increases the number of blocks found during an allocation pass. The - // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 8. - lfs_size_t lookahead_size; - - // Optional statically allocated read buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *read_buffer; - - // Optional statically allocated program buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *prog_buffer; - - // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 32-bit boundary. By default lfs_malloc is used to - // allocate this buffer. - void *lookahead_buffer; - - // Optional upper limit on length of file names in bytes. No downside for - // larger names except the size of the info struct which is controlled by - // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in - // superblock and must be respected by other littlefs drivers. - lfs_size_t name_max; - - // Optional upper limit on files in bytes. No downside for larger files - // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored - // in superblock and must be respected by other littlefs drivers. - lfs_size_t file_max; - - // Optional upper limit on custom attributes in bytes. No downside for - // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to - // LFS_ATTR_MAX when zero. - lfs_size_t attr_max; -}; - -// File info structure -struct lfs_info { - // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR - uint8_t type; - - // Size of the file, only valid for REG files. Limited to 32-bits. - lfs_size_t size; - - // Name of the file stored as a null-terminated string. Limited to - // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to - // reduce RAM. LFS_NAME_MAX is stored in superblock and must be - // respected by other littlefs drivers. - char name[LFS_NAME_MAX+1]; -}; - -// Custom attribute structure, used to describe custom attributes -// committed atomically during file writes. -struct lfs_attr { - // 8-bit type of attribute, provided by user and used to - // identify the attribute - uint8_t type; - - // Pointer to buffer containing the attribute - void *buffer; - - // Size of attribute in bytes, limited to LFS_ATTR_MAX - lfs_size_t size; -}; - -// Optional configuration provided during lfs_file_opencfg -struct lfs_file_config { - // Optional statically allocated file buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *buffer; - - // Optional list of custom attributes related to the file. If the file - // is opened with read access, these attributes will be read from disk - // during the open call. If the file is opened with write access, the - // attributes will be written to disk every file sync or close. This - // write occurs atomically with update to the file's contents. - // - // Custom attributes are uniquely identified by an 8-bit type and limited - // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller - // than the buffer, it will be padded with zeros. If the stored attribute - // is larger, then it will be silently truncated. If the attribute is not - // found, it will be created implicitly. - struct lfs_attr *attrs; - - // Number of custom attributes in the list - lfs_size_t attr_count; -}; - - -/// internal littlefs data structures /// -typedef struct lfs_cache { - lfs_block_t block; - lfs_off_t off; - lfs_size_t size; - uint8_t *buffer; -} lfs_cache_t; - -typedef struct lfs_mdir { - lfs_block_t pair[2]; - uint32_t rev; - lfs_off_t off; - uint32_t etag; - uint16_t count; - bool erased; - bool split; - lfs_block_t tail[2]; -} lfs_mdir_t; - -// littlefs directory type -typedef struct lfs_dir { - struct lfs_dir *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - - lfs_off_t pos; - lfs_block_t head[2]; -} lfs_dir_t; - -// littlefs file type -typedef struct lfs_file { - struct lfs_file *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - - struct lfs_ctz { - lfs_block_t head; - lfs_size_t size; - } ctz; - - uint32_t flags; - lfs_off_t pos; - lfs_block_t block; - lfs_off_t off; - lfs_cache_t cache; - - const struct lfs_file_config *cfg; -} lfs_file_t; - -typedef struct lfs_superblock { - uint32_t version; - lfs_size_t block_size; - lfs_size_t block_count; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; -} lfs_superblock_t; - -typedef struct lfs_gstate { - uint32_t tag; - lfs_block_t pair[2]; -} lfs_gstate_t; - -// The littlefs filesystem type -typedef struct lfs { - lfs_cache_t rcache; - lfs_cache_t pcache; - - lfs_block_t root[2]; - struct lfs_mlist { - struct lfs_mlist *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - } *mlist; - uint32_t seed; - - lfs_gstate_t gstate; - lfs_gstate_t gdisk; - lfs_gstate_t gdelta; - - struct lfs_free { - lfs_block_t off; - lfs_block_t size; - lfs_block_t i; - lfs_block_t ack; - uint32_t *buffer; - } free; - - const struct lfs_config *cfg; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; - -#ifdef LFS_MIGRATE - struct lfs1 *lfs1; -#endif -} lfs_t; - - -/// Filesystem functions /// - -// Format a block device with the littlefs -// -// Requires a littlefs object and config struct. This clobbers the littlefs -// object, and does not leave the filesystem mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_format(lfs_t *lfs, const struct lfs_config *config); - -// Mounts a littlefs -// -// Requires a littlefs object and config struct. Multiple filesystems -// may be mounted simultaneously with multiple littlefs objects. Both -// lfs and config must be allocated while mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_mount(lfs_t *lfs, const struct lfs_config *config); - -// Unmounts a littlefs -// -// Does nothing besides releasing any allocated resources. -// Returns a negative error code on failure. -int lfs_unmount(lfs_t *lfs); - -/// General operations /// - -// Removes a file or directory -// -// If removing a directory, the directory must be empty. -// Returns a negative error code on failure. -int lfs_remove(lfs_t *lfs, const char *path); - -// Rename or move a file or directory -// -// If the destination exists, it must match the source in type. -// If the destination is a directory, the directory must be empty. -// -// Returns a negative error code on failure. -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); - -// Find info about a file or directory -// -// Fills out the info structure, based on the specified file or directory. -// Returns a negative error code on failure. -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); - -// Get a custom attribute -// -// Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than -// the buffer, it will be padded with zeros. If the stored attribute is larger, -// then it will be silently truncated. If no attribute is found, the error -// LFS_ERR_NOATTR is returned and the buffer is filled with zeros. -// -// Returns the size of the attribute, or a negative error code on failure. -// Note, the returned size is the size of the attribute on disk, irrespective -// of the size of the buffer. This can be used to dynamically allocate a buffer -// or check for existance. -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size); - -// Set custom attributes -// -// Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be -// implicitly created. -// -// Returns a negative error code on failure. -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size); - -// Removes a custom attribute -// -// If an attribute is not found, nothing happens. -// -// Returns a negative error code on failure. -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); - - -/// File operations /// - -// Open a file -// -// The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. -// -// Returns a negative error code on failure. -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags); - -// Open a file with extra configuration -// -// The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. -// -// The config struct provides additional config options per file as described -// above. The config struct must be allocated while the file is open, and the -// config struct must be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags, - const struct lfs_file_config *config); - -// Close a file -// -// Any pending writes are written out to storage as though -// sync had been called and releases any allocated resources. -// -// Returns a negative error code on failure. -int lfs_file_close(lfs_t *lfs, lfs_file_t *file); - -// Synchronize a file on storage -// -// Any pending writes are written out to storage. -// Returns a negative error code on failure. -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); - -// Read data from file -// -// Takes a buffer and size indicating where to store the read data. -// Returns the number of bytes read, or a negative error code on failure. -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size); - -// Write data to file -// -// Takes a buffer and size indicating the data to write. The file will not -// actually be updated on the storage until either sync or close is called. -// -// Returns the number of bytes written, or a negative error code on failure. -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size); - -// Change the position of the file -// -// The change in position is determined by the offset and whence flag. -// Returns the new position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence); - -// Truncates the size of the file to the specified size -// -// Returns a negative error code on failure. -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); - -// Return the position of the file -// -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) -// Returns the position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); - -// Change the position of the file to the beginning of the file -// -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET) -// Returns a negative error code on failure. -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); - -// Return the size of the file -// -// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END) -// Returns the size of the file, or a negative error code on failure. -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); - - -/// Directory operations /// - -// Create a directory -// -// Returns a negative error code on failure. -int lfs_mkdir(lfs_t *lfs, const char *path); - -// Open a directory -// -// Once open a directory can be used with read to iterate over files. -// Returns a negative error code on failure. -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); - -// Close a directory -// -// Releases any allocated resources. -// Returns a negative error code on failure. -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); - -// Read an entry in the directory -// -// Fills out the info structure, based on the specified file or directory. -// Returns a positive value on success, 0 at the end of directory, -// or a negative error code on failure. -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); - -// Change the position of the directory -// -// The new off must be a value previous returned from tell and specifies -// an absolute offset in the directory seek. -// -// Returns a negative error code on failure. -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); - -// Return the position of the directory -// -// The returned offset is only meant to be consumed by seek and may not make -// sense, but does indicate the current position in the directory iteration. -// -// Returns the position of the directory, or a negative error code on failure. -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); - -// Change the position of the directory to the beginning of the directory -// -// Returns a negative error code on failure. -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); - - -/// Filesystem-level filesystem operations - -// Finds the current size of the filesystem -// -// Note: Result is best effort. If files share COW structures, the returned -// size may be larger than the filesystem actually is. -// -// Returns the number of allocated blocks, or a negative error code on failure. -lfs_ssize_t lfs_fs_size(lfs_t *lfs); - -// Traverse through all blocks in use by the filesystem -// -// The provided callback will be called with each block address that is -// currently in use by the filesystem. This can be used to determine which -// blocks are in use or how much of the storage is available. -// -// Returns a negative error code on failure. -int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); - -#ifdef LFS_MIGRATE -// Attempts to migrate a previous version of littlefs -// -// Behaves similarly to the lfs_format function. Attempts to mount -// the previous version of littlefs and update the filesystem so it can be -// mounted with the current version of littlefs. -// -// Requires a littlefs object and config struct. This clobbers the littlefs -// object, and does not leave the filesystem mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); -#endif - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/lib/LITTLEFS/src/lfs_util.c b/lib/LITTLEFS/src/lfs_util.c deleted file mode 100644 index 0b60e3b4..00000000 --- a/lib/LITTLEFS/src/lfs_util.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * lfs util functions - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "lfs_util.h" - -// Only compile if user does not provide custom config -#ifndef LFS_CONFIG - - -// Software CRC implementation with small lookup table -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { - static const uint32_t rtable[16] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, - }; - - const uint8_t *data = buffer; - - for (size_t i = 0; i < size; i++) { - crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; - crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; - } - - return crc; -} - - -#endif diff --git a/lib/LITTLEFS/src/lfs_util.h b/lib/LITTLEFS/src/lfs_util.h deleted file mode 100644 index dbb4c5ba..00000000 --- a/lib/LITTLEFS/src/lfs_util.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * lfs utility functions - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef LFS_UTIL_H -#define LFS_UTIL_H - -// Users can override lfs_util.h with their own configuration by defining -// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). -// -// If LFS_CONFIG is used, none of the default utils will be emitted and must be -// provided by the config file. To start, I would suggest copying lfs_util.h -// and modifying as needed. -#ifdef LFS_CONFIG -#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) -#define LFS_STRINGIZE2(x) #x -#include LFS_STRINGIZE(LFS_CONFIG) -#else - -// System includes -#include -#include -#include -#include - -#ifndef LFS_NO_MALLOC -#include -#endif -#ifndef LFS_NO_ASSERT -#include -#endif -#if !defined(LFS_NO_DEBUG) || \ - !defined(LFS_NO_WARN) || \ - !defined(LFS_NO_ERROR) || \ - defined(LFS_YES_TRACE) -#include -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - - -// Macros, may be replaced by system specific wrappers. Arguments to these -// macros must not have side-effects as the macros can be removed for a smaller -// code footprint - -// Logging functions -#ifdef LFS_YES_TRACE -#define LFS_TRACE_(fmt, ...) \ - printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") -#else -#define LFS_TRACE(...) -#endif - -#ifndef LFS_NO_DEBUG -#define LFS_DEBUG_(fmt, ...) \ - printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") -#else -#define LFS_DEBUG(...) -#endif - -#ifndef LFS_NO_WARN -#define LFS_WARN_(fmt, ...) \ - printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") -#else -#define LFS_WARN(...) -#endif - -#ifndef LFS_NO_ERROR -#define LFS_ERROR_(fmt, ...) \ - printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") -#else -#define LFS_ERROR(...) -#endif - -// Runtime assertions -#ifndef LFS_NO_ASSERT -#define LFS_ASSERT(test) assert(test) -#else -#define LFS_ASSERT(test) -#endif - - -// Builtin functions, these may be replaced by more efficient -// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more -// expensive basic C implementation for debugging purposes - -// Min/max functions for unsigned 32-bit numbers -static inline uint32_t lfs_max(uint32_t a, uint32_t b) { - return (a > b) ? a : b; -} - -static inline uint32_t lfs_min(uint32_t a, uint32_t b) { - return (a < b) ? a : b; -} - -// Align to nearest multiple of a size -static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { - return a - (a % alignment); -} - -static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { - return lfs_aligndown(a + alignment-1, alignment); -} - -// Find the smallest power of 2 greater than or equal to a -static inline uint32_t lfs_npw2(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return 32 - __builtin_clz(a-1); -#else - uint32_t r = 0; - uint32_t s; - a -= 1; - s = (a > 0xffff) << 4; a >>= s; r |= s; - s = (a > 0xff ) << 3; a >>= s; r |= s; - s = (a > 0xf ) << 2; a >>= s; r |= s; - s = (a > 0x3 ) << 1; a >>= s; r |= s; - return (r | (a >> 1)) + 1; -#endif -} - -// Count the number of trailing binary zeros in a -// lfs_ctz(0) may be undefined -static inline uint32_t lfs_ctz(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) - return __builtin_ctz(a); -#else - return lfs_npw2((a & -a) + 1) - 1; -#endif -} - -// Count the number of binary ones in a -static inline uint32_t lfs_popc(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return __builtin_popcount(a); -#else - a = a - ((a >> 1) & 0x55555555); - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); - return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; -#endif -} - -// Find the sequence comparison of a and b, this is the distance -// between a and b ignoring overflow -static inline int lfs_scmp(uint32_t a, uint32_t b) { - return (int)(unsigned)(a - b); -} - -// Convert between 32-bit little-endian and native order -static inline uint32_t lfs_fromle32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return __builtin_bswap32(a); -#else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | - (((uint8_t*)&a)[3] << 24); -#endif -} - -static inline uint32_t lfs_tole32(uint32_t a) { - return lfs_fromle32(a); -} - -// Convert between 32-bit big-endian and native order -static inline uint32_t lfs_frombe32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return __builtin_bswap32(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return a; -#else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); -#endif -} - -static inline uint32_t lfs_tobe32(uint32_t a) { - return lfs_frombe32(a); -} - -// Calculate CRC-32 with polynomial = 0x04c11db7 -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); - -// Allocate memory, only used if buffers are not provided to littlefs -// Note, memory must be 64-bit aligned -static inline void *lfs_malloc(size_t size) { -#ifndef LFS_NO_MALLOC - return malloc(size); -#else - (void)size; - return NULL; -#endif -} - -// Deallocate memory, only used if buffers are not provided to littlefs -static inline void lfs_free(void *p) { -#ifndef LFS_NO_MALLOC - free(p); -#else - (void)p; -#endif -} - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif -#endif diff --git a/lib/LITTLEFS/src/littlefs_api.c b/lib/LITTLEFS/src/littlefs_api.c deleted file mode 100644 index d823b487..00000000 --- a/lib/LITTLEFS/src/littlefs_api.c +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @file littlefs_api.c - * @brief Maps the HAL of esp_partition <-> littlefs - * @author Brian Pugh - * - * Copyright 2020 Brian Pugh - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#define ESP_LOCAL_LOG_LEVEL ESP_LOG_INFO - -#include "esp_log.h" -#include "esp_partition.h" -#include "esp_vfs.h" -#include "lfs.h" -#include "esp_littlefs.h" -#include "littlefs_api.h" - -static const char TAG[] = "esp_littlefs_api"; - -int littlefs_api_read(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - esp_littlefs_t * efs = c->context; - size_t part_off = (block * c->block_size) + off; - esp_err_t err = esp_partition_read(efs->partition, part_off, buffer, size); - if (err) { - ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", part_off, size, err); - return LFS_ERR_IO; - } - return 0; -} - -int littlefs_api_prog(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size) { - esp_littlefs_t * efs = c->context; - size_t part_off = (block * c->block_size) + off; - esp_err_t err = esp_partition_write(efs->partition, part_off, buffer, size); - if (err) { - ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", part_off, size, err); - return LFS_ERR_IO; - } - return 0; -} - -int littlefs_api_erase(const struct lfs_config *c, lfs_block_t block) { - esp_littlefs_t * efs = c->context; - size_t part_off = block * c->block_size; - esp_err_t err = esp_partition_erase_range(efs->partition, part_off, c->block_size); - if (err) { - ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", part_off, c->block_size, err); - return LFS_ERR_IO; - } - return 0; - -} - -int littlefs_api_sync(const struct lfs_config *c) { - /* Unnecessary for esp-idf */ - return 0; -} - diff --git a/lib/LITTLEFS/src/littlefs_api.h b/lib/LITTLEFS/src/littlefs_api.h deleted file mode 100644 index c1b2adc9..00000000 --- a/lib/LITTLEFS/src/littlefs_api.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef ESP_LITTLEFS_API_H__ -#define ESP_LITTLEFS_API_H__ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_vfs.h" -#include "esp_partition.h" -#include "lfs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief a file descriptor - * That's also a singly linked list used for keeping tracks of all opened file descriptor - * - * Shortcomings/potential issues of 32-bit hash (when CONFIG_LITTLEFS_USE_ONLY_HASH) listed here: - * * unlink - If a different file is open that generates a hash collision, it will report an - * error that it cannot unlink an open file. - * * rename - If a different file is open that generates a hash collision with - * src or dst, it will report an error that it cannot rename an open file. - * Potential consequences: - * 1. A file cannot be deleted while a collision-geneating file is open. - * Worst-case, if the other file is always open during the lifecycle - * of your app, it's collision file cannot be deleted, which in the - * worst-case could cause storage-capacity issues. - * 2. Same as (1), but for renames - */ -typedef struct _vfs_littlefs_file_t { - lfs_file_t file; - uint32_t hash; - struct _vfs_littlefs_file_t * next; /*!< Pointer to next file in Singly Linked List */ -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - char * path; -#endif -} vfs_littlefs_file_t; - -/** - * @brief littlefs definition structure - */ -typedef struct { - lfs_t *fs; /*!< Handle to the underlying littlefs */ - SemaphoreHandle_t lock; /*!< FS lock */ - const esp_partition_t* partition; /*!< The partition on which littlefs is located */ - char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */ - - struct lfs_config cfg; /*!< littlefs Mount configuration */ - - vfs_littlefs_file_t *file; /*!< Singly Linked List of files */ - - vfs_littlefs_file_t **cache; /*!< A cache of pointers to the opened files */ - uint16_t cache_size; /*!< The cache allocated size (in pointers) */ - uint16_t fd_count; /*!< The count of opened file descriptor used to speed up computation */ -} esp_littlefs_t; - -/** - * @brief Read a region in a block. - * - * Negative error codes are propogated to the user. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_read(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); - -/** - * @brief Program a region in a block. - * - * The block must have previously been erased. - * Negative error codes are propogated to the user. - * May return LFS_ERR_CORRUPT if the block should be considered bad. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_prog(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); - -/** - * @brief Erase a block. - * - * A block must be erased before being programmed. - * The state of an erased block is undefined. - * Negative error codes are propogated to the user. - * May return LFS_ERR_CORRUPT if the block should be considered bad. - * @return errorcode. 0 on success. - */ -int littlefs_api_erase(const struct lfs_config *c, lfs_block_t block); - -/** - * @brief Sync the state of the underlying block device. - * - * Negative error codes are propogated to the user. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_sync(const struct lfs_config *c); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/platformio.ini b/platformio.ini index 9135ebc3..20165fdf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,6 +39,8 @@ board = nodemcuv2 ;board = esp01_1m ;board = esp12e board_build.ldscript = eagle.flash.1m512.ld +;board_build.filesystem.spiffs_blocksize = 4096 +board_build.spiffs_blocksize = 4096 platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} From e60d5eb62fbfa139d45b596266427cec18770801 Mon Sep 17 00:00:00 2001 From: Yuri Trikoz Date: Sat, 19 Dec 2020 20:12:31 +0300 Subject: [PATCH 64/94] mqtt reserve #2 --- platformio.ini | 24 +++++----- src/MqttClient.cpp | 108 ++++++++++++++++++++++++--------------------- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/platformio.ini b/platformio.ini index 20165fdf..3d8bb9ec 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,7 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = esp8266_01_1m -;============================================================================================================================================= +default_envs = esp8266 + [common_env_data] lib_deps_external = bblanchon/ArduinoJson @5.* @@ -15,7 +24,7 @@ lib_deps_internal = ESP Async WebServer GyverFilters OneWire -;============================================================================================================================================= + [env:esp32] framework = arduino board = esp32dev @@ -32,15 +41,11 @@ upload_speed = 921600 monitor_speed = 115200 board_build.filesystem = littlefs extra_scripts = ./tools/littlefsbuilder.py -;============================================================================================================================================= + [env:esp8266_01_1m] framework = arduino board = nodemcuv2 -;board = esp01_1m -;board = esp12e board_build.ldscript = eagle.flash.1m512.ld -;board_build.filesystem.spiffs_blocksize = 4096 -board_build.spiffs_blocksize = 4096 platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} @@ -53,7 +58,7 @@ monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 board_build.filesystem = littlefs -;============================================================================================================================================= + [env:esp8266] framework = arduino board = nodemcuv2 @@ -70,4 +75,3 @@ monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 board_build.filesystem = littlefs -;============================================================================================================================================= diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 62aab647..f76ff552 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -6,7 +6,8 @@ #include "Global.h" #include "Init.h" -enum MqttBroker {MQTT_PRIMARY, MQTT_RESERVE}; +enum MqttBroker { MQTT_PRIMARY, + MQTT_RESERVE }; MqttBroker activeBroker = MQTT_PRIMARY; String mqttPrefix; @@ -16,14 +17,14 @@ String mqttServer; String mqttUser; uint16_t mqttPort{0}; uint16_t reconnectionCounter{0}; -bool primaryExist = false; +uint16_t fallbackCounter{0}; const String getParamName(const char* param, MqttBroker broker) { - return String("mqtt") + param + (broker == MQTT_RESERVE? "2": ""); + return String("mqtt") + param + (broker == MQTT_RESERVE ? "2" : ""); } bool checkBrokerParams(MqttBroker broker) { - return !jsonReadStr(configSetupJson, getParamName("Server", broker)).isEmpty(); + return !jsonReadStr(configSetupJson, getParamName("Server", broker)).isEmpty(); } void mqttInit() { @@ -41,15 +42,25 @@ void mqttInit() { if (WiFi.status() == WL_CONNECTED) { SerialPrint("I", "WIFI", "OK"); if (mqtt.connected()) { - SerialPrint("I", "MQTT", "OK"); - setLedStatus(LED_OFF); - } - else { + if (activeBroker == MQTT_RESERVE) { + // при 20 cекундных интервалах проверки, каждые 100 сек + if (fallbackCounter++ > 5) { + if (checkBrokerParams(MQTT_PRIMARY)) { + activeBroker = MQTT_PRIMARY; + fallbackCounter = 0; + mqttReconnect(); + } + } + } else { + SerialPrint("I", "MQTT", "OK"); + setLedStatus(LED_OFF); + } + } else { SerialPrint("E", "MQTT", "lost connection"); if (reconnectionCounter++ > 5) { if (activeBroker == MQTT_PRIMARY) { if (checkBrokerParams(MQTT_RESERVE)) { - activeBroker = MQTT_RESERVE; + activeBroker = MQTT_RESERVE; } } else { activeBroker = MQTT_PRIMARY; @@ -58,8 +69,7 @@ void mqttInit() { } mqttConnect(); } - } - else { + } else { SerialPrint("E", "WIFI", "Lost WiFi connection"); ts.remove(WIFI_MQTT_CONNECTION_CHECK); startAPMode(); @@ -105,7 +115,7 @@ void mqttSubscribe() { } bool readBrokerParams(MqttBroker broker) { - if(!checkBrokerParams(broker)) { + if (!checkBrokerParams(broker)) { return false; } mqttServer = jsonReadStr(configSetupJson, getParamName("Server", broker)); @@ -117,7 +127,7 @@ bool readBrokerParams(MqttBroker broker) { } boolean mqttConnect() { - SerialPrint("I", "MQTT", String("use ") + (activeBroker == MQTT_PRIMARY? "primary": "reserve")); + SerialPrint("I", "MQTT", String("use ") + (activeBroker == MQTT_PRIMARY ? "primary" : "reserve")); if (!checkBrokerParams(activeBroker)) { SerialPrint("E", "MQTT", "empty broker address"); return false; @@ -137,8 +147,7 @@ boolean mqttConnect() { setLedStatus(LED_OFF); mqttSubscribe(); res = true; - } - else { + } else { SerialPrint("E", "MQTT", "could't connect, retry in " + String(MQTT_RECONNECT_INTERVAL / 1000) + "s"); setLedStatus(LED_FAST); } @@ -166,10 +175,9 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { } else if (topicStr.indexOf("control") != -1) { - String key = selectFromMarkerToMarker(topicStr, "/", 3); - String order; + String order; order += key; order += " "; order += payloadStr; @@ -326,38 +334,38 @@ void publishState() { const String getStateStr() { switch (mqtt.state()) { - case -4: - return F("no respond"); - break; - case -3: - return F("connection was broken"); - break; - case -2: - return F("connection failed"); - break; - case -1: - return F("client disconnected"); - break; - case 0: - return F("client connected"); - break; - case 1: - return F("doesn't support the requested version"); - break; - case 2: - return F("rejected the client identifier"); - break; - case 3: - return F("unable to accept the connection"); - break; - case 4: - return F("wrong username/password"); - break; - case 5: - return F("not authorized to connect"); - break; - default: - return F("unspecified"); - break; + case -4: + return F("no respond"); + break; + case -3: + return F("connection was broken"); + break; + case -2: + return F("connection failed"); + break; + case -1: + return F("client disconnected"); + break; + case 0: + return F("client connected"); + break; + case 1: + return F("doesn't support the requested version"); + break; + case 2: + return F("rejected the client identifier"); + break; + case 3: + return F("unable to accept the connection"); + break; + case 4: + return F("wrong username/password"); + break; + case 5: + return F("not authorized to connect"); + break; + default: + return F("unspecified"); + break; } } From 2aa3b56e85e614a2a4d09d3d38206be7fefabb04 Mon Sep 17 00:00:00 2001 From: Yuri Trikoz Date: Sun, 20 Dec 2020 01:24:08 +0300 Subject: [PATCH 65/94] #SPIFFS --- include/Consts.h | 2 +- include/ESP8266.h | 1 - include/FSEditor.h | 8 +++----- include/FileSystem.h | 2 ++ include/Utils/FileHelper.h | 10 ++-------- include/Utils/FileUtils.h | 9 +-------- src/FSEditor.cpp | 11 ++--------- src/ItemsList.cpp | 9 +++++---- src/MqttClient.cpp | 4 ++-- src/UpgradeFirm.cpp | 8 +++++--- src/Utils/FileUtils.cpp | 37 +++++++++++++++++++------------------ src/WebServer.cpp | 14 +++++++------- src/items/vLogging.cpp | 5 ++++- 13 files changed, 53 insertions(+), 67 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index 1a37c29b..1805957a 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -4,7 +4,7 @@ #define FIRMWARE_VERSION 273 #define FLASH_SIZE_1MB true //===========FileSystem============================================================================================================================================== -#define USE_LITTLEFS true +#define USE_LITTLEFS false //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN LED_BUILTIN diff --git a/include/ESP8266.h b/include/ESP8266.h index 6296fc81..76abdc10 100644 --- a/include/ESP8266.h +++ b/include/ESP8266.h @@ -5,7 +5,6 @@ #include #include "ESPAsyncTCP.h" #include "ESPAsyncWebServer.h" -#include #include #include #include diff --git a/include/FSEditor.h b/include/FSEditor.h index c037d008..4fcdef02 100644 --- a/include/FSEditor.h +++ b/include/FSEditor.h @@ -1,9 +1,7 @@ #pragma once + #include -//#include -#ifdef ESP8266 -#include -#endif +#include "FileSystem.h" class FSEditor : public AsyncWebHandler { private: @@ -20,7 +18,7 @@ class FSEditor : public AsyncWebHandler { #ifdef ESP32 FSEditor(const fs::FS& fs, const String& username = String(), const String& password = String()); #else - FSEditor(const String& username = String(), const String& password = String(), const fs::FS& fs = LittleFS); + FSEditor(const String& username = String(), const String& password = String(), const fs::FS& fs = FileFS); #endif virtual bool canHandle(AsyncWebServerRequest* request) override final; virtual void handleRequest(AsyncWebServerRequest* request) override final; diff --git a/include/FileSystem.h b/include/FileSystem.h index 813569f7..48706d84 100644 --- a/include/FileSystem.h +++ b/include/FileSystem.h @@ -6,6 +6,8 @@ #define FILE_WRITE "w" #define FILE_APPEND "a" +#include + #if USE_LITTLEFS #include extern FS LittleFS; diff --git a/include/Utils/FileHelper.h b/include/Utils/FileHelper.h index d2691706..b5ba764c 100644 --- a/include/Utils/FileHelper.h +++ b/include/Utils/FileHelper.h @@ -1,13 +1,7 @@ #pragma once #include -//#include "FS.h" -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif + +#include "FileSystem.h" class FileHelper { public: diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h index cd51532f..3c33557e 100644 --- a/include/Utils/FileUtils.h +++ b/include/Utils/FileUtils.h @@ -1,14 +1,7 @@ #pragma once #include #include "Consts.h" -//#include "FS.h" -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif +#include "FileSystem.h" /* * Инициализация ФС diff --git a/src/FSEditor.cpp b/src/FSEditor.cpp index 5f33d075..98be53cd 100644 --- a/src/FSEditor.cpp +++ b/src/FSEditor.cpp @@ -1,17 +1,10 @@ #include "FSEditor.h" -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif +#include "FileSystem.h" #define FS_MAXLENGTH_FILEPATH 32 const char *excludeListFile = "/.exclude.files"; - typedef struct ExcludeListS { char *item; ExcludeListS *next; @@ -239,7 +232,7 @@ void FSEditor::handleRequest(AsyncWebServerRequest *request) { if (request->header("If-Modified-Since").equals(buildTime)) { request->send(304); } else { - AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/edit.htm", "text/html"); + AsyncWebServerResponse *response = request->beginResponse(FileFS, "/edit.htm", "text/html"); // response->addHeader("Content-Encoding", "gzip"); response->addHeader("Last-Modified", buildTime); request->send(response); diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index aa83a65d..a2f99028 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -1,5 +1,6 @@ #include "ItemsList.h" +#include "FileSystem.h" #include "Class/NotAsync.h" #include "Init.h" #include "Utils/StringUtils.h" @@ -25,7 +26,7 @@ void itemsListInit() { void addItem2(String param) { int num = selectToMarker(param, "-").toInt(); - File configFile = LittleFS.open("/items/items.txt", "r"); + File configFile = FileFS.open("/items/items.txt", "r"); if (!configFile) { return; } @@ -62,7 +63,7 @@ void addItem2(String param) { void addPreset2(int num) { - File configFile = LittleFS.open("/presets/presets.c.txt", "r"); + File configFile = FileFS.open("/presets/presets.c.txt", "r"); if (!configFile) { return; } @@ -85,7 +86,7 @@ void addPreset2(int num) { configFile.close(); addFile(DEVICE_CONFIG_FILE, config); - File scenFile = LittleFS.open("/presets/presets.s.txt", "r"); + File scenFile = FileFS.open("/presets/presets.s.txt", "r"); if (!scenFile) { return; } @@ -160,7 +161,7 @@ uint8_t getFreePinAnalog() { } void delChoosingItems() { - File configFile = LittleFS.open("/" + String(DEVICE_CONFIG_FILE), "r"); + File configFile = FileFS.open("/" + String(DEVICE_CONFIG_FILE), "r"); if (!configFile) { return; } diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index f76ff552..610957f1 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -1,6 +1,5 @@ #include "MqttClient.h" #include "BufferExecute.h" -#include #include "items/vLogging.h" #include "Class/NotAsync.h" #include "Global.h" @@ -8,7 +7,8 @@ enum MqttBroker { MQTT_PRIMARY, MQTT_RESERVE }; -MqttBroker activeBroker = MQTT_PRIMARY; + +MqttBroker activeBroker = MQTT_PRIMARY; String mqttPrefix; String mqttRootDevice; diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index 21e835eb..b3f57051 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -1,5 +1,7 @@ #include "Upgrade.h" +#include "FileSystem.h" + #include "Class/NotAsync.h" #ifdef ESP8266 #include "ESP8266.h" @@ -92,16 +94,16 @@ void upgrade_firmware(int type) { bool upgradeFS() { WiFiClient wifiClient; bool ret = false; - Serial.println("Start upgrade LittleFS, please wait..."); + Serial.printf("Start upgrade %s, please wait...", FS_NAME); #ifdef ESP8266 ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return retFS = ESPhttpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp8266/littlefs/littlefs.bin")); + t_httpUpdate_return retFS = ESPhttpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp8266/") + FS_NAME + "/"+ FS_NAME+ ".bin"); #else httpUpdate.rebootOnUpdate(false); HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, serverIP + F("/projects/iotmanager/esp32/littlefs/spiffs.bin")); #endif if (retFS == HTTP_UPDATE_OK) { //если FS обновилась успешно - SerialPrint("I", "Update", "LittleFS upgrade done!"); + SerialPrint("I", "Update", "FS upgrade done!"); ret = true; } return ret; diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp index 31927eed..dd8b8a0e 100644 --- a/src/Utils/FileUtils.cpp +++ b/src/Utils/FileUtils.cpp @@ -1,15 +1,16 @@ +#include "FileSystem.h" + #include "Utils/FileUtils.h" #include "Utils\SerialPrint.h" #include "Utils/StringUtils.h" - const String filepath(const String& filename) { return filename.startsWith("/") ? filename : "/" + filename; } bool fileSystemInit() { - if (!LittleFS.begin()) { + if (!FileFS.begin()) { SerialPrint("E", F("FS"), F("FS Init ERROR, may be FS was not flashed")); return false; } @@ -19,8 +20,8 @@ bool fileSystemInit() { void removeFile(const String& filename) { String path = filepath(filename); - if (LittleFS.exists(path)) { - if (!LittleFS.remove(path)) { + if (FileFS.exists(path)) { + if (!FileFS.remove(path)) { SerialPrint("I","Files","remove " + path); } } else { @@ -30,7 +31,7 @@ void removeFile(const String& filename) { File seekFile(const String& filename, size_t position) { String path = filepath(filename); - auto file = LittleFS.open(path, "r"); + auto file = FileFS.open(path, "r"); if (!file) { SerialPrint("[E]","Files","open " + path); } @@ -42,7 +43,7 @@ File seekFile(const String& filename, size_t position) { const String readFileString(const String& filename, const String& to_find) { String path = filepath(filename); String res = "failed"; - auto file = LittleFS.open(path, "r"); + auto file = FileFS.open(path, "r"); if (!file) { return "failed"; } @@ -55,7 +56,7 @@ const String readFileString(const String& filename, const String& to_find) { const String addFileLn(const String& filename, const String& str) { String path = filepath(filename); - auto file = LittleFS.open(path, "a"); + auto file = FileFS.open(path, "a"); if (!file) { return "failed"; } @@ -66,7 +67,7 @@ const String addFileLn(const String& filename, const String& str) { const String addFile(const String& filename, const String& str) { String path = filepath(filename); - auto file = LittleFS.open(path, "a"); + auto file = FileFS.open(path, "a"); if (!file) { return "failed"; } @@ -79,19 +80,19 @@ bool copyFile(const String& src, const String& dst, bool overwrite) { String srcPath = filepath(src); String dstPath = filepath(dst); SerialPrint("I","Files","copy " + srcPath + " to " + dstPath); - if (!LittleFS.exists(srcPath)) { + if (!FileFS.exists(srcPath)) { SerialPrint("[E]","Files","not exist: " + srcPath); return false; } - if (LittleFS.exists(dstPath)) { + if (FileFS.exists(dstPath)) { if (!overwrite) { SerialPrint("[E]","Files","already exist: " + dstPath); return false; } - LittleFS.remove(dstPath); + FileFS.remove(dstPath); } - auto srcFile = LittleFS.open(srcPath, "r"); - auto dstFile = LittleFS.open(dstPath, "w"); + auto srcFile = FileFS.open(srcPath, "r"); + auto dstFile = FileFS.open(dstPath, "w"); uint8_t buf[512]; while (srcFile.available()) { @@ -105,7 +106,7 @@ bool copyFile(const String& src, const String& dst, bool overwrite) { const String writeFile(const String& filename, const String& str) { String path = filepath(filename); - auto file = LittleFS.open(path, "w"); + auto file = FileFS.open(path, "w"); if (!file) { return "failed"; } @@ -116,7 +117,7 @@ const String writeFile(const String& filename, const String& str) { const String readFile(const String& filename, size_t max_size) { String path = filepath(filename); - auto file = LittleFS.open(path, "r"); + auto file = FileFS.open(path, "r"); if (!file) { return "failed"; } @@ -132,7 +133,7 @@ const String readFile(const String& filename, size_t max_size) { const String getFileSize(const String filename) { String filepath(filename); - auto file = LittleFS.open(filepath, "r"); + auto file = FileFS.open(filepath, "r"); if (!file) { return "failed"; } @@ -145,13 +146,13 @@ const String getFSSizeInfo() { String res; #ifdef ESP8266 FSInfo info; - if (LittleFS.info(info)) { + if (FileFS.info(info)) { res = prettyBytes(info.usedBytes) + " of " + prettyBytes(info.totalBytes); } else { res = "error"; } #else - res = prettyBytes(LittleFS.usedBytes()) + " of " + prettyBytes(LittleFS.totalBytes()); + res = prettyBytes(FileFS.usedBytes()) + " of " + prettyBytes(FileFS.totalBytes()); #endif return res; } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index c955e9a2..b6db2e19 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -16,18 +16,18 @@ void init() { String login = jsonReadStr(configSetupJson, "weblogin"); String pass = jsonReadStr(configSetupJson, "webpass"); #ifdef ESP32 - server.addHandler(new FSEditor(LittleFS, login, pass)); + server.addHandler(new FSEditor(FileFS, login, pass)); #else server.addHandler(new FSEditor(login, pass)); #endif - server.serveStatic("/css/", LittleFS, "/css/").setCacheControl("max-age=600"); - server.serveStatic("/js/", LittleFS, "/js/").setCacheControl("max-age=600"); - server.serveStatic("/favicon.ico", LittleFS, "/favicon.ico").setCacheControl("max-age=600"); - server.serveStatic("/icon.jpeg", LittleFS, "/icon.jpeg").setCacheControl("max-age=600"); - server.serveStatic("/edit", LittleFS, "/edit").setCacheControl("max-age=600"); + server.serveStatic("/css/", FileFS, "/css/").setCacheControl("max-age=600"); + server.serveStatic("/js/", FileFS, "/js/").setCacheControl("max-age=600"); + server.serveStatic("/favicon.ico", FileFS, "/favicon.ico").setCacheControl("max-age=600"); + server.serveStatic("/icon.jpeg", FileFS, "/icon.jpeg").setCacheControl("max-age=600"); + server.serveStatic("/edit", FileFS, "/edit").setCacheControl("max-age=600"); - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm").setAuthentication(login.c_str(), pass.c_str()); + server.serveStatic("/", FileFS, "/").setDefaultFile("index.htm").setAuthentication(login.c_str(), pass.c_str()); server.onNotFound([](AsyncWebServerRequest *request) { SerialPrint("[E]","WebServer","not found:\n" + getRequestInfo(request)); diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index b4cbc6d2..66f71094 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -2,6 +2,8 @@ #include +#include "FileSystem.h" + #include "Class/LineParsing.h" #include "Global.h" #include "BufferExecute.h" @@ -146,8 +148,9 @@ void sendLogData(String file, String topic) { } void cleanLogAndData() { + #ifdef ESP8266 - auto dir = LittleFS.openDir("logs"); + auto dir = FileFS.openDir("logs"); while (dir.next()) { String fname = dir.fileName(); SerialPrint("I", "System", fname); From 3417d0c915346d060ec264a96a71e860839545bd Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 20 Dec 2020 14:11:59 +0100 Subject: [PATCH 66/94] =?UTF-8?q?1=D0=BC=D0=B1=204=D0=BC=D0=B1=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Consts.h | 8 +++----- platformio.ini | 3 +-- src/Web.cpp | 22 ++++------------------ src/main.cpp | 1 - 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index 1805957a..f6a7fc80 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,9 +2,9 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 273 -#define FLASH_SIZE_1MB true +#define ESP8266_FLASH_SIZE_1MB false //===========FileSystem============================================================================================================================================== -#define USE_LITTLEFS false +#define USE_LITTLEFS true //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN LED_BUILTIN @@ -28,14 +28,12 @@ #define SensorDhtEnabled #define PwmOutEnable //=========Features================================================================================================================================= -#ifndef FLASH_SIZE_1MB #define telegramEnable -#endif #define uartEnable #ifdef ESP8266 -#ifdef FLASH_SIZE_1MB +#ifdef ESP8266_FLASH_SIZE_1MB #define FIRMWARE_NAME "esp8266-1mb" #else #define FIRMWARE_NAME "esp8266" diff --git a/platformio.ini b/platformio.ini index 3d8bb9ec..5ce94223 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ extra_scripts = ./tools/littlefsbuilder.py [env:esp8266_01_1m] framework = arduino board = nodemcuv2 -board_build.ldscript = eagle.flash.1m512.ld +board_build.ldscript = eagle.flash.1m256.ld platform = https://github.com/platformio/platform-espressif8266.git lib_deps = ${common_env_data.lib_deps_external} @@ -57,7 +57,6 @@ lib_deps = monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 -board_build.filesystem = littlefs [env:esp8266] framework = arduino diff --git a/src/Web.cpp b/src/Web.cpp index 0d4c5875..a6cf2b84 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -26,11 +26,9 @@ void web_init() { if (request->hasArg(F("addPreset"))) { addPreset2(request->getParam(F("addPreset"))->value().toInt()); -#ifdef FLASH_SIZE_1MB - jsonWriteStr(configSetupJson, F("warning1"), F("

Присеты не доступны, модуль на 1mb

")); -#else + jsonWriteStr(configSetupJson, F("warning1"), F("

Требуется перезагрузка

")); -#endif + request->redirect(F("/?set.device")); } @@ -335,13 +333,11 @@ void web_init() { SerialPrint("I", "Update", "firmware version: " + String(lastVersion)); String msg = ""; -#ifdef FLASH_SIZE_1MB - if (FLASH_SIZE_1MB) { + + if (ESP8266_FLASH_SIZE_1MB) { msg = F("Обновление невозможно, память устройства 1 мб"); } else { -#endif - if (lastVersion == FIRMWARE_VERSION) { msg = F("Актуальная версия прошивки уже установлена."); } @@ -357,17 +353,7 @@ void web_init() { else if (lastVersion < FIRMWARE_VERSION) { msg = F("Ошибка версии. Попробуйте повторить позже..."); } -#ifdef FLASH_SIZE_1MB } -#endif - - // else if (lastVersion == "") { - //msg = F("Нажмите на кнопку \"обновить прошивку\" повторно..."); - //} else if (lastVersion == "less") { - //msg = F("Обновление \"по воздуху\" не поддерживается!"); - //} else if (lastVersion == "notsupported") { - // msg = F("Обновление возможно только через usb!"); - //} String tmp = "{}"; jsonWriteStr(tmp, "title", "" + msg); diff --git a/src/main.cpp b/src/main.cpp index 3bd8fb0a..aeabdbd2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ #include - #include "BufferExecute.h" #include "Bus.h" #include "Class/CallBackTest.h" From 1df87f8986c4389155d681438762ec208b272104 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 20 Dec 2020 17:08:16 +0100 Subject: [PATCH 67/94] =?UTF-8?q?=D1=83=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B3=20=D1=82=D0=B5=D0=BB=D0=B5?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D0=BC=D0=B0,=20=D0=B1=D0=B0=D0=B3=20=D1=81?= =?UTF-8?q?=D1=86=D0=B5=D0=BD=D0=B0=D1=80=D0=B8=D0=B5=D0=B2....?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/presets/presets.c.txt | 4 +-- data/presets/presets.s.txt | 10 +++---- include/Class/LineParsing.h | 59 +++++++++++++++++++------------------ include/Tests.h | 3 ++ src/Init.cpp | 2 +- src/ItemsList.cpp | 33 +++++++++++---------- src/Telegram.cpp | 42 ++++++++++++-------------- src/Tests.cpp | 12 ++++++++ src/main.cpp | 5 +++- 9 files changed, 93 insertions(+), 77 deletions(-) create mode 100644 include/Tests.h create mode 100644 src/Tests.cpp diff --git a/data/presets/presets.c.txt b/data/presets/presets.c.txt index 5db609f9..cd7fde4a 100644 --- a/data/presets/presets.c.txt +++ b/data/presets/presets.c.txt @@ -3,10 +3,10 @@ 0;inoutput;tUp1;inputDigit;Термостат1;Верхний#порог;3 0;inoutput;tLow1;inputDigit;Термостат1;Нижний#порог;4 0;button-out;btn1;toggle;Термостат1;Нагрев;5;pin[12]* -0;dallas-temp;t2;anydataTemp;Термостат2;Температура;1;pin[2];index[0];int[60] +0;dallas-temp;t2;anydataTemp;Термостат2;Температура;1;pin[2];index[0];int[10] 0;logging;log2;chart;Термостат2;История;2;val[t2];int[10];cnt[100] 0;inoutput;threshold;inputDigitTemp;Термостат2;Заданная#температура;3 -0;button-out;heater2;toggle;Термостат2;Нагреватель;7;pin[12] +0;button-out;heater2;toggle;Термостат2;Нагреватель;7;pin[5] 0;inoutput;time21;inputTimeClock;Расписание2;Утренний#период;8 0;inoutput;threshold1;inputDigitTemp;Расписание2;Температура;9 0;inoutput;time22;inputTimeClock;Расписание2;Дневной#период;10 diff --git a/data/presets/presets.s.txt b/data/presets/presets.s.txt index 0e36c7c1..d0ebb9fb 100644 --- a/data/presets/presets.s.txt +++ b/data/presets/presets.s.txt @@ -1,10 +1,10 @@ t1 > tUp1 btn1 0 -telegram нагрев#выключен 1 +telegram Гостинная нагрев#выключен end t1 < tLow1 btn1 1 -telegram нагрев#включен 1 +telegram Гостинная нагрев#включен end* t2 > threshold+-2 heater2 0 @@ -26,11 +26,11 @@ threshold threshold4 end* h3 > hUp3 hUp3 0 -telegram полив#выключен 1 +telegram Теплица полив#выключен end h3 < hLow3 hUp3 1 -telegram полив#включен 1 +telegram Теплица полив#включен end* timenow = time41 btn41 1 @@ -65,7 +65,7 @@ end* sensor = 1 text обнаружено time %date% -telegram text обнаружено#движение 1 +telegram often Обнаружено#движение end reset = 1 text не#обнаружено diff --git a/include/Class/LineParsing.h b/include/Class/LineParsing.h index 3edad798..f3553ad4 100644 --- a/include/Class/LineParsing.h +++ b/include/Class/LineParsing.h @@ -1,12 +1,13 @@ #pragma once #include -#include "ItemsList.h" + #include "Global.h" +#include "ItemsList.h" #include "Utils/JsonUtils.h" class LineParsing { -protected: + protected: String _key; String _file; String _page; @@ -28,31 +29,31 @@ protected: int pinErrors; -public: + public: LineParsing() : - _key{ "" }, - _file{ "" }, - _page{ "" }, - _descr{ "" }, - _order{ "" }, - _addr{ "" }, - _reg{ "" }, - _pin{ "" }, - _map{ "" }, - _c{ "" }, - _inv{ "" }, - _state{ "" }, - _db{ "" }, - _type{ "" }, - _int{ "" }, - _cnt{ "" }, - _val{ "" }, - _index{ "" }, + _key{""}, + _file{""}, + _page{""}, + _descr{""}, + _order{""}, + _addr{""}, + _reg{""}, + _pin{""}, + _map{""}, + _c{""}, + _inv{""}, + _state{""}, + _db{""}, + _type{""}, + _int{""}, + _cnt{""}, + _val{""}, + _index{""}, - pinErrors{ 0 } + pinErrors{0} - {}; + {}; void update() { //String order = sCmd.order(); @@ -110,9 +111,12 @@ public: } } - if (!isPinExist(_pin.toInt()) || !isDigitStr(_pin)) { - pinErrors++; - _pin = ""; + if (_pin != "") { + if (!isPinExist(_pin.toInt()) || !isDigitStr(_pin)) { + pinErrors++; + Serial.println("'" + _pin + "'"); + _pin = ""; + } } _page.replace("#", " "); @@ -123,7 +127,6 @@ public: createWidget(_descr, _page, _order, _file, _key); } - String gkey() { return _key; } @@ -176,7 +179,6 @@ public: return _index; } - int getPinErrors() { return pinErrors; } @@ -185,7 +187,6 @@ public: pinErrors = 0; } - void clear() { _key = ""; _file = ""; diff --git a/include/Tests.h b/include/Tests.h new file mode 100644 index 00000000..cfec2e53 --- /dev/null +++ b/include/Tests.h @@ -0,0 +1,3 @@ +#pragma once + +extern void testsPerform(); \ No newline at end of file diff --git a/src/Init.cpp b/src/Init.cpp index cf1c4248..3a17ef50 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -106,7 +106,7 @@ void deviceInit() { int errors = myLineParsing.getPinErrors(); - if (errors != 0) { + if (errors > 0) { jsonWriteStr(configSetupJson, F("warning3"), F("

Обнаружен неверный номер пина

")); } else { diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index a2f99028..aa046e99 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -1,7 +1,7 @@ #include "ItemsList.h" -#include "FileSystem.h" #include "Class/NotAsync.h" +#include "FileSystem.h" #include "Init.h" #include "Utils/StringUtils.h" @@ -20,7 +20,6 @@ void itemsListInit() { }, nullptr); - SerialPrint("I", F("Items"), F("Items Init")); } @@ -61,7 +60,6 @@ void addItem2(String param) { Serial.println(seachingLine); } - void addPreset2(int num) { File configFile = FileFS.open("/presets/presets.c.txt", "r"); if (!configFile) { @@ -76,8 +74,7 @@ void addPreset2(int num) { if (i1 == num) { if (i1 == 1) { config = "\n" + item; - } - else { + } else { config = item; } break; @@ -85,7 +82,7 @@ void addPreset2(int num) { } configFile.close(); addFile(DEVICE_CONFIG_FILE, config); - + //=========================================================================== File scenFile = FileFS.open("/presets/presets.s.txt", "r"); if (!scenFile) { return; @@ -97,18 +94,24 @@ void addPreset2(int num) { i2++; String item = scenFile.readStringUntil('*'); if (i2 == num) { - scen = item; + if (i1 == 1) { + scen = "\n" + item; + } else { + scen = item; + } break; } } scenFile.close(); if (readFile(String(DEVICE_SCENARIO_FILE), 2048) == "//") { removeFile(DEVICE_SCENARIO_FILE); + scen = deleteBeforeDelimiter(scen, "\n"); + addFile(DEVICE_SCENARIO_FILE, scen); + } else { + addFile(DEVICE_SCENARIO_FILE, scen); } - addFile(DEVICE_SCENARIO_FILE, scen); } - void delAllItems() { removeFile(DEVICE_CONFIG_FILE); addFile(DEVICE_CONFIG_FILE, String(firstLine)); @@ -129,24 +132,23 @@ uint8_t getNewElementNumber(String file) { uint8_t getFreePinAll() { #ifdef ESP8266 - uint8_t pins[] = { 0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5 }; + uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; #endif #ifdef ESP32 - uint8_t pins[] = { 0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5 }; + uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; #endif uint8_t array_sz = sizeof(pins) / sizeof(pins[0]); uint8_t i = getNewElementNumber("pins.txt"); if (i < array_sz) { return pins[i]; - } - else { + } else { return 0; } } bool isPinExist(unsigned int num) { bool ret = false; - unsigned int pins[] = { 0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 16 }; + unsigned int pins[] = {0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15, 16}; uint8_t array_sz = sizeof(pins) / sizeof(pins[0]); for (uint8_t i = 0; i < array_sz; i++) { if (pins[i] == num) ret = true; @@ -172,8 +174,7 @@ void delChoosingItems() { String item = configFile.readStringUntil('\n'); if (firstLine) { finalConf += item; - } - else { + } else { int checkbox = selectToMarker(item, ";").toInt(); if (checkbox == 0) { finalConf += "\n" + item; diff --git a/src/Telegram.cpp b/src/Telegram.cpp index af81e785..49b45e7d 100644 --- a/src/Telegram.cpp +++ b/src/Telegram.cpp @@ -1,13 +1,13 @@ #include "Consts.h" #ifdef telegramEnable -#include "Telegram.h" #include "BufferExecute.h" -CTBot* myBot{ nullptr }; +#include "Telegram.h" +CTBot* myBot{nullptr}; void telegramInit() { if (isTelegramEnabled()) { telegramInitBeen = true; - sCmd.addCommand("telegramEnable", sendTelegramMsg); + sCmd.addCommand("telegram", sendTelegramMsg); String token = jsonReadStr(configSetupJson, "telegramApi"); if (!myBot) { myBot = new CTBot(); @@ -16,8 +16,7 @@ void telegramInit() { myBot->enableUTF8Encoding(true); if (myBot->testConnection()) { SerialPrint("I", "Telegram", "Connected"); - } - else { + } else { SerialPrint("E", "Telegram", "Not connected"); } SerialPrint("I", F("Telegram"), F("Telegram Init")); @@ -53,36 +52,34 @@ void telegramMsgParse(String msg) { loopCmdAdd(String(msg) + ","); myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), "order done"); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + String(msg)); - } - else if (msg.indexOf("get") != -1) { + } else if (msg.indexOf("get") != -1) { msg = deleteBeforeDelimiter(msg, "_"); - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), getValue(msg)); //jsonReadStr(configLiveJson , msg)); + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), getValue(msg)); //jsonReadStr(configLiveJson , msg)); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + String(msg)); - } - else if (msg.indexOf("all") != -1) { + } else if (msg.indexOf("all") != -1) { String list = returnListOfParams(); myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), list); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + "\n" + list); - } - else { + } else { myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), F("Wrong order, use /all to get all values, /get_id to get value, or /set_id_value to set value")); } } void sendTelegramMsg() { - String id = sCmd.next(); + String sabject = sCmd.next(); String msg = sCmd.next(); - msg.replace("#", " "); - if (id == "often") { + if (sabject == "often") { + msg.replace("#", " "); myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); - } - else { - String prevMsg = jsonReadStr(telegramMsgJson, id); + } else { + String prevMsg = jsonReadStr(telegramMsgJson, sabject); if (prevMsg != msg) { - jsonWriteStr(telegramMsgJson, id, msg); - myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), msg); - SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + msg); + jsonWriteStr(telegramMsgJson, sabject, msg); + msg.replace("#", " "); + sabject.replace("#", " "); + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), sabject + " " + msg); + SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + ", msg: " + sabject + " " + msg); } } } @@ -95,7 +92,6 @@ bool isTelegramInputOn() { return jsonReadBool(configSetupJson, "teleginput"); } - String returnListOfParams() { String cmdStr = readFile(DEVICE_CONFIG_FILE, 4096); cmdStr += "\r\n"; @@ -108,7 +104,7 @@ String returnListOfParams() { count++; if (count > 1) { String id = selectFromMarkerToMarker(buf, ";", 2); - String value = getValue(id); //jsonReadStr(configLiveJson , id); + String value = getValue(id); //jsonReadStr(configLiveJson , id); String page = selectFromMarkerToMarker(buf, ";", 4); page.replace("#", " "); String name = selectFromMarkerToMarker(buf, ";", 5); diff --git a/src/Tests.cpp b/src/Tests.cpp new file mode 100644 index 00000000..f64fe70e --- /dev/null +++ b/src/Tests.cpp @@ -0,0 +1,12 @@ +#include "Tests.h" + +#include "Global.h" +#include "ItemsList.h" + +void testsPerform() { + Serial.println("====some tests section===="); + + + + Serial.println("==========end============"); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index aeabdbd2..c4113bbb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ #include "Telegram.h" #include "SoftUART.h" #include "FileSystem.h" +#include "Tests.h" void not_async_actions(); @@ -67,7 +68,9 @@ void setup() { SsdpInit(); #endif getFSInfo(); - //esp_log_level_set("esp_littlefs", ESP_LOG_NONE); + + testsPerform(); + just_load = false; initialized = true; } From c5772f8451e698bd6b500a8f7b4cff2fe370db14 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 20 Dec 2020 17:44:40 +0100 Subject: [PATCH 68/94] =?UTF-8?q?=D0=B1=D0=B0=D0=B3=20=D0=B0=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D1=81=D0=B5?= =?UTF-8?q?=D0=BD=D1=81=D0=BE=D1=80=D0=B0=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/items/items.txt | 2 +- src/main.cpp | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/data/items/items.txt b/data/items/items.txt index 3ff53afd..9e6be090 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -6,7 +6,7 @@ 6;0;inoutput;id;inputDigit;Ввод;Введите#цифру;order 7;0;inoutput;id;inputTime;Ввод;Введите#время;order 8;0;inoutput;id;anydata;Вывод;Сигнализация;order -9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;gpio;map[0,1024,0,100];c[1] +9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10] 10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10] 11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10] 12;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1] diff --git a/src/main.cpp b/src/main.cpp index c4113bbb..d4673839 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,28 +1,30 @@ #include + #include "BufferExecute.h" #include "Bus.h" #include "Class/CallBackTest.h" #include "Class/NotAsync.h" #include "Class/ScenarioClass3.h" #include "Cmd.h" +#include "FileSystem.h" #include "Global.h" #include "Init.h" #include "ItemsList.h" #include "RemoteOrdersUdp.h" +#include "SoftUART.h" +#include "Telegram.h" +#include "Tests.h" #include "Utils/StatUtils.h" #include "Utils/Timings.h" #include "Utils/WebUtils.h" #include "items/ButtonInClass.h" -#include "items/vLogging.h" -#include "items/vImpulsOut.h" -#include "items/vSensorDallas.h" #include "items/vCountDown.h" +#include "items/vImpulsOut.h" +#include "items/vLogging.h" +#include "items/vSensorAnalog.h" +#include "items/vSensorDallas.h" #include "items/vSensorUltrasonic.h" -#include "Telegram.h" -#include "SoftUART.h" -#include "FileSystem.h" -#include "Tests.h" void not_async_actions(); @@ -47,7 +49,7 @@ void setup() { #endif clockInit(); timeInit(); - sensorsInit(); //Will be remooved + sensorsInit(); //Will be remooved itemsListInit(); espInit(); routerConnect(); @@ -69,15 +71,12 @@ void setup() { #endif getFSInfo(); - testsPerform(); + testsPerform(); just_load = false; initialized = true; } - - - void loop() { if (!initialized) { return; @@ -130,4 +129,9 @@ void loop() { myCountDown->at(i).loop(); } } + if (mySensorAnalog != nullptr) { + for (unsigned int i = 0; i < mySensorAnalog->size(); i++) { + mySensorAnalog->at(i).loop(); + } + } } \ No newline at end of file From d9672d17b0708cd082eaad7c6cf16b91b0ba075c Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Mon, 21 Dec 2020 01:46:11 +0100 Subject: [PATCH 69/94] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BD=20dht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/items/items.txt | 8 +- include/BufferExecute.h | 12 --- include/Global.h | 4 +- include/items/SensorAnalogClass.h | 36 ------- include/items/SensorDhtClass.h | 160 ++++++++++++++--------------- include/items/vSensorDht.h | 63 ++++++++++++ src/BufferExecute.cpp | 6 +- src/FileSystem.cpp | 16 +-- src/Global.cpp | 3 + src/Init.cpp | 24 ++--- src/items/SensorAnalogClass.cpp | 21 ---- src/items/SensorDhtClass.cpp | 70 ++++++------- src/items/vSensorDht.cpp | 161 ++++++++++++++++++++++++++++++ src/main.cpp | 7 ++ 14 files changed, 380 insertions(+), 211 deletions(-) delete mode 100644 include/items/SensorAnalogClass.h create mode 100644 include/items/vSensorDht.h delete mode 100644 src/items/SensorAnalogClass.cpp create mode 100644 src/items/vSensorDht.cpp diff --git a/data/items/items.txt b/data/items/items.txt index 9e6be090..19622ab8 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -9,10 +9,10 @@ 9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10] 10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10] 11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10] -12;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1] -13;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1] -14;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1] -15;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1] +12;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1];int[10] +13;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1];int[10] +14;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1];int[10] +15;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1];int[10] 16;0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] 17;0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] 18;0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] diff --git a/include/BufferExecute.h b/include/BufferExecute.h index a236e57d..28644b59 100644 --- a/include/BufferExecute.h +++ b/include/BufferExecute.h @@ -12,19 +12,7 @@ extern int getKeyNum(String& key, String& keyNumberTable); extern void buttonIn(); extern void buttonInSet(); -extern void analogAdc(); -extern void analogReading(); -//extern void ultrasonicCm(); -//extern void ultrasonicReading(); - -extern void dallasTemp(); -extern void dallasReading(); - -extern void dhtTemp(); -extern void dhtReadingTemp(); -extern void dhtHum(); -extern void dhtReadingHum(); extern void bme280Temp(); extern void bme280ReadingTemp(); diff --git a/include/Global.h b/include/Global.h index bdd63086..db71e643 100644 --- a/include/Global.h +++ b/include/Global.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -89,6 +88,9 @@ extern int countDown_EnterCounter; extern String logging_KeyList; extern int logging_EnterCounter; //========================================= +extern int dhtTmp_EnterCounter; +extern int dhtHum_EnterCounter; +//========================================= // Sensors extern String sensorReadingMap10sec; diff --git a/include/items/SensorAnalogClass.h b/include/items/SensorAnalogClass.h deleted file mode 100644 index a4007eb4..00000000 --- a/include/items/SensorAnalogClass.h +++ /dev/null @@ -1,36 +0,0 @@ -//#pragma once -//#include -// -//#include "Class/LineParsing.h" -//#include "Global.h" -//#include "items/SensorConvertingClass.h" -// -//class SensorAnalogClass : public SensorConvertingClass { -// public: -// SensorAnalogClass() : SensorConvertingClass(){}; -// -// void SensorAnalogInit() { -// jsonWriteStr(configOptionJson, _key + "_pin", _pin); -// jsonWriteStr(configOptionJson, _key + "_map", _map); -// jsonWriteStr(configOptionJson, _key + "_с", _c); -// } -// -// int SensorAnalogRead(String key, String pin) { -// int value; -//#ifdef ESP32 -// int pinInt = pin.toInt(); -// value = analogRead(pinInt); -//#endif -//#ifdef ESP8266 -// value = analogRead(A0); -//#endif -// value = this->mapping(key, value); -// float valueFl = this->correction(key, value); -// eventGen2(key, String(valueFl)); -// jsonWriteStr(configLiveJson, key, String(valueFl)); -// publishStatus(key, String(valueFl)); -// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); -// return value; -// } -//}; -//extern SensorAnalogClass mySensorAnalog; \ No newline at end of file diff --git a/include/items/SensorDhtClass.h b/include/items/SensorDhtClass.h index 2242ed4e..43cc236e 100644 --- a/include/items/SensorDhtClass.h +++ b/include/items/SensorDhtClass.h @@ -1,80 +1,80 @@ -#pragma once -#include "Consts.h" -#ifdef SensorDhtEnabled -#include -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" - -DHTesp dht; -class SensorDhtClass : public SensorConvertingClass { - public: - SensorDhtClass() : SensorConvertingClass(){}; - - void SensorDhtInit() { - if (_type == "dht11") { - dht.setup(_pin.toInt(), DHTesp::DHT11); - } - if (_type == "dht22") { - dht.setup(_pin.toInt(), DHTesp::DHT22); - } - sensorReadingMap10sec += _key + ","; - - //to do если надо будет читать несколько dht - //dhtEnterCounter++; - //jsonWriteInt(configOptionJson, _key + "_num", dhtEnterCounter); - - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - } - - void SensorDhtReadTemp(String key) { - //to do если надо будет читать несколько dht - //int cnt = jsonReadInt(configOptionJson, key + "_num"); - float value; - static int counter; - if (dht.getStatus() != 0 && counter < 5) { - counter++; - //return; - } else { - counter = 0; - value = dht.getTemperature(); - if (String(value) != "nan") { - //value = this->mapping(key, value); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } else { - Serial.println("[E] sensor '" + key); - } - } - } - - void SensorDhtReadHum(String key) { - //to do если надо будет читать несколько dht - //int cnt = jsonReadInt(configOptionJson, key + "_num"); - float value; - static int counter; - if (dht.getStatus() != 0 && counter < 5) { - counter++; - //return; - } else { - counter = 0; - value = dht.getHumidity(); - if (String(value) != "nan") { - //value = this->mapping(key, value); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } else { - Serial.println("[E] sensor '" + key); - } - } - } -}; -extern SensorDhtClass mySensorDht; -#endif \ No newline at end of file +//#pragma once +//#include "Consts.h" +//#ifdef SensorDhtEnabled +//#include +//#include "Class/LineParsing.h" +//#include "Global.h" +//#include "items/SensorConvertingClass.h" +// +//DHTesp dht; +//class SensorDhtClass : public SensorConvertingClass { +// public: +// SensorDhtClass() : SensorConvertingClass(){}; +// +// void SensorDhtInit() { +// if (_type == "dht11") { +// dht.setup(_pin.toInt(), DHTesp::DHT11); +// } +// if (_type == "dht22") { +// dht.setup(_pin.toInt(), DHTesp::DHT22); +// } +// sensorReadingMap10sec += _key + ","; +// +// //to do если надо будет читать несколько dht +// //dhtEnterCounter++; +// //jsonWriteInt(configOptionJson, _key + "_num", dhtEnterCounter); +// +// jsonWriteStr(configOptionJson, _key + "_map", _map); +// jsonWriteStr(configOptionJson, _key + "_с", _c); +// } +// +// void SensorDhtReadTemp(String key) { +// //to do если надо будет читать несколько dht +// //int cnt = jsonReadInt(configOptionJson, key + "_num"); +// float value; +// static int counter; +// if (dht.getStatus() != 0 && counter < 5) { +// counter++; +// //return; +// } else { +// counter = 0; +// value = dht.getTemperature(); +// if (String(value) != "nan") { +// //value = this->mapping(key, value); +// float valueFl = this->correction(key, value); +// eventGen2(key, String(valueFl)); +// jsonWriteStr(configLiveJson, key, String(valueFl)); +// publishStatus(key, String(valueFl)); +// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); +// } else { +// Serial.println("[E] sensor '" + key); +// } +// } +// } +// +// void SensorDhtReadHum(String key) { +// //to do если надо будет читать несколько dht +// //int cnt = jsonReadInt(configOptionJson, key + "_num"); +// float value; +// static int counter; +// if (dht.getStatus() != 0 && counter < 5) { +// counter++; +// //return; +// } else { +// counter = 0; +// value = dht.getHumidity(); +// if (String(value) != "nan") { +// //value = this->mapping(key, value); +// float valueFl = this->correction(key, value); +// eventGen2(key, String(valueFl)); +// jsonWriteStr(configLiveJson, key, String(valueFl)); +// publishStatus(key, String(valueFl)); +// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); +// } else { +// Serial.println("[E] sensor '" + key); +// } +// } +// } +//}; +//extern SensorDhtClass mySensorDht; +//#endif \ No newline at end of file diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h new file mode 100644 index 00000000..744af653 --- /dev/null +++ b/include/items/vSensorDht.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include "Global.h" +#include "GyverFilters.h" + +extern DHTesp* dht; + +class SensorDht; + +typedef std::vector MySensorDhtVector; + +struct tmpParams { + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + unsigned long interval; + String key; + unsigned int pin; + int map1; + int map2; + int map3; + int map4; + float c; +}; + +struct humParams { + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + unsigned long interval; + String key; + unsigned int pin; + int map1; + int map2; + int map3; + int map4; + float c; +}; + +class SensorDht { + public: + SensorDht(); + ~SensorDht(); + + void loopTmp(); + void loopHum(); + + void readTmp(); + void readHum(); + + void tmpInit(const tmpParams& tmpSet); + void humInit(const humParams& humSet); + + private: + tmpParams _tmpSet; + humParams _humSet; +}; + +extern MySensorDhtVector* mySensorDht; + +extern void dhtTmp(); +extern void dhtHum(); \ No newline at end of file diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index e0752de6..30bb9b85 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -10,6 +10,8 @@ #include "items/vLogging.h" #include "items/vImpulsOut.h" #include "items/vCountDown.h" +#include "items/vSensorAnalog.h" +#include "items/vSensorDht.h" void loopCmdAdd(const String& cmdStr) { if (cmdStr.endsWith(",")) { @@ -73,7 +75,7 @@ void csvCmdExecute(String& cmdStr) { } #ifdef SensorDhtEnabled else if (order == F("dht-temp")) { - sCmd.addCommand(order.c_str(), dhtTemp); + sCmd.addCommand(order.c_str(), dhtTmp); } else if (order == F("dht-hum")) { sCmd.addCommand(order.c_str(), dhtHum); @@ -84,7 +86,7 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), bme280Temp); } else if (order == F("bme280-hum")) { - sCmd.addCommand(order.c_str(), bme280Hum); + //sCmd.addCommand(order.c_str(), bme280Hum); } else if (order == F("bme280-press")) { sCmd.addCommand(order.c_str(), bme280Press); diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index e07fb8c0..2c65f899 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -18,14 +18,14 @@ void getFSInfo() { jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + prettyBytes(freeBytes) + ")"); - SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); - SerialPrint("I", F("FS"), "usedBytes=" + String(usedBytes)); - SerialPrint("I", F("FS"), "maxOpenFiles=" + String(maxOpenFiles)); - SerialPrint("I", F("FS"), "blockSize=" + String(blockSize)); - SerialPrint("I", F("FS"), "pageSize=" + String(pageSize)); - SerialPrint("I", F("FS"), "maxPathLength=" + String(maxPathLength)); - SerialPrint("I", F("FS"), "freeBytes=" + String(freeBytes)); - SerialPrint("I", F("FS"), "freePer=" + String(freePer)); + //SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); + //SerialPrint("I", F("FS"), "usedBytes=" + String(usedBytes)); + //SerialPrint("I", F("FS"), "maxOpenFiles=" + String(maxOpenFiles)); + //SerialPrint("I", F("FS"), "blockSize=" + String(blockSize)); + //SerialPrint("I", F("FS"), "pageSize=" + String(pageSize)); + //SerialPrint("I", F("FS"), "maxPathLength=" + String(maxPathLength)); + //SerialPrint("I", F("FS"), "freeBytes=" + String(freeBytes)); + //SerialPrint("I", F("FS"), "freePer=" + String(freePer)); } else { SerialPrint("E", F("FS"), F("FS info error")); diff --git a/src/Global.cpp b/src/Global.cpp index 93db61b1..0e98c22a 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -59,6 +59,9 @@ int countDown_EnterCounter = -1; String logging_KeyList = ""; int logging_EnterCounter = -1; //========================================= +int dhtTmp_EnterCounter = -1; +int dhtHum_EnterCounter = -1; +//========================================= // Sensors String sensorReadingMap10sec; diff --git a/src/Init.cpp b/src/Init.cpp index 3a17ef50..983dbc40 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -1,16 +1,17 @@ #include "Init.h" + #include "BufferExecute.h" +#include "Class/LineParsing.h" #include "Cmd.h" #include "Global.h" -#include "Class/LineParsing.h" -#include "items/vLogging.h" -#include "items/vImpulsOut.h" #include "items/vButtonOut.h" +#include "items/vCountDown.h" +#include "items/vImpulsOut.h" +#include "items/vInOutput.h" +#include "items/vLogging.h" +#include "items/vPwmOut.h" #include "items/vSensorDallas.h" #include "items/vSensorUltrasonic.h" -#include "items/vInOutput.h" -#include "items/vPwmOut.h" -#include "items/vCountDown.h" void loadConfig() { configSetupJson = readFile("config.json", 4096); @@ -42,7 +43,6 @@ void espInit() { } void deviceInit() { - sensorReadingMap10sec = ""; //======clear dallas params====== @@ -92,14 +92,16 @@ void deviceInit() { countDown_KeyList = ""; countDown_EnterCounter = -1; //=================================== - + dhtTmp_EnterCounter = -1; + dhtHum_EnterCounter = -1; + //========================================= #ifdef LAYOUT_IN_RAM all_widgets = ""; #else removeFile(String("layout.txt")); #endif - + myLineParsing.clearErrors(); fileCmdExecute(String(DEVICE_CONFIG_FILE)); @@ -108,8 +110,7 @@ void deviceInit() { if (errors > 0) { jsonWriteStr(configSetupJson, F("warning3"), F("

Обнаружен неверный номер пина

")); - } - else { + } else { jsonWriteStr(configSetupJson, F("warning3"), ""); } @@ -135,4 +136,3 @@ void uptime_init() { void handle_uptime() { jsonWriteStr(configSetupJson, "uptime", timeNow->getUptime()); } - diff --git a/src/items/SensorAnalogClass.cpp b/src/items/SensorAnalogClass.cpp deleted file mode 100644 index e8dcc9dd..00000000 --- a/src/items/SensorAnalogClass.cpp +++ /dev/null @@ -1,21 +0,0 @@ -//#include "BufferExecute.h" -//#include "items/SensorAnalogClass.h" -//#ifdef ANALOG_ENABLED -////==============================================Модуль аналогового сенсора=========================================================================================== -////=================================================================================================================================================================== -//SensorAnalogClass mySensorAnalog; -//void analogAdc() { -// mySensorAnalog.update(); -// String key = mySensorAnalog.gkey(); -// sCmd.addCommand(key.c_str(), analogReading); -// sensorReadingMap10sec += key + ","; -// mySensorAnalog.SensorAnalogInit(); -// mySensorAnalog.clear(); -//} -// -//void analogReading() { -// String key = sCmd.order(); -// String pin = jsonReadStr(configOptionJson, key + "_pin"); -// mySensorAnalog.SensorAnalogRead(key, pin); -//} -//#endif \ No newline at end of file diff --git a/src/items/SensorDhtClass.cpp b/src/items/SensorDhtClass.cpp index d0c2e41c..c6f89671 100644 --- a/src/items/SensorDhtClass.cpp +++ b/src/items/SensorDhtClass.cpp @@ -1,35 +1,35 @@ -#include "Consts.h" -#ifdef SensorDhtEnabled -#include "items/SensorDhtClass.h" -#include "BufferExecute.h" -//=========================================DHT Sensor================================================================== -//dht-temp;id;anydata;Сенсоры;Температура;order;pin;type[dht11];c[1] -//dht-hum;id;anydata;Сенсоры;Влажность;order;pin;type[dht11];c[1] -//========================================================================================================================================= -SensorDhtClass mySensorDht; -void dhtTemp() { - mySensorDht.update(); - String key = mySensorDht.gkey(); - sCmd.addCommand(key.c_str(), dhtReadingTemp); - mySensorDht.SensorDhtInit(); - mySensorDht.clear(); -} -void dhtReadingTemp() { - String key = sCmd.order(); - mySensorDht.SensorDhtReadTemp(key); -} - - - -void dhtHum() { - mySensorDht.update(); - String key = mySensorDht.gkey(); - sCmd.addCommand(key.c_str(), dhtReadingHum); - mySensorDht.SensorDhtInit(); - mySensorDht.clear(); -} -void dhtReadingHum() { - String key = sCmd.order(); - mySensorDht.SensorDhtReadHum(key); -} -#endif \ No newline at end of file +//#include "Consts.h" +//#ifdef SensorDhtEnabled +//#include "items/SensorDhtClass.h" +//#include "BufferExecute.h" +////=========================================DHT Sensor================================================================== +////dht-temp;id;anydata;Сенсоры;Температура;order;pin;type[dht11];c[1] +////dht-hum;id;anydata;Сенсоры;Влажность;order;pin;type[dht11];c[1] +////========================================================================================================================================= +//SensorDhtClass mySensorDht; +//void dhtTemp() { +// mySensorDht.update(); +// String key = mySensorDht.gkey(); +// sCmd.addCommand(key.c_str(), dhtReadingTemp); +// mySensorDht.SensorDhtInit(); +// mySensorDht.clear(); +//} +//void dhtReadingTemp() { +// String key = sCmd.order(); +// mySensorDht.SensorDhtReadTemp(key); +//} +// +// +// +//void dhtHum() { +// mySensorDht.update(); +// String key = mySensorDht.gkey(); +// sCmd.addCommand(key.c_str(), dhtReadingHum); +// mySensorDht.SensorDhtInit(); +// mySensorDht.clear(); +//} +//void dhtReadingHum() { +// String key = sCmd.order(); +// mySensorDht.SensorDhtReadHum(key); +//} +//#endif \ No newline at end of file diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp new file mode 100644 index 00000000..2aa10f24 --- /dev/null +++ b/src/items/vSensorDht.cpp @@ -0,0 +1,161 @@ +#include "items/vSensorDht.h" + +#include + +#include "BufferExecute.h" +#include "Class/LineParsing.h" +#include "Global.h" + +DHTesp* dht = nullptr; + +SensorDht::SensorDht() {} + +SensorDht::~SensorDht() {} + +void SensorDht::tmpInit(const tmpParams& tmpSet) { + _tmpSet = tmpParams(tmpSet); + if (!dht) { + dht = new DHTesp(); + } + dht->setup(_tmpSet.pin, DHTesp::DHT11); +} + +void SensorDht::humInit(const humParams& humSet) { + _humSet = humParams(humSet); + if (!dht) { + dht = new DHTesp(); + } + dht->setup(_tmpSet.pin, DHTesp::DHT11); +} + +void SensorDht::loopTmp() { + _tmpSet.currentMillis = millis(); + _tmpSet.difference = _tmpSet.currentMillis - _tmpSet.prevMillis; + if (_tmpSet.difference >= _tmpSet.interval) { + _tmpSet.prevMillis = millis(); + readTmp(); + } +} + +void SensorDht::loopHum() { + _humSet.currentMillis = millis(); + _humSet.difference = _humSet.currentMillis - _humSet.prevMillis; + if (_humSet.difference >= _humSet.interval) { + _humSet.prevMillis = millis(); + readHum(); + } +} + +void SensorDht::readTmp() { + float value; + static int counter; + if (dht->getStatus() != 0 && counter < 5) { + counter++; + SerialPrint("E", "Sensor", "Disconnected"); + } else { + counter = 0; + value = dht->getTemperature(); + if (String(value) != "nan") { + //value = map(value, _tmpSet.map1, _tmpSet.map2, _tmpSet.map3, _tmpSet.map4); + value = value * _tmpSet.c; + eventGen2(_tmpSet.key, String(value)); + jsonWriteStr(configLiveJson, _tmpSet.key, String(value)); + publishStatus(_tmpSet.key, String(value)); + SerialPrint("I", "Sensor", "'" + _tmpSet.key + "' data: " + String(value)); + } else { + SerialPrint("E", "Sensor", "'" + _tmpSet.key + "' data: " + String(value)); + } + } +} + +void SensorDht::readHum() { + float value; + static int counter; + if (dht->getStatus() != 0 && counter < 5) { + counter++; + SerialPrint("E", "Sensor", "Disconnected"); + } else { + counter = 0; + value = dht->getHumidity(); + if (String(value) != "nan") { + //value = map(value, _humSet.map1, _humSet.map2, _humSet.map3, _humSet.map4); + value = value * _humSet.c; + eventGen2(_humSet.key, String(value)); + jsonWriteStr(configLiveJson, _humSet.key, String(value)); + publishStatus(_humSet.key, String(value)); + SerialPrint("I", "Sensor", "'" + _humSet.key + "' data: " + String(value)); + } else { + SerialPrint("E", "Sensor", "'" + _humSet.key + "' data: " + String(value)); + } + } +} + +MySensorDhtVector* mySensorDht = nullptr; + +void dhtTmp() { + myLineParsing.update(); + String interval = myLineParsing.gint(); + String pin = myLineParsing.gpin(); + String key = myLineParsing.gkey(); + String map = myLineParsing.gmap(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); + int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); + int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); + int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); + + tmpParams buf; + + buf.interval = interval.toInt() * 1000; + buf.key = key; + buf.pin = pin.toInt(); + buf.map1 = map1; + buf.map2 = map2; + buf.map3 = map3; + buf.map4 = map4; + buf.c = c.toFloat(); + + dhtTmp_EnterCounter++; + + static bool firstTime = true; + if (firstTime) mySensorDht = new MySensorDhtVector(); + firstTime = false; + mySensorDht->push_back(SensorDht()); + mySensorDht->at(dhtTmp_EnterCounter).tmpInit(buf); +} + +void dhtHum() { + myLineParsing.update(); + String interval = myLineParsing.gint(); + String pin = myLineParsing.gpin(); + String key = myLineParsing.gkey(); + String map = myLineParsing.gmap(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); + int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); + int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); + int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); + + humParams buf; + + buf.interval = interval.toInt() * 1000; + buf.key = key; + buf.pin = pin.toInt(); + buf.map1 = map1; + buf.map2 = map2; + buf.map3 = map3; + buf.map4 = map4; + buf.c = c.toFloat(); + + dhtHum_EnterCounter++; + + static bool firstTime = true; + if (firstTime) mySensorDht = new MySensorDhtVector(); + firstTime = false; + mySensorDht->push_back(SensorDht()); + mySensorDht->at(dhtHum_EnterCounter).humInit(buf); +} diff --git a/src/main.cpp b/src/main.cpp index d4673839..b9981f51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ #include "items/vSensorAnalog.h" #include "items/vSensorDallas.h" #include "items/vSensorUltrasonic.h" +#include "items/vSensorDht.h" void not_async_actions(); @@ -134,4 +135,10 @@ void loop() { mySensorAnalog->at(i).loop(); } } + if (mySensorDht != nullptr) { + for (unsigned int i = 0; i < mySensorDht->size(); i++) { + mySensorDht->at(i).loopTmp(); + mySensorDht->at(i).loopHum(); + } + } } \ No newline at end of file From 0df63ba6425c2c4c4a0c4ec9c2ce11631b12b9ac Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Mon, 21 Dec 2020 20:51:59 +0100 Subject: [PATCH 70/94] =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BufferExecute.cpp | 2 +- src/items/vSensorDht.cpp | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 30bb9b85..7d1c88ef 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -86,7 +86,7 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), bme280Temp); } else if (order == F("bme280-hum")) { - //sCmd.addCommand(order.c_str(), bme280Hum); + sCmd.addCommand(order.c_str(), bme280Hum); } else if (order == F("bme280-press")) { sCmd.addCommand(order.c_str(), bme280Press); diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 2aa10f24..44920100 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -18,6 +18,7 @@ void SensorDht::tmpInit(const tmpParams& tmpSet) { dht = new DHTesp(); } dht->setup(_tmpSet.pin, DHTesp::DHT11); + _tmpSet.interval = dht->getMinimumSamplingPeriod() + _tmpSet.interval; } void SensorDht::humInit(const humParams& humSet) { @@ -26,6 +27,7 @@ void SensorDht::humInit(const humParams& humSet) { dht = new DHTesp(); } dht->setup(_tmpSet.pin, DHTesp::DHT11); + _tmpSet.interval = dht->getMinimumSamplingPeriod() + _tmpSet.interval; } void SensorDht::loopTmp() { @@ -49,10 +51,10 @@ void SensorDht::loopHum() { void SensorDht::readTmp() { float value; static int counter; - if (dht->getStatus() != 0 && counter < 5) { - counter++; - SerialPrint("E", "Sensor", "Disconnected"); - } else { + //if (dht->getStatus() != 0 && counter < 5) { + // counter++; + // SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); + //} else { counter = 0; value = dht->getTemperature(); if (String(value) != "nan") { @@ -65,16 +67,16 @@ void SensorDht::readTmp() { } else { SerialPrint("E", "Sensor", "'" + _tmpSet.key + "' data: " + String(value)); } - } + //} } void SensorDht::readHum() { float value; static int counter; - if (dht->getStatus() != 0 && counter < 5) { - counter++; - SerialPrint("E", "Sensor", "Disconnected"); - } else { + //if (dht->getStatus() != 0 && counter < 5) { + // counter++; + // SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); + //} else { counter = 0; value = dht->getHumidity(); if (String(value) != "nan") { @@ -87,7 +89,7 @@ void SensorDht::readHum() { } else { SerialPrint("E", "Sensor", "'" + _humSet.key + "' data: " + String(value)); } - } + //} } MySensorDhtVector* mySensorDht = nullptr; From 5c664594af5de265fa9713d19a31eef68cbb5bc2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Tue, 22 Dec 2020 12:38:05 +0100 Subject: [PATCH 71/94] in progress not working version!!! --- data/items/items.txt | 50 ++++---- data/set.device.json.gz | Bin 2770 -> 2681 bytes data_ungzip/set.device.json | 45 +++---- data_ungzip/set.device.json.gz | Bin 2770 -> 0 bytes include/Global.h | 3 +- include/ItemsList.h | 2 +- include/items/vSensorDht.h | 46 ++----- include/main.h | 3 - src/BufferExecute.cpp | 4 +- src/Global.cpp | 3 +- src/Init.cpp | 3 +- src/ItemsList.cpp | 33 ++--- src/Web.cpp | 2 +- src/items/vSensorDht.cpp | 216 +++++++++++++-------------------- src/main.cpp | 12 +- 15 files changed, 171 insertions(+), 251 deletions(-) delete mode 100644 data_ungzip/set.device.json.gz delete mode 100644 include/main.h diff --git a/data/items/items.txt b/data/items/items.txt index 19622ab8..f9d180b4 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -1,27 +1,27 @@ -1;0;button-out;id;toggle;Кнопки;Освещение;order;gpio -2;0;button-out;id;toggle;Кнопки;Освещение;order;gpio;inv[1] -3;0;button-out;id;toggle;Кнопки;Освещение;order -4;0;button-in;id;toggle;Кнопки;Освещение;order;gpio;db[20] -5;0;pwm-out;id;range;Ползунки;Яркость;order;gpio -6;0;inoutput;id;inputDigit;Ввод;Введите#цифру;order -7;0;inoutput;id;inputTime;Ввод;Введите#время;order -8;0;inoutput;id;anydata;Вывод;Сигнализация;order -9;0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10] -10;0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10] -11;0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10] -12;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1];int[10] -13;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1];int[10] -14;0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1];int[10] -15;0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1];int[10] -16;0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] -17;0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] -18;0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] -19;0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] -20;0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] -21;0;impuls-out;id;na;na;na;order;gpio -22;0;count-down;id;anydata;Таймер;Обратный#отчет;order -23;0;inoutput;id;anydata;Вывод;Вывод#uart;order -24;0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100] -25;0;uptime;id;anydataTime;Системные;%name%#uptime;order +0;button-out;id;toggle;Кнопки;Освещение;order;gpio* +0;button-out;id;toggle;Кнопки;Освещение;order;gpio;inv[1]* +0;button-out;id;toggle;Кнопки;Освещение;order* +0;button-in;id;toggle;Кнопки;Освещение;order;gpio;db[20]* +0;pwm-out;id;range;Ползунки;Яркость;order;gpio* +0;inoutput;id;inputDigit;Ввод;Введите#цифру;order* +0;inoutput;id;inputTime;Ввод;Введите#время;order* +0;inoutput;id;anydata;Вывод;Сигнализация;order* +0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* +0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* +0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* +0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1];int[10] +0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1];int[10]* +0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1];int[10] +0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1];int[10]* +0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] +0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] +0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1]* +0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] +0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1]* +0;impuls-out;id;na;na;na;order;gpio* +0;count-down;id;anydata;Таймер;Обратный#отчет;order* +0;inoutput;id;anydata;Вывод;Вывод#uart;order* +0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100]* +0;uptime;id;anydataTime;Системные;%name%#uptime;order* diff --git a/data/set.device.json.gz b/data/set.device.json.gz index ab29128af32f371342a7642eb7b2fb90aee70eca..e769d3e93a9f7ac1c0894c6a61f5ed060d48393a 100644 GIT binary patch literal 2681 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 YZzQW7d(4MN7B4#c4R`m9P%kY20QtaSfdBvi diff --git a/data_ungzip/set.device.json b/data_ungzip/set.device.json index fcba5100..47d120a8 100644 --- a/data_ungzip/set.device.json +++ b/data_ungzip/set.device.json @@ -78,31 +78,26 @@ "style": "display:inline", "title": { "#": "Выберите элемент из списка", - "/set?addItem=1-btn": "1.Кнопка управляющая пином", - "/set?addItem=2-btn": "2.Кнопка управляющая пином (с инверсией)", - "/set?addItem=3-btn": "3.Кнопка виртуальная (не привязанная к пину, для использования в сценариях)", - "/set?addItem=4-btn": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", - "/set?addItem=5-pwm": "5.Широтно импульсная модуляция pwm", - "/set?addItem=6-dgt": "6.Окно ввода цифровых значений", - "/set?addItem=7-tm": "7.Окно ввода времени", - "/set?addItem=8-txt": "8.Окно вывода любого текста, предупреждения, цифры", - "/set?addItem=9-adc": "9.Датчик аналоговый, чтение аналогового входа", - "/set?addItem=10-tmp": "10.Датчик температуры ds18b20", - "/set?addItem=11-rng": "11.Датчик расстояния ультрозвуковой JSN-SR04T, HC-SR04, HY-SRF05", - "/set?addItem=12-tmp": "12.Датчик температуры DHT11", - "/set?addItem=13-hmd": "13.Датчик влажности DHT11", - "/set?addItem=14-tmp": "14.Датчик температуры DHT22, DHT33, DHT44, AM2302, RHT03", - "/set?addItem=15-hmd": "15.Датчик влажности DHT22, DHT33, DHT44, AM2302, RHT03", - "/set?addItem=16-tmp": "16.Датчик температуры bme280", - "/set?addItem=17-hmd": "17.Датчик влажности bme280", - "/set?addItem=18-ps": "18.Датчик давления bme280", - "/set?addItem=19-tmp": "19.Датчик температуры bmp280", - "/set?addItem=20-ps": "20.Датчик давления bmp280", - "/set?addItem=21-ips": "21.Создать импульсы через заданный промежуток времени (управление шд)", - "/set?addItem=22-cnt": "22.Таймер обратного отчета", - "/set?addItem=23-txt": "23.Виджет для отображения информации полученной из uart, get-запроса, или по udp", - "/set?addItem=24-log": "24.Логгирование и вывод в график любой величины", - "/set?addItem=25-ut": "25.Отобразить время работы устройства" + "/set?addItem=1": "1.Кнопка управляющая пином", + "/set?addItem=2": "2.Кнопка управляющая пином (с инверсией)", + "/set?addItem=3": "3.Кнопка виртуальная (не привязанная к пину, для использования в сценариях)", + "/set?addItem=4": "4.Кнопка физическая, чтение состояния пина (подключается провдами к устройству)", + "/set?addItem=5": "5.Широтно импульсная модуляция pwm", + "/set?addItem=6": "6.Окно ввода цифровых значений", + "/set?addItem=7": "7.Окно ввода времени", + "/set?addItem=8": "8.Окно вывода любого текста, предупреждения, цифры", + "/set?addItem=9": "9.Датчик аналоговый, чтение аналогового входа", + "/set?addItem=10": "10.Датчик температуры ds18b20", + "/set?addItem=11": "11.Датчик расстояния ультрозвуковой JSN-SR04T, HC-SR04, HY-SRF05", + "/set?addItem=12": "12.Датчик температуры и влажности DHT11", + "/set?addItem=13": "13.Датчик температуры и влажности DHT22, DHT33, DHT44, AM2302, RHT03", + "/set?addItem=14": "14.Датчик температуры, влажности и давления bme280", + "/set?addItem=15": "15.Датчик температуры и давления bmp280", + "/set?addItem=16": "16.Создать импульсы через заданный промежуток времени (управление шд)", + "/set?addItem=17": "17.Таймер обратного отчета", + "/set?addItem=18": "18.Виджет для отображения информации полученной из uart, get-запроса, или по udp", + "/set?addItem=19": "19.Логгирование и вывод в график любой величины", + "/set?addItem=20": "20.Отобразить время работы устройства" } }, { diff --git a/data_ungzip/set.device.json.gz b/data_ungzip/set.device.json.gz deleted file mode 100644 index ab29128af32f371342a7642eb7b2fb90aee70eca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2770 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 YZzQW7d(4MN7B4#c4R`m9P%kY20QtaSfdBvi diff --git a/include/Global.h b/include/Global.h index db71e643..d018a37d 100644 --- a/include/Global.h +++ b/include/Global.h @@ -88,8 +88,7 @@ extern int countDown_EnterCounter; extern String logging_KeyList; extern int logging_EnterCounter; //========================================= -extern int dhtTmp_EnterCounter; -extern int dhtHum_EnterCounter; +extern int dht_EnterCounter; //========================================= // Sensors diff --git a/include/ItemsList.h b/include/ItemsList.h index 2961d9e1..573ad33a 100644 --- a/include/ItemsList.h +++ b/include/ItemsList.h @@ -4,7 +4,7 @@ #include "Global.h" extern void itemsListInit(); -extern void addItem2(String param); +extern void addItem2(int num); extern void addItem(String name); extern void addPreset(String name); extern void addPreset2(int num); diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h index 744af653..c95992b3 100644 --- a/include/items/vSensorDht.h +++ b/include/items/vSensorDht.h @@ -1,6 +1,7 @@ #pragma once #include #include + #include "Global.h" #include "GyverFilters.h" @@ -10,51 +11,30 @@ class SensorDht; typedef std::vector MySensorDhtVector; -struct tmpParams { - unsigned long currentMillis; - unsigned long prevMillis; - unsigned long difference; - unsigned long interval; +struct params { + String type; + String value; String key; - unsigned int pin; - int map1; - int map2; - int map3; - int map4; - float c; -}; - -struct humParams { - unsigned long currentMillis; - unsigned long prevMillis; - unsigned long difference; unsigned long interval; - String key; unsigned int pin; - int map1; - int map2; - int map3; - int map4; float c; }; class SensorDht { public: - SensorDht(); + SensorDht(const params& paramsTmp, const params& paramsHum); ~SensorDht(); - void loopTmp(); - void loopHum(); - - void readTmp(); - void readHum(); - - void tmpInit(const tmpParams& tmpSet); - void humInit(const humParams& humSet); + void loop(); + void readTmpHum(); private: - tmpParams _tmpSet; - humParams _humSet; + params _paramsTmp; + params _paramsHum; + + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; }; extern MySensorDhtVector* mySensorDht; diff --git a/include/main.h b/include/main.h deleted file mode 100644 index 0d48dfc5..00000000 --- a/include/main.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -//void myCallback; \ No newline at end of file diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 7d1c88ef..11cd2a00 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -75,10 +75,10 @@ void csvCmdExecute(String& cmdStr) { } #ifdef SensorDhtEnabled else if (order == F("dht-temp")) { - sCmd.addCommand(order.c_str(), dhtTmp); + //sCmd.addCommand(order.c_str(), dhtTmp); } else if (order == F("dht-hum")) { - sCmd.addCommand(order.c_str(), dhtHum); + //sCmd.addCommand(order.c_str(), dhtHum); } #endif #ifdef SensorBme280Enabled diff --git a/src/Global.cpp b/src/Global.cpp index 0e98c22a..39853d25 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -59,8 +59,7 @@ int countDown_EnterCounter = -1; String logging_KeyList = ""; int logging_EnterCounter = -1; //========================================= -int dhtTmp_EnterCounter = -1; -int dhtHum_EnterCounter = -1; +int dht_EnterCounter = -1; //========================================= // Sensors diff --git a/src/Init.cpp b/src/Init.cpp index 983dbc40..195464d1 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -92,8 +92,7 @@ void deviceInit() { countDown_KeyList = ""; countDown_EnterCounter = -1; //=================================== - dhtTmp_EnterCounter = -1; - dhtHum_EnterCounter = -1; + dht_EnterCounter = -1; //========================================= #ifdef LAYOUT_IN_RAM diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index aa046e99..2f810729 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -23,40 +23,43 @@ void itemsListInit() { SerialPrint("I", F("Items"), F("Items Init")); } -void addItem2(String param) { - int num = selectToMarker(param, "-").toInt(); +void addItem2(int num) { File configFile = FileFS.open("/items/items.txt", "r"); if (!configFile) { return; } configFile.seek(0, SeekSet); String seachingLine; - + int i = 0; while (configFile.position() != configFile.size()) { - String item = configFile.readStringUntil('\n'); - int tmpNum = selectToMarker(item, ";").toInt(); - if (tmpNum == num) { - seachingLine = item; + i++; + String item = configFile.readStringUntil('*'); + if (i == num) { + if (i == 1) { + seachingLine = "\n" + item; + } else { + seachingLine = item; + } break; } } configFile.close(); - String name = deleteBeforeDelimiter(param, "-"); + //while (seachingLine.length()) { + //String tmp = selectToMarker(seachingLine, "\n"); + randomSeed(micros()); unsigned int rnd = random(0, 1000); - seachingLine.replace("id", name + String(rnd)); + seachingLine.replace("id", String(rnd)); seachingLine.replace("order", String(getNewElementNumber("order.txt"))); - if (seachingLine.indexOf("gpio") != -1) { seachingLine.replace("gpio", "pin[" + String(getFreePinAll()) + "]"); } + + //seachingLine = deleteBeforeDelimiter(seachingLine, ","); + //} - seachingLine = deleteBeforeDelimiter(seachingLine, ";"); - seachingLine.replace("\r\n", ""); - seachingLine.replace("\r", ""); - seachingLine.replace("\n", ""); - addFile(DEVICE_CONFIG_FILE, "\n" + seachingLine); + addFile(DEVICE_CONFIG_FILE, seachingLine); Serial.println(seachingLine); } diff --git a/src/Web.cpp b/src/Web.cpp index a6cf2b84..b01713ce 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -20,7 +20,7 @@ void web_init() { server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { //==============================set.device.json==================================================================================================== if (request->hasArg(F("addItem"))) { - addItem2(request->getParam("addItem")->value()); + addItem2(request->getParam("addItem")->value().toInt()); request->redirect("/?set.device"); } diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 44920100..70fc3328 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -8,156 +8,104 @@ DHTesp* dht = nullptr; -SensorDht::SensorDht() {} +SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { + _paramsTmp = params(paramsTmp); + _paramsHum = params(paramsHum); + + if (!dht) { + dht = new DHTesp(); + } + + dht->setup(_paramsTmp.pin, DHTesp::DHT11); + + //dht->getMinimumSamplingPeriod() +} SensorDht::~SensorDht() {} -void SensorDht::tmpInit(const tmpParams& tmpSet) { - _tmpSet = tmpParams(tmpSet); - if (!dht) { - dht = new DHTesp(); - } - dht->setup(_tmpSet.pin, DHTesp::DHT11); - _tmpSet.interval = dht->getMinimumSamplingPeriod() + _tmpSet.interval; -} - -void SensorDht::humInit(const humParams& humSet) { - _humSet = humParams(humSet); - if (!dht) { - dht = new DHTesp(); - } - dht->setup(_tmpSet.pin, DHTesp::DHT11); - _tmpSet.interval = dht->getMinimumSamplingPeriod() + _tmpSet.interval; -} - -void SensorDht::loopTmp() { - _tmpSet.currentMillis = millis(); - _tmpSet.difference = _tmpSet.currentMillis - _tmpSet.prevMillis; - if (_tmpSet.difference >= _tmpSet.interval) { - _tmpSet.prevMillis = millis(); - readTmp(); - } -} - -void SensorDht::loopHum() { - _humSet.currentMillis = millis(); - _humSet.difference = _humSet.currentMillis - _humSet.prevMillis; - if (_humSet.difference >= _humSet.interval) { - _humSet.prevMillis = millis(); - readHum(); - } -} - -void SensorDht::readTmp() { - float value; - static int counter; - //if (dht->getStatus() != 0 && counter < 5) { - // counter++; - // SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); - //} else { - counter = 0; - value = dht->getTemperature(); - if (String(value) != "nan") { - //value = map(value, _tmpSet.map1, _tmpSet.map2, _tmpSet.map3, _tmpSet.map4); - value = value * _tmpSet.c; - eventGen2(_tmpSet.key, String(value)); - jsonWriteStr(configLiveJson, _tmpSet.key, String(value)); - publishStatus(_tmpSet.key, String(value)); - SerialPrint("I", "Sensor", "'" + _tmpSet.key + "' data: " + String(value)); - } else { - SerialPrint("E", "Sensor", "'" + _tmpSet.key + "' data: " + String(value)); - } +void SensorDht::loop() { + //currentMillis = millis(); + //difference = currentMillis - prevMillis; + //if (difference >= _myParams.interval) { + // prevMillis = millis(); + // readTmpHum(); //} } -void SensorDht::readHum() { - float value; - static int counter; - //if (dht->getStatus() != 0 && counter < 5) { - // counter++; - // SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); +void SensorDht::readTmpHum() { + //float tmp; + //float hum; + //tmp = dht->getTemperature(); + //hum = dht->getHumidity(); + // + //if (String(tmp) != "nan" && String(hum) != "nan") { + // if (_myParams.type == "tmp") { + // } + // + // if (_myParams.type == "hum") { + // } + // + // tmp = tmp * _tmpSet.c; + // hum = hum * _humSet.c; + // + // if (_tmpSet.interval > 0) { + // eventGen2(_tmpSet.key, String(tmp)); + // jsonWriteStr(configLiveJson, _tmpSet.key, String(tmp)); + // publishStatus(_tmpSet.key, String(tmp)); + // SerialPrint("I", "Sensor", "'" + _tmpSet.key + "' data: " + String(tmp)); + // } + // + // if (_humSet.interval > 0) { + // eventGen2(_humSet.key, String(hum)); + // jsonWriteStr(configLiveJson, _humSet.key, String(hum)); + // publishStatus(_humSet.key, String(hum)); + // SerialPrint("I", "Sensor", "'" + _humSet.key + "' data: " + String(hum)); + // } + // //} else { - counter = 0; - value = dht->getHumidity(); - if (String(value) != "nan") { - //value = map(value, _humSet.map1, _humSet.map2, _humSet.map3, _humSet.map4); - value = value * _humSet.c; - eventGen2(_humSet.key, String(value)); - jsonWriteStr(configLiveJson, _humSet.key, String(value)); - publishStatus(_humSet.key, String(value)); - SerialPrint("I", "Sensor", "'" + _humSet.key + "' data: " + String(value)); - } else { - SerialPrint("E", "Sensor", "'" + _humSet.key + "' data: " + String(value)); - } + // SerialPrint("E", "Sensor DHT", "Error"); //} } +//if (dht->getStatus() != 0 && counter < 5) { +// counter++; +// SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); +//} else { + MySensorDhtVector* mySensorDht = nullptr; -void dhtTmp() { +void dhtSensor() { myLineParsing.update(); + String type = myLineParsing.gtype(); + String value = myLineParsing.gval(); String interval = myLineParsing.gint(); String pin = myLineParsing.gpin(); String key = myLineParsing.gkey(); - String map = myLineParsing.gmap(); String c = myLineParsing.gc(); myLineParsing.clear(); - - int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); - int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); - int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); - int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); - - tmpParams buf; - - buf.interval = interval.toInt() * 1000; - buf.key = key; - buf.pin = pin.toInt(); - buf.map1 = map1; - buf.map2 = map2; - buf.map3 = map3; - buf.map4 = map4; - buf.c = c.toFloat(); - - dhtTmp_EnterCounter++; - - static bool firstTime = true; - if (firstTime) mySensorDht = new MySensorDhtVector(); - firstTime = false; - mySensorDht->push_back(SensorDht()); - mySensorDht->at(dhtTmp_EnterCounter).tmpInit(buf); -} - -void dhtHum() { - myLineParsing.update(); - String interval = myLineParsing.gint(); - String pin = myLineParsing.gpin(); - String key = myLineParsing.gkey(); - String map = myLineParsing.gmap(); - String c = myLineParsing.gc(); - myLineParsing.clear(); - - int map1 = selectFromMarkerToMarker(map, ",", 0).toInt(); - int map2 = selectFromMarkerToMarker(map, ",", 1).toInt(); - int map3 = selectFromMarkerToMarker(map, ",", 2).toInt(); - int map4 = selectFromMarkerToMarker(map, ",", 3).toInt(); - - humParams buf; - - buf.interval = interval.toInt() * 1000; - buf.key = key; - buf.pin = pin.toInt(); - buf.map1 = map1; - buf.map2 = map2; - buf.map3 = map3; - buf.map4 = map4; - buf.c = c.toFloat(); - - dhtHum_EnterCounter++; - - static bool firstTime = true; - if (firstTime) mySensorDht = new MySensorDhtVector(); - firstTime = false; - mySensorDht->push_back(SensorDht()); - mySensorDht->at(dhtHum_EnterCounter).humInit(buf); + static params paramsTmp; + static params paramsHum; + if (value = "tmp") { + paramsTmp.type = type; + paramsTmp.value = value; + paramsTmp.key = key; + paramsTmp.interval = interval.toInt() * 1000; + paramsTmp.pin = pin.toInt(); + paramsTmp.c = c.toFloat(); + } + if (value = "hum") { + paramsHum.type = type; + paramsHum.value = value; + paramsHum.key = key; + paramsHum.interval = interval.toInt() * 1000; + paramsHum.pin = pin.toInt(); + paramsHum.c = c.toFloat(); + } + dht_EnterCounter++; + if (dht_EnterCounter == 2) { + static bool firstTime = true; + if (firstTime) mySensorDht = new MySensorDhtVector(); + firstTime = false; + mySensorDht->push_back(SensorDht(paramsTmp, paramsHum)); + } } diff --git a/src/main.cpp b/src/main.cpp index b9981f51..350eca5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -135,10 +135,10 @@ void loop() { mySensorAnalog->at(i).loop(); } } - if (mySensorDht != nullptr) { - for (unsigned int i = 0; i < mySensorDht->size(); i++) { - mySensorDht->at(i).loopTmp(); - mySensorDht->at(i).loopHum(); - } - } + //if (mySensorDht != nullptr) { + // for (unsigned int i = 0; i < mySensorDht->size(); i++) { + // mySensorDht->at(i).loopTmp(); + // mySensorDht->at(i).loopHum(); + // } + //} } \ No newline at end of file From 595574342662d3c8b83dea8932c315ba29f8876f Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 24 Dec 2020 01:05:54 +0100 Subject: [PATCH 72/94] =?UTF-8?q?dht=20=D0=BF=D0=BE=D0=BA=D0=B0=20=D0=B1?= =?UTF-8?q?=D0=B5=D0=B7=20=D0=BA=D0=BE=D1=80=D0=BE=D0=B1=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/items/vSensorDht.h | 6 +-- src/items/vSensorDht.cpp | 91 ++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 55 deletions(-) diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h index c95992b3..b3848c84 100644 --- a/include/items/vSensorDht.h +++ b/include/items/vSensorDht.h @@ -13,7 +13,6 @@ typedef std::vector MySensorDhtVector; struct params { String type; - String value; String key; unsigned long interval; unsigned int pin; @@ -31,8 +30,9 @@ class SensorDht { private: params _paramsTmp; params _paramsHum; - - unsigned long currentMillis; + + unsigned int interval; + unsigned long prevMillis; unsigned long difference; }; diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 70fc3328..18b22f09 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -18,91 +18,78 @@ SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { dht->setup(_paramsTmp.pin, DHTesp::DHT11); - //dht->getMinimumSamplingPeriod() + interval = _paramsTmp.interval < _paramsHum.interval ? _paramsTmp.interval : _paramsHum.interval; + interval = interval + dht->getMinimumSamplingPeriod(); } SensorDht::~SensorDht() {} void SensorDht::loop() { - //currentMillis = millis(); - //difference = currentMillis - prevMillis; - //if (difference >= _myParams.interval) { - // prevMillis = millis(); - // readTmpHum(); - //} + difference = millis() - prevMillis; + if (difference >= interval) { + prevMillis = millis(); + readTmpHum(); + } } void SensorDht::readTmpHum() { - //float tmp; - //float hum; - //tmp = dht->getTemperature(); - //hum = dht->getHumidity(); - // - //if (String(tmp) != "nan" && String(hum) != "nan") { - // if (_myParams.type == "tmp") { - // } - // - // if (_myParams.type == "hum") { - // } - // - // tmp = tmp * _tmpSet.c; - // hum = hum * _humSet.c; - // - // if (_tmpSet.interval > 0) { - // eventGen2(_tmpSet.key, String(tmp)); - // jsonWriteStr(configLiveJson, _tmpSet.key, String(tmp)); - // publishStatus(_tmpSet.key, String(tmp)); - // SerialPrint("I", "Sensor", "'" + _tmpSet.key + "' data: " + String(tmp)); - // } - // - // if (_humSet.interval > 0) { - // eventGen2(_humSet.key, String(hum)); - // jsonWriteStr(configLiveJson, _humSet.key, String(hum)); - // publishStatus(_humSet.key, String(hum)); - // SerialPrint("I", "Sensor", "'" + _humSet.key + "' data: " + String(hum)); - // } - // - //} else { - // SerialPrint("E", "Sensor DHT", "Error"); - //} -} + float tmp; + float hum; -//if (dht->getStatus() != 0 && counter < 5) { -// counter++; -// SerialPrint("E", "Sensor", "Disconnected " + String(counter) + " " + dht->getStatusString()); -//} else { + tmp = dht->getTemperature(); + hum = dht->getHumidity(); + + if (String(tmp) != "nan" && String(hum) != "nan") { + tmp = tmp * _paramsTmp.c; + hum = hum * _paramsHum.c; + + eventGen2(_paramsTmp.key, String(tmp)); + jsonWriteStr(configLiveJson, _paramsTmp.key, String(tmp)); + publishStatus(_paramsTmp.key, String(tmp)); + SerialPrint("I", "Sensor", "'" + _paramsTmp.key + "' data: " + String(tmp)); + + eventGen2(_paramsHum.key, String(hum)); + jsonWriteStr(configLiveJson, _paramsHum.key, String(hum)); + publishStatus(_paramsHum.key, String(hum)); + SerialPrint("I", "Sensor", "'" + _paramsHum.key + "' data: " + String(hum)); + + } else { + SerialPrint("E", "Sensor DHT", "Error"); + } +} MySensorDhtVector* mySensorDht = nullptr; void dhtSensor() { myLineParsing.update(); String type = myLineParsing.gtype(); - String value = myLineParsing.gval(); String interval = myLineParsing.gint(); String pin = myLineParsing.gpin(); String key = myLineParsing.gkey(); String c = myLineParsing.gc(); myLineParsing.clear(); + + static int enterCnt = -1; + enterCnt++; + static params paramsTmp; static params paramsHum; - if (value = "tmp") { + + if (enterCnt == 0) { paramsTmp.type = type; - paramsTmp.value = value; paramsTmp.key = key; paramsTmp.interval = interval.toInt() * 1000; paramsTmp.pin = pin.toInt(); paramsTmp.c = c.toFloat(); } - if (value = "hum") { + + if (enterCnt == 1) { paramsHum.type = type; - paramsHum.value = value; paramsHum.key = key; paramsHum.interval = interval.toInt() * 1000; paramsHum.pin = pin.toInt(); paramsHum.c = c.toFloat(); - } - dht_EnterCounter++; - if (dht_EnterCounter == 2) { + static bool firstTime = true; if (firstTime) mySensorDht = new MySensorDhtVector(); firstTime = false; From 99134cb6fde32efe887df308405a6f6d8aff65cd Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 24 Dec 2020 01:41:15 +0100 Subject: [PATCH 73/94] dht working --- data/items/items.txt | 12 +++++------- include/items/vSensorDht.h | 7 +++---- src/BufferExecute.cpp | 7 ++----- src/items/vSensorDht.cpp | 6 +++--- src/main.cpp | 13 ++++++------- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/data/items/items.txt b/data/items/items.txt index f9d180b4..8f11e97e 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -9,10 +9,10 @@ 0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* 0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* 0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht11];c[1];int[10] -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht11];c[1];int[10]* -0;dht-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];type[dht22];c[1];int[10] -0;dht-hum;id;anydataHum;Сенсоры;Влажность;order;pin[2];type[dht22];c[1];int[10]* +0;dht;dhtTmp;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1];int[10] +0;dht;dhtHum;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht11];c[1];int[10]* +0;dht;dhtTmp;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1];int[10] +0;dht;dhtHum;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht22];c[1];int[10]* 0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] 0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] 0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1]* @@ -22,6 +22,4 @@ 0;count-down;id;anydata;Таймер;Обратный#отчет;order* 0;inoutput;id;anydata;Вывод;Вывод#uart;order* 0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100]* -0;uptime;id;anydataTime;Системные;%name%#uptime;order* - - +0;uptime;id;anydataTime;Системные;%name%#uptime;order* \ No newline at end of file diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h index b3848c84..dc86d285 100644 --- a/include/items/vSensorDht.h +++ b/include/items/vSensorDht.h @@ -31,13 +31,12 @@ class SensorDht { params _paramsTmp; params _paramsHum; - unsigned int interval; - + unsigned int _interval; + unsigned long prevMillis; unsigned long difference; }; extern MySensorDhtVector* mySensorDht; -extern void dhtTmp(); -extern void dhtHum(); \ No newline at end of file +extern void dhtSensor(); \ No newline at end of file diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 11cd2a00..5ac53b74 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -74,11 +74,8 @@ void csvCmdExecute(String& cmdStr) { sCmd.addCommand(order.c_str(), dallas); } #ifdef SensorDhtEnabled - else if (order == F("dht-temp")) { - //sCmd.addCommand(order.c_str(), dhtTmp); - } - else if (order == F("dht-hum")) { - //sCmd.addCommand(order.c_str(), dhtHum); + else if (order == F("dht")) { + sCmd.addCommand(order.c_str(), dhtSensor); } #endif #ifdef SensorBme280Enabled diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 18b22f09..64851f51 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -18,15 +18,15 @@ SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { dht->setup(_paramsTmp.pin, DHTesp::DHT11); - interval = _paramsTmp.interval < _paramsHum.interval ? _paramsTmp.interval : _paramsHum.interval; - interval = interval + dht->getMinimumSamplingPeriod(); + _interval = _paramsTmp.interval < _paramsHum.interval ? _paramsTmp.interval : _paramsHum.interval; + _interval = _interval + dht->getMinimumSamplingPeriod(); } SensorDht::~SensorDht() {} void SensorDht::loop() { difference = millis() - prevMillis; - if (difference >= interval) { + if (difference >= _interval) { prevMillis = millis(); readTmpHum(); } diff --git a/src/main.cpp b/src/main.cpp index 350eca5c..4ae6557e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,8 +24,8 @@ #include "items/vLogging.h" #include "items/vSensorAnalog.h" #include "items/vSensorDallas.h" -#include "items/vSensorUltrasonic.h" #include "items/vSensorDht.h" +#include "items/vSensorUltrasonic.h" void not_async_actions(); @@ -135,10 +135,9 @@ void loop() { mySensorAnalog->at(i).loop(); } } - //if (mySensorDht != nullptr) { - // for (unsigned int i = 0; i < mySensorDht->size(); i++) { - // mySensorDht->at(i).loopTmp(); - // mySensorDht->at(i).loopHum(); - // } - //} + if (mySensorDht != nullptr) { + for (unsigned int i = 0; i < mySensorDht->size(); i++) { + mySensorDht->at(i).loop(); + } + } } \ No newline at end of file From 1a097ff2b7fc0cfca3513d881d7603a3a6dddfdd Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 24 Dec 2020 01:58:14 +0100 Subject: [PATCH 74/94] change table --- data/items/items.txt | 50 ++++++++++++++++++++++---------------------- src/ItemsList.cpp | 6 ------ 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/data/items/items.txt b/data/items/items.txt index 8f11e97e..a460c8c1 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -1,25 +1,25 @@ -0;button-out;id;toggle;Кнопки;Освещение;order;gpio* -0;button-out;id;toggle;Кнопки;Освещение;order;gpio;inv[1]* -0;button-out;id;toggle;Кнопки;Освещение;order* -0;button-in;id;toggle;Кнопки;Освещение;order;gpio;db[20]* -0;pwm-out;id;range;Ползунки;Яркость;order;gpio* -0;inoutput;id;inputDigit;Ввод;Введите#цифру;order* -0;inoutput;id;inputTime;Ввод;Введите#время;order* -0;inoutput;id;anydata;Вывод;Сигнализация;order* -0;analog-adc;id;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* -0;dallas-temp;id;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* -0;ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* -0;dht;dhtTmp;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1];int[10] -0;dht;dhtHum;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht11];c[1];int[10]* -0;dht;dhtTmp;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1];int[10] -0;dht;dhtHum;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht22];c[1];int[10]* -0;bme280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1] -0;bme280-hum;id;anydataHum;Сенсоры;Влажность;order;addr[0x76];c[1] -0;bme280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1]* -0;bmp280-press;id;anydataPress;Сенсоры;Давление;order;addr[0x76];c[1] -0;bmp280-temp;id;anydataTemp;Сенсоры;Температура;order;addr[0x76];c[1]* -0;impuls-out;id;na;na;na;order;gpio* -0;count-down;id;anydata;Таймер;Обратный#отчет;order* -0;inoutput;id;anydata;Вывод;Вывод#uart;order* -0;logging;id;chart;Графики;История;order;val[any];int[60];cnt[100]* -0;uptime;id;anydataTime;Системные;%name%#uptime;order* \ No newline at end of file +0;button-out;btnid;toggle;Кнопки;Освещение;order;gpio* +0;button-out;btnid;toggle;Кнопки;Освещение;order;gpio;inv[1]* +0;button-out;btnid;toggle;Кнопки;Освещение;order* +0;button-in;btnid;toggle;Кнопки;Освещение;order;gpio;db[20]* +0;pwm-out;pwmid;range;Ползунки;Яркость;order;gpio* +0;inoutput;dgtid;inputDigit;Ввод;Введите#цифру;order* +0;inoutput;tmid;inputTime;Ввод;Введите#время;order* +0;inoutput;txtid;anydata;Вывод;Сигнализация;order* +0;analog-adc;adcid;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* +0;dallas-temp;tmpid;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* +0;ultrasonic-cm;cmid;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1];int[10] +0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht11];c[1];int[10]* +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1];int[10] +0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht22];c[1];int[10]* +0;bme280;tmpid;anydataTemp;Сенсоры;Температура;1;addr[0x76];c[1] +0;bme280;humid;anydataHum;Сенсоры;Влажность;2;addr[0x76];c[1] +0;bme280;prsid;anydataPress;Сенсоры;Давление;3;addr[0x76];c[1]* +0;bmp280;tmpid;anydataPress;Сенсоры;Давление;1;addr[0x76];c[1] +0;bmp280;humid;anydataTemp;Сенсоры;Температура;2;addr[0x76];c[1]* +0;impuls-out;impid;na;na;na;order;gpio* +0;count-down;cntid;anydata;Таймер;Обратный#отчет;order* +0;inoutput;txtid;anydata;Вывод;Вывод#uart;order* +0;logging;crtid;chart;Графики;История;order;val[any];int[60];cnt[100]* +0;uptime;uptid;anydataTime;Системные;%name%#uptime;order* \ No newline at end of file diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp index 2f810729..10415c85 100644 --- a/src/ItemsList.cpp +++ b/src/ItemsList.cpp @@ -45,9 +45,6 @@ void addItem2(int num) { } configFile.close(); - //while (seachingLine.length()) { - //String tmp = selectToMarker(seachingLine, "\n"); - randomSeed(micros()); unsigned int rnd = random(0, 1000); seachingLine.replace("id", String(rnd)); @@ -56,9 +53,6 @@ void addItem2(int num) { seachingLine.replace("gpio", "pin[" + String(getFreePinAll()) + "]"); } - //seachingLine = deleteBeforeDelimiter(seachingLine, ","); - //} - addFile(DEVICE_CONFIG_FILE, seachingLine); Serial.println(seachingLine); } From 7e4f852bf12668c4bf98f3aee1d47eb51147a956 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Thu, 24 Dec 2020 02:01:58 +0100 Subject: [PATCH 75/94] cleaning --- include/items/SensorDhtClass.h | 80 ---------------------------------- src/items/SensorDhtClass.cpp | 35 --------------- 2 files changed, 115 deletions(-) delete mode 100644 include/items/SensorDhtClass.h delete mode 100644 src/items/SensorDhtClass.cpp diff --git a/include/items/SensorDhtClass.h b/include/items/SensorDhtClass.h deleted file mode 100644 index 43cc236e..00000000 --- a/include/items/SensorDhtClass.h +++ /dev/null @@ -1,80 +0,0 @@ -//#pragma once -//#include "Consts.h" -//#ifdef SensorDhtEnabled -//#include -//#include "Class/LineParsing.h" -//#include "Global.h" -//#include "items/SensorConvertingClass.h" -// -//DHTesp dht; -//class SensorDhtClass : public SensorConvertingClass { -// public: -// SensorDhtClass() : SensorConvertingClass(){}; -// -// void SensorDhtInit() { -// if (_type == "dht11") { -// dht.setup(_pin.toInt(), DHTesp::DHT11); -// } -// if (_type == "dht22") { -// dht.setup(_pin.toInt(), DHTesp::DHT22); -// } -// sensorReadingMap10sec += _key + ","; -// -// //to do если надо будет читать несколько dht -// //dhtEnterCounter++; -// //jsonWriteInt(configOptionJson, _key + "_num", dhtEnterCounter); -// -// jsonWriteStr(configOptionJson, _key + "_map", _map); -// jsonWriteStr(configOptionJson, _key + "_с", _c); -// } -// -// void SensorDhtReadTemp(String key) { -// //to do если надо будет читать несколько dht -// //int cnt = jsonReadInt(configOptionJson, key + "_num"); -// float value; -// static int counter; -// if (dht.getStatus() != 0 && counter < 5) { -// counter++; -// //return; -// } else { -// counter = 0; -// value = dht.getTemperature(); -// if (String(value) != "nan") { -// //value = this->mapping(key, value); -// float valueFl = this->correction(key, value); -// eventGen2(key, String(valueFl)); -// jsonWriteStr(configLiveJson, key, String(valueFl)); -// publishStatus(key, String(valueFl)); -// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); -// } else { -// Serial.println("[E] sensor '" + key); -// } -// } -// } -// -// void SensorDhtReadHum(String key) { -// //to do если надо будет читать несколько dht -// //int cnt = jsonReadInt(configOptionJson, key + "_num"); -// float value; -// static int counter; -// if (dht.getStatus() != 0 && counter < 5) { -// counter++; -// //return; -// } else { -// counter = 0; -// value = dht.getHumidity(); -// if (String(value) != "nan") { -// //value = this->mapping(key, value); -// float valueFl = this->correction(key, value); -// eventGen2(key, String(valueFl)); -// jsonWriteStr(configLiveJson, key, String(valueFl)); -// publishStatus(key, String(valueFl)); -// SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); -// } else { -// Serial.println("[E] sensor '" + key); -// } -// } -// } -//}; -//extern SensorDhtClass mySensorDht; -//#endif \ No newline at end of file diff --git a/src/items/SensorDhtClass.cpp b/src/items/SensorDhtClass.cpp deleted file mode 100644 index c6f89671..00000000 --- a/src/items/SensorDhtClass.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//#include "Consts.h" -//#ifdef SensorDhtEnabled -//#include "items/SensorDhtClass.h" -//#include "BufferExecute.h" -////=========================================DHT Sensor================================================================== -////dht-temp;id;anydata;Сенсоры;Температура;order;pin;type[dht11];c[1] -////dht-hum;id;anydata;Сенсоры;Влажность;order;pin;type[dht11];c[1] -////========================================================================================================================================= -//SensorDhtClass mySensorDht; -//void dhtTemp() { -// mySensorDht.update(); -// String key = mySensorDht.gkey(); -// sCmd.addCommand(key.c_str(), dhtReadingTemp); -// mySensorDht.SensorDhtInit(); -// mySensorDht.clear(); -//} -//void dhtReadingTemp() { -// String key = sCmd.order(); -// mySensorDht.SensorDhtReadTemp(key); -//} -// -// -// -//void dhtHum() { -// mySensorDht.update(); -// String key = mySensorDht.gkey(); -// sCmd.addCommand(key.c_str(), dhtReadingHum); -// mySensorDht.SensorDhtInit(); -// mySensorDht.clear(); -//} -//void dhtReadingHum() { -// String key = sCmd.order(); -// mySensorDht.SensorDhtReadHum(key); -//} -//#endif \ No newline at end of file From e33ec57a1e7399154983cb1bf4a313c1d9c83dae Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Fri, 25 Dec 2020 22:07:27 +0100 Subject: [PATCH 76/94] fixed dht22 bug --- src/items/vSensorDht.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 64851f51..6ac8b83c 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -16,8 +16,13 @@ SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { dht = new DHTesp(); } - dht->setup(_paramsTmp.pin, DHTesp::DHT11); - + if (_paramsTmp.type == _paramsHum.type) { + if (_paramsTmp.type == "dht11") { + dht->setup(_paramsTmp.pin, DHTesp::DHT11); + } else if (_paramsTmp.type == "dht22") { + dht->setup(_paramsTmp.pin, DHTesp::DHT22); + } + } _interval = _paramsTmp.interval < _paramsHum.interval ? _paramsTmp.interval : _paramsHum.interval; _interval = _interval + dht->getMinimumSamplingPeriod(); } From ddc3a92c157379780985ed731e121b6b493de65b Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 26 Dec 2020 01:22:43 +0100 Subject: [PATCH 77/94] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BD=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=8C=D1=8E=20=D0=B4=D0=B0=D1=82=D1=87=D0=B8=D0=BA=20bme=20?= =?UTF-8?q?=D0=B8=20bmp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/items/items.txt | 14 ++-- include/Cmd.h | 1 - include/Consts.h | 2 - include/Global.h | 8 +-- include/items/SensorBme280Class.h | 57 --------------- include/items/SensorBmp280Class.h | 50 ------------- include/items/SensorConvertingClass.h | 32 --------- include/items/vSensorBme280.h | 39 ++++++++++ include/items/vSensorBmp280.h | 38 ++++++++++ include/items/vSensorDht.h | 10 ++- include/items/vSensorUltrasonic.h | 3 +- src/BufferExecute.cpp | 44 ++---------- src/Global.cpp | 3 - src/Init.cpp | 1 - src/items/SensorBme280Class.cpp | 47 ------------ src/items/SensorBmp280Class.cpp | 35 --------- src/items/sysUptime.cpp | 2 +- src/items/vSensorBme280.cpp | 100 ++++++++++++++++++++++++++ src/items/vSensorBmp280.cpp | 86 ++++++++++++++++++++++ src/items/vSensorDht.cpp | 23 +++--- src/main.cpp | 13 +++- 21 files changed, 307 insertions(+), 301 deletions(-) delete mode 100644 include/items/SensorBme280Class.h delete mode 100644 include/items/SensorBmp280Class.h delete mode 100644 include/items/SensorConvertingClass.h create mode 100644 include/items/vSensorBme280.h create mode 100644 include/items/vSensorBmp280.h delete mode 100644 src/items/SensorBme280Class.cpp delete mode 100644 src/items/SensorBmp280Class.cpp create mode 100644 src/items/vSensorBme280.cpp create mode 100644 src/items/vSensorBmp280.cpp diff --git a/data/items/items.txt b/data/items/items.txt index a460c8c1..fc7f1bd5 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -9,15 +9,15 @@ 0;analog-adc;adcid;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* 0;dallas-temp;tmpid;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* 0;ultrasonic-cm;cmid;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* -0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1];int[10] +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1] 0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht11];c[1];int[10]* -0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1];int[10] +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1] 0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht22];c[1];int[10]* -0;bme280;tmpid;anydataTemp;Сенсоры;Температура;1;addr[0x76];c[1] -0;bme280;humid;anydataHum;Сенсоры;Влажность;2;addr[0x76];c[1] -0;bme280;prsid;anydataPress;Сенсоры;Давление;3;addr[0x76];c[1]* -0;bmp280;tmpid;anydataPress;Сенсоры;Давление;1;addr[0x76];c[1] -0;bmp280;humid;anydataTemp;Сенсоры;Температура;2;addr[0x76];c[1]* +0;bme280;tmpid;anydataTemp;Сенсоры;Температура;1;c[1] +0;bme280;humid;anydataHum;Сенсоры;Влажность;2;c[1] +0;bme280;prsid;anydataPress;Сенсоры;Давление;3;c[1];addr[0x76];int[10]* +0;bmp280;tmpid;anydataTemp;Сенсоры;Температура;1;c[1] +0;bmp280;prsid;anydataPress;Сенсоры;Давление;3;c[1];addr[0x76];int[10]* 0;impuls-out;impid;na;na;na;order;gpio* 0;count-down;cntid;anydata;Таймер;Обратный#отчет;order* 0;inoutput;txtid;anydata;Вывод;Вывод#uart;order* diff --git a/include/Cmd.h b/include/Cmd.h index 2fec07d9..f19f54d1 100644 --- a/include/Cmd.h +++ b/include/Cmd.h @@ -4,7 +4,6 @@ extern void cmd_init(); -extern void sensorsInit(); //extern void levelPr(); //extern void ultrasonicCm(); diff --git a/include/Consts.h b/include/Consts.h index f6a7fc80..b4b677c6 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -47,8 +47,6 @@ //================================================================================================================================================================ enum TimerTask_t { WIFI_SCAN, WIFI_MQTT_CONNECTION_CHECK, - SENSORS10SEC, - SENSORS30SEC, TIME, TIME_SYNC, STATISTICS, diff --git a/include/Global.h b/include/Global.h index d018a37d..67f4c02e 100644 --- a/include/Global.h +++ b/include/Global.h @@ -1,8 +1,8 @@ #pragma once //===================Libraries=================================================================================================================================================== #include "Consts.h" -#include -#include + + #include #include "CTBot.h" #include @@ -91,9 +91,7 @@ extern int logging_EnterCounter; extern int dht_EnterCounter; //========================================= -// Sensors -extern String sensorReadingMap10sec; -extern String sensorReadingMap30sec; + extern String itemName; extern String presetName; diff --git a/include/items/SensorBme280Class.h b/include/items/SensorBme280Class.h deleted file mode 100644 index d3156418..00000000 --- a/include/items/SensorBme280Class.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -#include "Consts.h" -#ifdef SensorBme280Enabled -#include -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" - -Adafruit_BME280 bme; -Adafruit_Sensor *bme_temp = bme.getTemperatureSensor(); -Adafruit_Sensor *bme_pressure = bme.getPressureSensor(); -Adafruit_Sensor *bme_humidity = bme.getHumiditySensor(); - -class SensorBme280Class : public SensorConvertingClass { - public: - SensorBme280Class() : SensorConvertingClass(){}; - - void SensorBme280Init() { - bme.begin(hexStringToUint8(_addr)); - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - sensorReadingMap10sec += _key + ","; - } - - void SensorBme280ReadTmp(String key) { - float value; - value = bme.readTemperature(); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } - - void SensorBme280ReadHum(String key) { - float value; - value = bme.readHumidity(); - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } - - void SensorBme280ReadPress(String key) { - float value; - value = bme.readPressure(); - value = value / 1.333224 / 100; - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } -}; -extern SensorBme280Class mySensorBme280; -#endif \ No newline at end of file diff --git a/include/items/SensorBmp280Class.h b/include/items/SensorBmp280Class.h deleted file mode 100644 index d775cc87..00000000 --- a/include/items/SensorBmp280Class.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include "Consts.h" -#ifdef SensorBmp280Enabled -#include -#include "Class/LineParsing.h" -#include "Global.h" -#include "items/SensorConvertingClass.h" - -Adafruit_BMP280 bmp; -Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor(); -Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor(); - -class SensorBmp280Class : public SensorConvertingClass { - public: - SensorBmp280Class() : SensorConvertingClass(){}; - - void SensorBmp280Init() { - bmp.begin(hexStringToUint8(_addr)); - jsonWriteStr(configOptionJson, _key + "_map", _map); - jsonWriteStr(configOptionJson, _key + "_с", _c); - sensorReadingMap10sec += _key + ","; - } - - void SensorBmp280ReadTmp(String key) { - float value; - sensors_event_t temp_event; - bmp_temp->getEvent(&temp_event); - value = temp_event.temperature; - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } - - void SensorBmp280ReadPress(String key) { - float value; - sensors_event_t pressure_event; - bmp_pressure->getEvent(&pressure_event); - value = pressure_event.pressure; - value = value / 1.333224; - float valueFl = this->correction(key, value); - eventGen2(key, String(valueFl)); - jsonWriteStr(configLiveJson, key, String(valueFl)); - publishStatus(key, String(valueFl)); - SerialPrint("I", "Sensor", "'" + key + "' data: " + String(valueFl)); - } -}; -extern SensorBmp280Class mySensorBmp280; -#endif \ No newline at end of file diff --git a/include/items/SensorConvertingClass.h b/include/items/SensorConvertingClass.h deleted file mode 100644 index f7e1e5cd..00000000 --- a/include/items/SensorConvertingClass.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class SensorConvertingClass : public LineParsing { - public: - SensorConvertingClass() : LineParsing(){}; - - int mapping(String key, int input) { - String map_ = jsonReadStr(configOptionJson, key + "_map"); - if (map_ != "") { - input = map(input, - selectFromMarkerToMarker(map_, ",", 0).toInt(), - selectFromMarkerToMarker(map_, ",", 1).toInt(), - selectFromMarkerToMarker(map_, ",", 2).toInt(), - selectFromMarkerToMarker(map_, ",", 3).toInt()); - } - return input; - } - - float correction(String key, float input) { - String corr = jsonReadStr(configOptionJson, key + "_с"); - if (corr != "") { - float coef = corr.toFloat(); - input = input * coef; - } - return input; - } -}; diff --git a/include/items/vSensorBme280.h b/include/items/vSensorBme280.h new file mode 100644 index 00000000..76ac1e10 --- /dev/null +++ b/include/items/vSensorBme280.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +#include "Global.h" + +extern Adafruit_BME280* bme; + +class SensorBme280; + +typedef std::vector MySensorBme280Vector; + +struct paramsBme { + String key; + String addr; + unsigned long interval; + float c; +}; + +class SensorBme280 { + public: + SensorBme280(const paramsBme& paramsTmp, const paramsBme& paramsHum, const paramsBme& paramsPrs); + ~SensorBme280(); + + void loop(); + void read(); + + private: + paramsBme _paramsTmp; + paramsBme _paramsHum; + paramsBme _paramsPrs; + + unsigned long prevMillis; + unsigned long difference; +}; + +extern MySensorBme280Vector* mySensorBme280; + +extern void bme280Sensor(); \ No newline at end of file diff --git a/include/items/vSensorBmp280.h b/include/items/vSensorBmp280.h new file mode 100644 index 00000000..e681142c --- /dev/null +++ b/include/items/vSensorBmp280.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include + +#include "Global.h" + +extern Adafruit_BMP280* bmp; + +class SensorBmp280; + +typedef std::vector MySensorBmp280Vector; + +struct paramsBmp { + String key; + String addr; + unsigned long interval; + float c; +}; + +class SensorBmp280 { + public: + SensorBmp280(const paramsBmp& paramsTmp, const paramsBmp& paramsPrs); + ~SensorBmp280(); + + void loop(); + void read(); + + private: + paramsBmp _paramsTmp; + paramsBmp _paramsPrs; + + unsigned long prevMillis; + unsigned long difference; +}; + +extern MySensorBmp280Vector* mySensorBmp280; + +extern void bmp280Sensor(); \ No newline at end of file diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h index dc86d285..608db0a8 100644 --- a/include/items/vSensorDht.h +++ b/include/items/vSensorDht.h @@ -11,7 +11,7 @@ class SensorDht; typedef std::vector MySensorDhtVector; -struct params { +struct paramsDht { String type; String key; unsigned long interval; @@ -21,18 +21,16 @@ struct params { class SensorDht { public: - SensorDht(const params& paramsTmp, const params& paramsHum); + SensorDht(const paramsDht& paramsTmp, const paramsDht& paramsHum); ~SensorDht(); void loop(); void readTmpHum(); private: - params _paramsTmp; - params _paramsHum; + paramsDht _paramsTmp; + paramsDht _paramsHum; - unsigned int _interval; - unsigned long prevMillis; unsigned long difference; }; diff --git a/include/items/vSensorUltrasonic.h b/include/items/vSensorUltrasonic.h index 368af649..2195d7c5 100644 --- a/include/items/vSensorUltrasonic.h +++ b/include/items/vSensorUltrasonic.h @@ -1,14 +1,13 @@ #pragma once #include "Global.h" #include -#include "items/SensorConvertingClass.h" #include "GyverFilters.h" class SensorUltrasonic; typedef std::vector MySensorUltrasonicVector; -class SensorUltrasonic : public SensorConvertingClass { +class SensorUltrasonic { public: SensorUltrasonic(String key, unsigned long interval, unsigned int trig, unsigned int echo, int map1, int map2, int map3, int map4, float c); diff --git a/src/BufferExecute.cpp b/src/BufferExecute.cpp index 5ac53b74..2a28b95f 100644 --- a/src/BufferExecute.cpp +++ b/src/BufferExecute.cpp @@ -12,6 +12,8 @@ #include "items/vCountDown.h" #include "items/vSensorAnalog.h" #include "items/vSensorDht.h" +#include "items/vSensorBme280.h" +#include "items/vSensorBmp280.h" void loopCmdAdd(const String& cmdStr) { if (cmdStr.endsWith(",")) { @@ -79,22 +81,13 @@ void csvCmdExecute(String& cmdStr) { } #endif #ifdef SensorBme280Enabled - else if (order == F("bme280-temp")) { - sCmd.addCommand(order.c_str(), bme280Temp); - } - else if (order == F("bme280-hum")) { - sCmd.addCommand(order.c_str(), bme280Hum); - } - else if (order == F("bme280-press")) { - sCmd.addCommand(order.c_str(), bme280Press); + else if (order == F("bme280")) { + sCmd.addCommand(order.c_str(), bme280Sensor); } #endif #ifdef SensorBmp280Enabled - else if (order == F("bmp280-temp")) { - sCmd.addCommand(order.c_str(), bmp280Temp); - } - else if (order == F("bmp280-press")) { - sCmd.addCommand(order.c_str(), bmp280Press); + else if (order == F("bmp280")) { + sCmd.addCommand(order.c_str(), bmp280Sensor); } #endif else if (order == F("uptime")) { @@ -136,31 +129,6 @@ void loopCmdExecute() { } } -void sensorsInit() { - ts.add( - SENSORS10SEC, 10000, [&](void*) { - String buf = sensorReadingMap10sec; - while (buf.length()) { - String tmp = selectToMarker(buf, ","); - sCmd.readStr(tmp); - buf = deleteBeforeDelimiter(buf, ","); - } - }, - nullptr, true); - - ts.add( - SENSORS30SEC, 30000, [&](void*) { - String buf = sensorReadingMap30sec; - while (buf.length()) { - String tmp = selectToMarker(buf, ","); - sCmd.readStr(tmp); - buf = deleteBeforeDelimiter(buf, ","); - } - }, - nullptr, true); - SerialPrint("I", F("Sensors"), F("Sensors Init")); -} - void addKey(String& key, String& keyNumberTable, int number) { keyNumberTable += key + " " + String(number) + ","; } diff --git a/src/Global.cpp b/src/Global.cpp index 39853d25..6274d264 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -62,9 +62,6 @@ int logging_EnterCounter = -1; int dht_EnterCounter = -1; //========================================= -// Sensors -String sensorReadingMap10sec; -String sensorReadingMap30sec; String itemName; String presetName; diff --git a/src/Init.cpp b/src/Init.cpp index 195464d1..32fb4737 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -43,7 +43,6 @@ void espInit() { } void deviceInit() { - sensorReadingMap10sec = ""; //======clear dallas params====== if (mySensorDallas2 != nullptr) { diff --git a/src/items/SensorBme280Class.cpp b/src/items/SensorBme280Class.cpp deleted file mode 100644 index 086c44f4..00000000 --- a/src/items/SensorBme280Class.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "Consts.h" -#ifdef SensorBme280Enabled -#include "items/SensorBme280Class.h" -#include "BufferExecute.h" -//=========================================Модуль ультрозвукового дальномера================================================================== -//bme280-temp;id;anydata;Сенсоры;Температура;order;c[1] -//bme280-hum;id;anydata;Сенсоры;Температура;order;c[1] -//bme280-press;id;anydata;Сенсоры;Температура;order;c[1] -//========================================================================================================================================= -SensorBme280Class mySensorBme280; - -void bme280Temp() { - mySensorBme280.update(); - String key = mySensorBme280.gkey(); - sCmd.addCommand(key.c_str(), bme280ReadingTemp); - mySensorBme280.SensorBme280Init(); - mySensorBme280.clear(); -} -void bme280ReadingTemp() { - String key = sCmd.order(); - mySensorBme280.SensorBme280ReadTmp(key); -} - -void bme280Hum() { - mySensorBme280.update(); - String key = mySensorBme280.gkey(); - sCmd.addCommand(key.c_str(), bme280ReadingHum); - mySensorBme280.SensorBme280Init(); - mySensorBme280.clear(); -} -void bme280ReadingHum() { - String key = sCmd.order(); - mySensorBme280.SensorBme280ReadHum(key); -} - -void bme280Press() { - mySensorBme280.update(); - String key = mySensorBme280.gkey(); - sCmd.addCommand(key.c_str(), bme280ReadingPress); - mySensorBme280.SensorBme280Init(); - mySensorBme280.clear(); -} -void bme280ReadingPress() { - String key = sCmd.order(); - mySensorBme280.SensorBme280ReadPress(key); -} -#endif \ No newline at end of file diff --git a/src/items/SensorBmp280Class.cpp b/src/items/SensorBmp280Class.cpp deleted file mode 100644 index f8e5b06f..00000000 --- a/src/items/SensorBmp280Class.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "Consts.h" -#ifdef SensorBmp280Enabled -#include "items/SensorBmp280Class.h" -#include "BufferExecute.h" -//=========================================Модуль ультрозвукового дальномера================================================================== -//bmp280-temp;id;anydata;Сенсоры;Температура;order;c[1] -//bmp280-hum;id;anydata;Сенсоры;Температура;order;c[1] -//bmp280-press;id;anydata;Сенсоры;Температура;order;c[1] -//========================================================================================================================================= -SensorBmp280Class mySensorBmp280; - -void bmp280Temp() { - mySensorBmp280.update(); - String key = mySensorBmp280.gkey(); - sCmd.addCommand(key.c_str(), bmp280ReadingTemp); - mySensorBmp280.SensorBmp280Init(); - mySensorBmp280.clear(); -} -void bmp280ReadingTemp() { - String key = sCmd.order(); - mySensorBmp280.SensorBmp280ReadTmp(key); -} - -void bmp280Press() { - mySensorBmp280.update(); - String key = mySensorBmp280.gkey(); - sCmd.addCommand(key.c_str(), bmp280ReadingPress); - mySensorBmp280.SensorBmp280Init(); - mySensorBmp280.clear(); -} -void bmp280ReadingPress() { - String key = sCmd.order(); - mySensorBmp280.SensorBmp280ReadPress(key); -} -#endif \ No newline at end of file diff --git a/src/items/sysUptime.cpp b/src/items/sysUptime.cpp index 910bb871..58a9e12c 100644 --- a/src/items/sysUptime.cpp +++ b/src/items/sysUptime.cpp @@ -7,7 +7,7 @@ void sysUptime() { myLineParsing.update(); String key = myLineParsing.gkey(); sCmd.addCommand(key.c_str(), uptimeReading); - sensorReadingMap30sec += key + ","; + //ensorReadingMap30sec += key + ","; myLineParsing.clear(); } diff --git a/src/items/vSensorBme280.cpp b/src/items/vSensorBme280.cpp new file mode 100644 index 00000000..6ec0b64b --- /dev/null +++ b/src/items/vSensorBme280.cpp @@ -0,0 +1,100 @@ +#include "items/vSensorBme280.h" + +#include + +#include "BufferExecute.h" +#include "Class/LineParsing.h" +#include "Global.h" + +Adafruit_BME280* bme = nullptr; + +SensorBme280::SensorBme280(const paramsBme& paramsTmp, const paramsBme& paramsHum, const paramsBme& paramsPrs) { + _paramsTmp = paramsBme(paramsTmp); + _paramsHum = paramsBme(paramsHum); + _paramsPrs = paramsBme(paramsPrs); + + if (!bme) { + bme = new Adafruit_BME280; + } + + bme->getTemperatureSensor(); + bme->getPressureSensor(); + bme->getHumiditySensor(); + bme->begin(hexStringToUint8(_paramsPrs.addr)); +} + +SensorBme280::~SensorBme280() {} + +void SensorBme280::loop() { + difference = millis() - prevMillis; + if (difference >= _paramsPrs.interval) { + prevMillis = millis(); + read(); + } +} + +void SensorBme280::read() { + float tmp = bme->readTemperature(); + float hum = bme->readHumidity(); + float prs = bme->readPressure(); + prs = prs / 1.333224 / 100; + + tmp = tmp * _paramsTmp.c; + hum = hum * _paramsHum.c; + prs = prs * _paramsPrs.c; + + eventGen2(_paramsTmp.key, String(tmp)); + jsonWriteStr(configLiveJson, _paramsTmp.key, String(tmp)); + publishStatus(_paramsTmp.key, String(tmp)); + SerialPrint("I", "Sensor", "'" + _paramsTmp.key + "' data: " + String(tmp)); + + eventGen2(_paramsHum.key, String(hum)); + jsonWriteStr(configLiveJson, _paramsHum.key, String(hum)); + publishStatus(_paramsHum.key, String(hum)); + SerialPrint("I", "Sensor", "'" + _paramsHum.key + "' data: " + String(hum)); + + eventGen2(_paramsPrs.key, String(prs)); + jsonWriteStr(configLiveJson, _paramsPrs.key, String(prs)); + publishStatus(_paramsPrs.key, String(prs)); + SerialPrint("I", "Sensor", "'" + _paramsPrs.key + "' data: " + String(prs)); +} + +MySensorBme280Vector* mySensorBme280 = nullptr; + +void bme280Sensor() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + String addr = myLineParsing.gaddr(); + String interval = myLineParsing.gint(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + static int enterCnt = -1; + enterCnt++; + + static paramsBme paramsTmp; + static paramsBme paramsHum; + static paramsBme paramsPrs; + + if (enterCnt == 0) { + paramsTmp.key = key; + paramsTmp.c = c.toFloat(); + } + + if (enterCnt == 1) { + paramsHum.key = key; + paramsHum.c = c.toFloat(); + } + + if (enterCnt == 2) { + paramsPrs.key = key; + paramsPrs.addr = addr; + paramsPrs.interval = interval.toInt() * 1000; + paramsPrs.c = c.toFloat(); + + static bool firstTime = true; + if (firstTime) mySensorBme280 = new MySensorBme280Vector(); + firstTime = false; + mySensorBme280->push_back(SensorBme280(paramsTmp, paramsHum, paramsPrs)); + } +} diff --git a/src/items/vSensorBmp280.cpp b/src/items/vSensorBmp280.cpp new file mode 100644 index 00000000..df238317 --- /dev/null +++ b/src/items/vSensorBmp280.cpp @@ -0,0 +1,86 @@ +#include "items/vSensorBmp280.h" + +#include + +#include "BufferExecute.h" +#include "Class/LineParsing.h" +#include "Global.h" + +Adafruit_BMP280* bmp = nullptr; + +SensorBmp280::SensorBmp280(const paramsBmp& paramsTmp, const paramsBmp& paramsPrs) { + _paramsTmp = paramsBmp(paramsTmp); + _paramsPrs = paramsBmp(paramsPrs); + + if (!bmp) { + bmp = new Adafruit_BMP280; + } + + bmp->getTemperatureSensor(); + bmp->getPressureSensor(); + bmp->begin(hexStringToUint8(_paramsPrs.addr)); +} + +SensorBmp280::~SensorBmp280() {} + +void SensorBmp280::loop() { + difference = millis() - prevMillis; + if (difference >= _paramsPrs.interval) { + prevMillis = millis(); + read(); + } +} + +void SensorBmp280::read() { + float tmp = bmp->readTemperature(); + float prs = bmp->readPressure(); + prs = prs / 1.333224 / 100; + + tmp = tmp * _paramsTmp.c; + prs = prs * _paramsPrs.c; + + eventGen2(_paramsTmp.key, String(tmp)); + jsonWriteStr(configLiveJson, _paramsTmp.key, String(tmp)); + publishStatus(_paramsTmp.key, String(tmp)); + SerialPrint("I", "Sensor", "'" + _paramsTmp.key + "' data: " + String(tmp)); + + eventGen2(_paramsPrs.key, String(prs)); + jsonWriteStr(configLiveJson, _paramsPrs.key, String(prs)); + publishStatus(_paramsPrs.key, String(prs)); + SerialPrint("I", "Sensor", "'" + _paramsPrs.key + "' data: " + String(prs)); +} + +MySensorBmp280Vector* mySensorBmp280 = nullptr; + +void bmp280Sensor() { + myLineParsing.update(); + String key = myLineParsing.gkey(); + String addr = myLineParsing.gaddr(); + String interval = myLineParsing.gint(); + String c = myLineParsing.gc(); + myLineParsing.clear(); + + static int enterCnt = -1; + enterCnt++; + + static paramsBmp paramsTmp; + static paramsBmp paramsHum; + static paramsBmp paramsPrs; + + if (enterCnt == 0) { + paramsTmp.key = key; + paramsTmp.c = c.toFloat(); + } + + if (enterCnt == 1) { + paramsPrs.key = key; + paramsPrs.addr = addr; + paramsPrs.interval = interval.toInt() * 1000; + paramsPrs.c = c.toFloat(); + + static bool firstTime = true; + if (firstTime) mySensorBmp280 = new MySensorBmp280Vector(); + firstTime = false; + mySensorBmp280->push_back(SensorBmp280(paramsTmp, paramsPrs)); + } +} diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 6ac8b83c..a733c234 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -8,9 +8,9 @@ DHTesp* dht = nullptr; -SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { - _paramsTmp = params(paramsTmp); - _paramsHum = params(paramsHum); +SensorDht::SensorDht(const paramsDht& paramsTmp, const paramsDht& paramsHum) { + _paramsTmp = paramsDht(paramsTmp); + _paramsHum = paramsDht(paramsHum); if (!dht) { dht = new DHTesp(); @@ -23,26 +23,23 @@ SensorDht::SensorDht(const params& paramsTmp, const params& paramsHum) { dht->setup(_paramsTmp.pin, DHTesp::DHT22); } } - _interval = _paramsTmp.interval < _paramsHum.interval ? _paramsTmp.interval : _paramsHum.interval; - _interval = _interval + dht->getMinimumSamplingPeriod(); + + _paramsHum.interval = _paramsHum.interval + dht->getMinimumSamplingPeriod(); } SensorDht::~SensorDht() {} void SensorDht::loop() { difference = millis() - prevMillis; - if (difference >= _interval) { + if (difference >= _paramsHum.interval) { prevMillis = millis(); readTmpHum(); } } void SensorDht::readTmpHum() { - float tmp; - float hum; - - tmp = dht->getTemperature(); - hum = dht->getHumidity(); + float tmp = dht->getTemperature(); + float hum = dht->getHumidity(); if (String(tmp) != "nan" && String(hum) != "nan") { tmp = tmp * _paramsTmp.c; @@ -77,8 +74,8 @@ void dhtSensor() { static int enterCnt = -1; enterCnt++; - static params paramsTmp; - static params paramsHum; + static paramsDht paramsTmp; + static paramsDht paramsHum; if (enterCnt == 0) { paramsTmp.type = type; diff --git a/src/main.cpp b/src/main.cpp index 4ae6557e..ddb16b5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,8 @@ #include "items/vSensorDallas.h" #include "items/vSensorDht.h" #include "items/vSensorUltrasonic.h" +#include "items/vSensorBme280.h" +#include "items/vSensorBmp280.h" void not_async_actions(); @@ -50,7 +52,6 @@ void setup() { #endif clockInit(); timeInit(); - sensorsInit(); //Will be remooved itemsListInit(); espInit(); routerConnect(); @@ -140,4 +141,14 @@ void loop() { mySensorDht->at(i).loop(); } } + if (mySensorBme280 != nullptr) { + for (unsigned int i = 0; i < mySensorBme280->size(); i++) { + mySensorBme280->at(i).loop(); + } + } + if (mySensorBmp280 != nullptr) { + for (unsigned int i = 0; i < mySensorBmp280->size(); i++) { + mySensorBmp280->at(i).loop(); + } + } } \ No newline at end of file From d20f30d5e989263bee35e7971c86e4bea351b0a2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 26 Dec 2020 16:28:58 +0100 Subject: [PATCH 78/94] fix dht --- data/items/items.txt | 8 ++++---- data/presets/presets.c.txt | 11 ++++++----- src/items/vSensorDht.cpp | 14 +++++--------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/data/items/items.txt b/data/items/items.txt index fc7f1bd5..3b9d7ca4 100644 --- a/data/items/items.txt +++ b/data/items/items.txt @@ -9,10 +9,10 @@ 0;analog-adc;adcid;fillgauge;Сенсоры;Аналоговый;order;pin[0];map[0,1024,0,100];c[1];int[10]* 0;dallas-temp;tmpid;anydataTemp;Сенсоры;Температура;order;pin[2];index[0];int[10]* 0;ultrasonic-cm;cmid;anydata;Сенсоры;Расстояние;order;pin[12,13];map[0,500,0,100];c[1];int[10]* -0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht11];c[1] -0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht11];c[1];int[10]* -0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;pin[2];type[dht22];c[1] -0;dht;humid;anydataHum;Сенсоры;Влажность;2;pin[2];type[dht22];c[1];int[10]* +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;c[1] +0;dht;humid;anydataHum;Сенсоры;Влажность;2;c[1];pin[2];type[dht11];int[10]* +0;dht;tmpid;anydataTemp;Сенсоры;Температура;1;c[1] +0;dht;humid;anydataHum;Сенсоры;Влажность;2;c[1];pin[2];type[dht22];int[10]* 0;bme280;tmpid;anydataTemp;Сенсоры;Температура;1;c[1] 0;bme280;humid;anydataHum;Сенсоры;Влажность;2;c[1] 0;bme280;prsid;anydataPress;Сенсоры;Давление;3;c[1];addr[0x76];int[10]* diff --git a/data/presets/presets.c.txt b/data/presets/presets.c.txt index cd7fde4a..44091627 100644 --- a/data/presets/presets.c.txt +++ b/data/presets/presets.c.txt @@ -15,11 +15,12 @@ 0;inoutput;threshold3;inputDigitTemp;Расписание2;Температура;13 0;inoutput;time24;inputTimeClock;Расписание2;Ночной#период;14 0;inoutput;threshold4;inputDigitTemp;Расписание2;Температура;15* -0;dht-hum;h3;anydataHum;Теплица3;Влажность;1;pin[2];type[dht11];c[1] -0;logging;log3;chart;Теплица3;История;2;val[hum];int[60];cnt[100] -0;inoutput;hUp3;inputDigit;Теплица3;Верхний#порог;3 -0;inoutput;hLow3;inputDigit;Теплица3;Нижний#порог;4 -0;button-out;hUp3;toggle;Теплица3;Полив;5;pin[12]* +0;dht;t3;anydataTemp;Теплица3;Температура;1;c[1] +0;dht;h3;anydataHum;Теплица3;Влажность;2;c[1];pin[2];type[dht11];int[10] +0;logging;log3;chart;Теплица3;История;3;val[hum];int[60];cnt[100] +0;inoutput;hUp3;inputDigit;Теплица3;Верхний#порог;4 +0;inoutput;hLow3;inputDigit;Теплица3;Нижний#порог;5 +0;button-out;hUp3;toggle;Теплица3;Полив;6;pin[12]* 0;button-out;btn41;toggle;Реле4;Освещение;1;pin[12] 0;button-out;btn42;toggle;Реле4;Освещение;2;pin[13] 0;inoutput;time41;inputTime;Реле4;Введите#время#включения;3 diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index a733c234..04dd4e80 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -16,14 +16,12 @@ SensorDht::SensorDht(const paramsDht& paramsTmp, const paramsDht& paramsHum) { dht = new DHTesp(); } - if (_paramsTmp.type == _paramsHum.type) { - if (_paramsTmp.type == "dht11") { - dht->setup(_paramsTmp.pin, DHTesp::DHT11); - } else if (_paramsTmp.type == "dht22") { - dht->setup(_paramsTmp.pin, DHTesp::DHT22); - } + if (_paramsHum.type == "dht11") { + dht->setup(_paramsHum.pin, DHTesp::DHT11); + } else if (_paramsHum.type == "dht22") { + dht->setup(_paramsHum.pin, DHTesp::DHT22); } - + _paramsHum.interval = _paramsHum.interval + dht->getMinimumSamplingPeriod(); } @@ -78,10 +76,8 @@ void dhtSensor() { static paramsDht paramsHum; if (enterCnt == 0) { - paramsTmp.type = type; paramsTmp.key = key; paramsTmp.interval = interval.toInt() * 1000; - paramsTmp.pin = pin.toInt(); paramsTmp.c = c.toFloat(); } From 02c363f1d6a636d0e11cb17f006e45ca8c34a886 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 26 Dec 2020 16:31:10 +0100 Subject: [PATCH 79/94] dht preset --- data/presets/presets.c.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/presets/presets.c.txt b/data/presets/presets.c.txt index 44091627..f26eb705 100644 --- a/data/presets/presets.c.txt +++ b/data/presets/presets.c.txt @@ -17,7 +17,7 @@ 0;inoutput;threshold4;inputDigitTemp;Расписание2;Температура;15* 0;dht;t3;anydataTemp;Теплица3;Температура;1;c[1] 0;dht;h3;anydataHum;Теплица3;Влажность;2;c[1];pin[2];type[dht11];int[10] -0;logging;log3;chart;Теплица3;История;3;val[hum];int[60];cnt[100] +0;logging;log3;chart;Теплица3;История;3;val[h3];int[60];cnt[100] 0;inoutput;hUp3;inputDigit;Теплица3;Верхний#порог;4 0;inoutput;hLow3;inputDigit;Теплица3;Нижний#порог;5 0;button-out;hUp3;toggle;Теплица3;Полив;6;pin[12]* From 454922b8b48c41aec8f72b9c8e8981be06708116 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 26 Dec 2020 23:10:07 +0100 Subject: [PATCH 80/94] get mqtt remote --- include/Class/ScenarioClass3.h | 28 ++++++----------- src/Class/ScenarioClass3.cpp | 5 ++- src/Init.cpp | 2 -- src/MqttClient.cpp | 23 +++++++++----- src/Web.cpp | 37 ++++++++++++---------- src/items/vLogging.cpp | 56 +++++++++++++++++++--------------- 6 files changed, 81 insertions(+), 70 deletions(-) diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index cc8b0b5b..13937370 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -1,12 +1,11 @@ #pragma once #include + #include "Cmd.h" #include "Global.h" class Scenario { - -public: - + public: void loop() { if (!jsonReadBool(configSetupJson, "scen")) { return; @@ -27,7 +26,6 @@ public: String setEventKey = selectFromMarkerToMarker(condition, " ", 0); if (incommingEventKey == setEventKey) { - String setEventSign = selectFromMarkerToMarker(condition, " ", 1); String setEventValue = selectFromMarkerToMarker(condition, " ", 2); @@ -42,13 +40,10 @@ public: if (setEventSign == ">") { setEventValue = upValue; - } - else if (setEventSign == "<") { + } else if (setEventSign == "<") { setEventValue = lowValue; - } - } - else { + } else { setEventValue = getValue(setEventValue); } } @@ -57,20 +52,15 @@ public: if (setEventSign == "=") { flag = incommingEventValue == setEventValue; - } - else if (setEventSign == "!=") { + } else if (setEventSign == "!=") { flag = incommingEventValue != setEventValue; - } - else if (setEventSign == "<") { + } else if (setEventSign == "<") { flag = incommingEventValue.toFloat() < setEventValue.toFloat(); - } - else if (setEventSign == ">") { + } else if (setEventSign == ">") { flag = incommingEventValue.toFloat() > setEventValue.toFloat(); - } - else if (setEventSign == ">=") { + } else if (setEventSign == ">=") { flag = incommingEventValue.toFloat() >= setEventValue.toFloat(); - } - else if (setEventSign == "<=") { + } else if (setEventSign == "<=") { flag = incommingEventValue.toFloat() <= setEventValue.toFloat(); } diff --git a/src/Class/ScenarioClass3.cpp b/src/Class/ScenarioClass3.cpp index b50f3f83..09350105 100644 --- a/src/Class/ScenarioClass3.cpp +++ b/src/Class/ScenarioClass3.cpp @@ -1,4 +1,5 @@ #include "Class/ScenarioClass3.h" + #include "MqttClient.h" #include "RemoteOrdersUdp.h" Scenario* myScenario; @@ -11,7 +12,9 @@ void eventGen2(String eventName, String eventValue) { eventBuf += event; if (jsonReadBool(configSetupJson, "MqttOut")) { - publishEvent(eventName, eventValue); + if (eventName != "timenow") { + publishEvent(eventName, eventValue); + } } } diff --git a/src/Init.cpp b/src/Init.cpp index 32fb4737..074211c2 100644 --- a/src/Init.cpp +++ b/src/Init.cpp @@ -43,7 +43,6 @@ void espInit() { } void deviceInit() { - //======clear dallas params====== if (mySensorDallas2 != nullptr) { mySensorDallas2->clear(); @@ -111,7 +110,6 @@ void deviceInit() { } else { jsonWriteStr(configSetupJson, F("warning3"), ""); } - //outcoming_date(); } //-------------------------------сценарии----------------------------------------------------- diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 610957f1..f5147292 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -1,14 +1,15 @@ #include "MqttClient.h" + #include "BufferExecute.h" -#include "items/vLogging.h" #include "Class/NotAsync.h" #include "Global.h" #include "Init.h" +#include "items/vLogging.h" enum MqttBroker { MQTT_PRIMARY, MQTT_RESERVE }; -MqttBroker activeBroker = MQTT_PRIMARY; +MqttBroker activeBroker = MQTT_PRIMARY; String mqttPrefix; String mqttRootDevice; @@ -110,6 +111,7 @@ void mqttSubscribe() { if (jsonReadBool(configSetupJson, "MqttIn")) { mqtt.subscribe((mqttPrefix + "/+/+/event").c_str()); + mqtt.subscribe((mqttPrefix + "/+/+/order").c_str()); mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); } } @@ -201,6 +203,17 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { } } + else if (topicStr.indexOf("order") != -1) { + if (!jsonReadBool(configSetupJson, "MqttIn")) { + return; + } + String devId = selectFromMarkerToMarker(topicStr, "/", 2); + String key = selectFromMarkerToMarker(topicStr, "/", 3); + SerialPrint("I", "=>MQTT", "Received direct order " + key + " " + payloadStr); + String order = key + " " + payloadStr + ","; + orderBuf += order; + } + else if (topicStr.indexOf("info") != -1) { if (topicStr.indexOf("scen") != -1) { writeFile(String(DEVICE_SCENARIO_FILE), payloadStr); @@ -208,12 +221,6 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { SerialPrint("I", "=>MQTT", "Scenario received"); } } - - //else if (topicStr.indexOf("update")) { - // if (payloadStr == "1") { - // myNotAsyncActions->make(do_UPGRADE); - // } - //} } boolean publish(const String& topic, const String& data) { diff --git a/src/Web.cpp b/src/Web.cpp index b01713ce..ef30e82a 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -1,12 +1,13 @@ #include "Web.h" + #include "Class/NotAsync.h" #include "Global.h" #include "Init.h" #include "ItemsList.h" -#include "items/vLogging.h" -#include "Telegram.h" #include "RemoteOrdersUdp.h" #include "SoftUART.h" +#include "Telegram.h" +#include "items/vLogging.h" bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { if (request->hasArg("preset")) { @@ -84,7 +85,6 @@ void web_init() { request->send(200); } - if (request->hasArg(F("cleanlog"))) { cleanLogAndData(); request->send(200); @@ -325,8 +325,18 @@ void web_init() { serverIP = jsonReadStr(configSetupJson, "serverip"); request->send(200); } - }); + //set?order=button_1 + if (request->hasArg("order")) { + String order = request->getParam("order")->value(); + order.replace("_"," "); + orderBuf += order + ","; + request->send(200); + } + }); + server.on("/order", HTTP_GET, [](AsyncWebServerRequest* request) { + + }); server.on("/check", HTTP_GET, [](AsyncWebServerRequest* request) { myNotAsyncActions->make(do_GETLASTVERSION); @@ -336,21 +346,16 @@ void web_init() { if (ESP8266_FLASH_SIZE_1MB) { msg = F("Обновление невозможно, память устройства 1 мб"); - } - else { + } else { if (lastVersion == FIRMWARE_VERSION) { msg = F("Актуальная версия прошивки уже установлена."); - } - else if (lastVersion > FIRMWARE_VERSION) { + } else if (lastVersion > FIRMWARE_VERSION) { msg = F("Новая версия прошивкиИдет обновление прошивки, после обновления страница перезагрузится автоматически...')\">Установить"); - } - else if (lastVersion == -1) { + } else if (lastVersion == -1) { msg = F("Cервер не найден. Попробуйте повторить позже..."); - } - else if (lastVersion == -2) { + } else if (lastVersion == -2) { msg = F("Устройство не подключено к роутеру!"); - } - else if (lastVersion < FIRMWARE_VERSION) { + } else if (lastVersion < FIRMWARE_VERSION) { msg = F("Ошибка версии. Попробуйте повторить позже..."); } } @@ -359,7 +364,7 @@ void web_init() { jsonWriteStr(tmp, "title", "" + msg); jsonWriteStr(tmp, "class", "pop-up"); request->send(200, "text/html", tmp); - }); + }); /* * Upgrade @@ -367,7 +372,7 @@ void web_init() { server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest* request) { myNotAsyncActions->make(do_UPGRADE); request->send(200, "text/html"); - }); + }); SerialPrint("I", F("Web"), F("WebAdmin Init")); } diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 66f71094..3593afa4 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -2,11 +2,10 @@ #include -#include "FileSystem.h" - -#include "Class/LineParsing.h" -#include "Global.h" #include "BufferExecute.h" +#include "Class/LineParsing.h" +#include "FileSystem.h" +#include "Global.h" LoggingClass::LoggingClass(unsigned long period, unsigned int maxPoints, String loggingValueKey, String key) { _period = period * 1000; @@ -28,10 +27,25 @@ void LoggingClass::loop() { } } -void LoggingClass::execute(String payload) { +void LoggingClass::execute(String keyOrValue) { + String loggingValue = ""; - if (_period > 0) { - payload = getValue(_loggingValueKey); + if (keyOrValue == "") { //прилетело из лупа + if (getValue(_loggingValueKey) != "no value") { + loggingValue = getValue(_loggingValueKey); + } else { + SerialPrint("E", "Logging", "This value not found on this device"); + } + } else { //прилетело из события + if (isDigitStr(keyOrValue)) { //если это число + loggingValue = keyOrValue; + } else { //если это ключ + if (getValue(_loggingValueKey) != "no value") { + loggingValue = getValue(keyOrValue); + } else { + SerialPrint("E", "Logging", "This value not found on this device"); + } + } } String filename = "logs/" + _key + ".txt"; @@ -46,20 +60,24 @@ void LoggingClass::execute(String payload) { lines_cnt = 0; } - if (payload != "") { - if (lines_cnt > _maxPoints) { + if (loggingValue != "") { + if (lines_cnt > _maxPoints) { //удаляем старую строку и добавляем новую logData = deleteBeforeDelimiter(logData, "\r\n"); if (timeNow->hasTimeSynced()) { - logData += timeNow->getTimeUnix() + " " + payload + "\r\n"; + logData += timeNow->getTimeUnix() + " " + loggingValue + "\r\n"; writeFile(filename, logData); } - } - else { + } else { //просто добавляем новую строку if (timeNow->hasTimeSynced()) { - addFileLn(filename, timeNow->getTimeUnix() + " " + payload); + addFileLn(filename, timeNow->getTimeUnix() + " " + loggingValue); } } } + String buf = "{}"; + jsonWriteInt(buf, "x", timeNow->getTimeUnix().toInt()); + jsonWriteFloat(buf, "y1", loggingValue.toFloat()); + buf = "{\"status\":[" + buf + "]}"; + publishChart(_key, buf); } MyLoggingVector* myLogging = nullptr; @@ -88,13 +106,7 @@ void logging() { void loggingExecute() { String key = sCmd.order(); String value = sCmd.next(); - - if (!isDigitStr(value)) { //если значение - текст - value = getValue(value); - } - int number = getKeyNum(key, logging_KeyList); - if (myLogging != nullptr) { if (number != -1) { myLogging->at(number).execute(value); @@ -102,8 +114,6 @@ void loggingExecute() { } } - - void choose_log_date_and_send() { String all_line = logging_KeyList; while (all_line.length() != 0) { @@ -131,8 +141,7 @@ void sendLogData(String file, String topic) { jsonWriteFloat(buf, "y1", value.toFloat()); if (log_date.length() < 3) { json_array += buf; - } - else { + } else { json_array += buf + ","; } buf = "{}"; @@ -148,7 +157,6 @@ void sendLogData(String file, String topic) { } void cleanLogAndData() { - #ifdef ESP8266 auto dir = FileFS.openDir("logs"); while (dir.next()) { From a30dc040559542dd35829edae0cf652b686e16be Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 26 Dec 2020 23:24:37 +0100 Subject: [PATCH 81/94] 274 --- data/set.device.json.gz | Bin 2681 -> 2682 bytes data_ungzip/set.device.json | 2 +- include/Consts.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/set.device.json.gz b/data/set.device.json.gz index e769d3e93a9f7ac1c0894c6a61f5ed060d48393a..7ed66e68cd717990c4f62f7c2c7b26fcde352300 100644 GIT binary patch delta 2589 zcmV+&3gY$o6#5hgABzYG!MEp;2O}t_T=+Wg3lDtGZlP&Y$nT!v`a4ip8RNr65-{Hs;@TJ`${1 zQ}vU>bZ@D!-Lk6hlac`ye`4z=p_Z&s9~d7zvz^-{r{+vK7o7|I8~=hWKF#YHsanmI zRCDLH^XC^*+dhfcc>{FKI8&%YH5XJqzmKB%taAs2GqtSf#+e1xaL7cdD7`|h1r^Tk z11}exDc*GMNc=l4iF1uNVL@8Fi8>3aRX!tI%JI{RWrM{no_6OFe=O7&{uPX38Vy6j zezXpv_eMratr=fH-PNkC7Ij%$*aSE28&Ih*wlz(rIpTJRue;|Qbe_0i*E{*Eao$5eH z6NFp@wRKQ<6NXUTO8A_i*obk_xx+uh@SHoyTP_@@7Lj-ZaRMNs`y0vljNuF)5D|77 zmcqHhXXx<@*ymgh0e2kXj(4$h36~Z`T*cG|U4SPdIaeuQ&LI=H?B6gT!QVUB)g})f zUr7)f@Z48mfA%UsoPnuNxoLn`7FxF-fW&y=T#E&aB3rcE8 zkTc~mIg46@ip+3|R+A!# zEHBAq8YK5-SMUNQ7*5D9KTDJv8X`TQBYB~8e-^#gsfntvD{x3F=chS8bYJ6*5CJy1 zhEPI;dPUXqEMk#B%?CjQNE6KK<`a&8{1iI_`hzKD{fB3mpX6=4sLthrl{DF}Et{MCI_s^rZOXx9;1h-^=A zf41<`bTe4p3t`Q9;TE$n|495?LhJE^?-#Vz8Pra8H5VqRrYN+f5)jyZS}-r8wY1k7 z_@Vv}v<^4=?tsTSRh?g?qBZq57Of(+!TQfEY{|OMQlQl?T3?|`tTeoBnnbk_UZZW| z>U-%20H7az7roAP&olWf+7E&zZ~aKie>ximgjeD|5ZhWWYTy;f8x0xURigxVl_Je7 zS(itdXZGP>wM${pGAC>jV_ToV-3tjb$ifrlRlE1S;?xsKPTbQ;G=8Pgc@dZ(&dhVb zFq*x3^}eshcjPG$w^Hl_M4at#>8`>aTGOk-sPyXB``rp#UajxcJmS4lP|UZ_e;kM6 zyXa*I><-Eip|H2BW9Kr8^3z^peudeD?c0}2d3?BUTAaJ6o*FBI0I-9 zYi^jW)0tKsS;CO(+6htt0$bP>29{SqzwS-gZN-@G;eyf^`SW$C6K;1{E|EUt?iJmg zC6?Fz+QEax7l~g;jf~d+X+SZa`*l?ale~hhIH3{7Z z;dI-mu$6_1PSRGcssc|kHe)4ADQs8~-vqbC^2%v7r>u}BVd@erO=8vL`NijaR0<(o z3}MNmVY8@bjR#DZX%rL?5&9js4b=F|aD2uKY8`l@$H5DE&f!5@TbEC_Kv0RCC z>&kDE}dqq)EbnAye=4Y!mU z;&v}Pimv8}qP;{me{j=e=M;Ug_U@vsy9R6q1ppMk;m8sEICgAoV4|v4;f~Q?y%kh| zIVAg`&=@<*LS?^_q83Xmlb!%K9JU_(n^z19PQp;2>MC3O0622e4785jrx@E?VMFM6 zE*#H2g{r$oLJoSvxhVXmK@Ry5eYNko0G|!w(8U3kNdtq8ExrFm*de_r#Hz%T^%Q4kP9z(n6m0jp+I8pLz9-L=>0Z zvZN19^xWmK5WWfw{EfaENZ2zB(l}KSmO4%V-81QF_@Ui(yX^H3bAyYyoTkXSW)v5n zoPbL71s=DGf46GA*|XkheO%6e8YDO^ZD@+CBP# zcl11Do+ZXiI}?u=RaUBGVG~%6?=%kXmUZ~5m9@_@PAEAxuP1&+J*h4}A0wN5DLpW* zmd^<|gEPAcn=j-eQ&7(;`AMwxCm_pfN`X;BWd8BQcf9(a#-b_e!p%ff*5Dg78Hfo? zu_gx-R8sSKMIUi;!T!uYh*L=6&s|?$a%z1-+;HQkWYu#69Q>GuZj;U?`p2M-lexxnHB#7FdvI_XNtJ1N?qIqoTlJh?RhI*C%iz@&C5%KZ; delta 2588 zcmV+%3gh+q6!{bfABzYGk|yDi2O}tla-zS@QYb4?(L=p)(H*lDRxx8IZ9^Z6^cglxrW*R-aZNp=^cmG;y-0sz zRz(bp(c_V&BsY0eL2;#V?>T=+Wg3lDtGZlPE?nro!v`a4ip8RNr65-{Hs;@TArh=v zQ}t8BbZ@P&J+i9rmy!V$e_`t-9sanmI zRCCw%3l|nr+kT1Hc>{D!JCmqGH5XJqe}JO-taBHIGqtSf#@PkcaM(ntD7`|h1r^R8 z052DvN#1nsO8h%6iF2JdVL@8Fi8>3aRX!_Q%84_IWrM{no_6Ole=O7t{|ZJig@z$v zKUxRTdov@Y)}|LwceQG(MP1ewc0$hya2u4HqDaf=GW$SsS*rO{Q)dQJw zjt!eUtf{W*#pK9HLJCeTmf-Zrf=A{qZy9FZIIH)dfpj1wDw-LsSXweT^tu7J=SG{I z*Mu_FHZ}QtM%5u9e<%Y9@Ui-1%iI8TlYc}%;!X4w$+^wHqRYVVoO4M6Xa0h&^c|fE zyt=-@Hf3ERpc{{k<-oVuv9V1X;@G`uzJ5PDL#Ev<=kvRmQr?(ErO6n7!so!ozoSHm z=M*~X;J9DG+V40wcpW?%b<$n4!D3S=HZ`JHX{9qM(Z!S(e@I}~GpmBtrBPkFQymCt zf{;s~whjt!!4Rrj37^vx8!;|Aclj3>o^uCz%Z20AA`)*PP5?x7e6}F2EC!oNE*?=a30p_HP)F;O|}RYLf?# zuOx^KcQ)61tm2k z$eHw*oJBa)dDpp0Sk1HLU=?dn#hMs@&RatG6#RfBm?DX}EGFaJbgoGJ3n{zi5;PMm zeJx5~+fO<)Mdla6f<>)EQR{qBh^Z|M7lD6-i%&2@e=FD$nIs~XV;ZS(Tc6aXh*nMzs^x%QS>O&D}=Qe6V z;|Mf%eM$@O{XpRVh6wAPAxqh^l3EvL&Bsakco^ja6q(@^tszAa zSzeLJG)V5PuHXeqFr1KIevv3OG(>tpNAg1He=K^fQxjETSKyFV&QEiG=)TSyAp&f2 zEun-6^_r^ZS;QiNnva4AkS4qpgD)Yy6v|)MkywaU4|o%1^cf#?^BalsG{A^QEM>vV zFnAGA)G;o>yEtr?lTH0$3 z{80Z#T8Eo`cfezvsxB;2(VF@ji&l}^VEv~Twq)IJDbQ*ct*=ofRvO+mO`=)|uhBMf z^}X~10ML)Vi(co3=b3yK?FT`Vw|=5!f1M2j!YgqfifyeIHSh}LjfM>Fs!@WwMv-Qg ztjnX!Gy8C`+NCgPnG-gNv8_+w?uCRIWZ{YOs@(@(aq5XAC+_Ja8o$!$ya-GXr{_6f z7|mY2`oLG?JMtumTPgMdBF=WWbXQ>yt*KRERC@L6gKmW_uhw^J9`Rl&DCXN|e~v@( zUGg#nb_eB%P}tkmv2z7Q`Dw2)zrt+b#>z_+;nu;cl^+d}1Jm|5FSI#qibJC*oB=e5 zH8;%G=}fDREMdrX?F6X+fi3I`1IsI*-}ENzwqi{8a6##d{P_md33oaymq?#>_loY$ z63c6UZoifaMn3S!*=GjE@S}Zbf60}U+^MW_t}kaLR@V9qhy$MAGSJz7yf=%1%V!iU3GCnm``ZuXhrev#ZzyJdZO1yy&b~+Y z94a|Chj4LHJ|o*XOEp=hYAbsFgr%IWC^lQkN~#@MK~&`xZClAOY&g#&f5ujiNo3WCm6gIAiZ-(1qdF70nQ&vb5Fm(x*Cb4Sb!s2s2Dus|P zhOp$(utn2;lg$i2U)_*b&q!qT8&{NNtElQxW|(03=D7*?I%_aylttHyO&bdEnhoE+ z8P>)XSxwHJfF>yo`YSqs12PuPEEM+{8W8k>v;A`Q9!z>sW|6 zwe|+3cOydgw>hRQ(wui7>T->8+#}cPZMLz-bLZ}h#Mek0;wIGUXfE&pX0~ZW!!4zT zxZSIcqN_QgXfKfsf7~?LIYl3=y}M}Zt^u1t0RY8sJbDyAjvpTzn5e2%xMTEJZv_=# z4#|EfG{(-cP}#4fsKpY?q{qPxhph+y<`sj2lQ0yhy2=(m0FInA1Fd7vX~yD-7>2xE=!K#YOx>>0J#l2_vK7d+!-#o>w2-HLBYM90XI_0H5yfS< zEa^iNJ$Gd+gs%bvf1|Gk680Q}G)`57rH&Io_e^>UerR{yE_?mM+~8s^rzx_o8O6mX zC!i92iN~$tf2~??Hm(nuhN!N~vR;w3KRyd6e*|!RRpY)8&cx$Im6a-4*aVj2yNpA7WF5Y0W$m|&lS+=w>xrLLPpON~$H*pMN)L>y z0K;btN#Yw!)448#Pc zSQCQ@DyjLrqK`PaV1MQx#VMrl=dLd=Iki3^Zn*JNvg)}34t`8Sw@K&GqaLrvkN;ct yK*v}Ay-B Date: Sun, 27 Dec 2020 01:26:19 +0100 Subject: [PATCH 82/94] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D1=81=20=D0=B4=D1=80=D0=BE=D0=B1=D0=BD=D1=8B?= =?UTF-8?q?=D0=BC=D0=B8=20=D1=87=D0=B8=D1=81=D0=BB=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Consts.h | 4 ++-- platformio.ini | 2 +- src/items/vLogging.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index fd4b8f24..a0b8d159 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,9 +2,9 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 274 -#define ESP8266_FLASH_SIZE_1MB false +#define ESP8266_FLASH_SIZE_1MB true //===========FileSystem============================================================================================================================================== -#define USE_LITTLEFS true +#define USE_LITTLEFS false //================================================================================================================================================================== #define NUM_BUTTONS 6 #define LED_PIN LED_BUILTIN diff --git a/platformio.ini b/platformio.ini index 5ce94223..1e8b8ece 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = esp8266 +default_envs = esp8266_01_1m [common_env_data] lib_deps_external = diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 3593afa4..49b23c26 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -36,8 +36,8 @@ void LoggingClass::execute(String keyOrValue) { } else { SerialPrint("E", "Logging", "This value not found on this device"); } - } else { //прилетело из события - if (isDigitStr(keyOrValue)) { //если это число + } else { //прилетело из события + if (isDigitStr(keyOrValue) || keyOrValue.indexOf(".") != -1) { //если это число или дробное число loggingValue = keyOrValue; } else { //если это ключ if (getValue(_loggingValueKey) != "no value") { From dad08c168630ddeec6f987ca267067707c622f53 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 30 Dec 2020 13:48:29 +0100 Subject: [PATCH 83/94] =?UTF-8?q?=D0=92=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20esp32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Consts.h | 7 ++- include/FileSystem.h | 16 +++++-- include/SoftUART.h | 7 +++ platformio.ini | 8 ++-- src/FileSystem.cpp | 26 ++++++----- src/Utils/WiFiUtils.cpp | 91 ++++++++++++++++++------------------- src/items/vSensorAnalog.cpp | 3 +- src/main.cpp | 6 ++- 8 files changed, 92 insertions(+), 72 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index a0b8d159..2a228f76 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,11 +2,14 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 274 -#define ESP8266_FLASH_SIZE_1MB true +#define ESP8266_FLASH_SIZE_1MB false //===========FileSystem============================================================================================================================================== -#define USE_LITTLEFS false +#define USE_LITTLEFS true //================================================================================================================================================================== #define NUM_BUTTONS 6 +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif #define LED_PIN LED_BUILTIN //===========MQTT================================================================================================================================================= #define MQTT_RECONNECT_INTERVAL 20000 diff --git a/include/FileSystem.h b/include/FileSystem.h index 48706d84..ae31147e 100644 --- a/include/FileSystem.h +++ b/include/FileSystem.h @@ -8,8 +8,16 @@ #include +#ifdef ESP32 +#include +extern FS* filesystem; +#define FileFS SPIFFS +#define FS_NAME "SPIFFS" +#endif + +#ifdef ESP8266 #if USE_LITTLEFS -#include +#include "LittleFS.h" extern FS LittleFS; using littlefs_impl::LittleFSConfig; extern FS* filesystem; @@ -20,7 +28,9 @@ extern FS* filesystem; #define FileFS SPIFFS #define FS_NAME "SPIFFS" #endif - +#endif extern void getFSInfo(); -extern bool getInfo(FSInfo& info); \ No newline at end of file +#ifdef ESP8266 +extern bool getInfo(FSInfo& info); +#endif \ No newline at end of file diff --git a/include/SoftUART.h b/include/SoftUART.h index 8b928a36..6cd707f9 100644 --- a/include/SoftUART.h +++ b/include/SoftUART.h @@ -3,7 +3,14 @@ #ifdef uartEnable #include "SoftwareSerial.h" +#ifdef ESP8266 +#include extern SoftwareSerial* myUART; +#else +#include +extern HardwareSerial* myUART; +#endif + extern void uartInit(); extern void uartHandle(); diff --git a/platformio.ini b/platformio.ini index 1e8b8ece..28d42848 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = esp8266_01_1m +default_envs = esp32 [common_env_data] lib_deps_external = @@ -28,6 +28,7 @@ lib_deps_internal = [env:esp32] framework = arduino board = esp32dev +;board_build.ldscript = eagle.flash.4m2m.ld platform = https://github.com/platformio/platform-espressif32.git lib_deps = ${common_env_data.lib_deps_external} @@ -36,11 +37,12 @@ lib_deps = madhephaestus/ESP32Servo luc-github/ESP32SSDP CTBot + EspSoftwareSerial monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 -board_build.filesystem = littlefs -extra_scripts = ./tools/littlefsbuilder.py +;board_build.filesystem = littlefs +;extra_scripts = ./tools/littlefsbuilder.py [env:esp8266_01_1m] framework = arduino diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 2c65f899..a73e6657 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -1,21 +1,27 @@ #include "FileSystem.h" + #include "Global.h" +#ifdef ESP8266 +bool getInfo(FSInfo& info) { + return FileFS.info(info); +} + // Информация о ФС void getFSInfo() { FSInfo buf; if (getInfo(buf)) { SerialPrint("I", F("FS"), F("Get FS info completed")); - size_t totalBytes = buf.totalBytes; // всего - size_t usedBytes = buf.usedBytes; // использовано - size_t maxOpenFiles = buf.maxOpenFiles; // лимит на открые файлы + size_t totalBytes = buf.totalBytes; // всего + size_t usedBytes = buf.usedBytes; // использовано + size_t maxOpenFiles = buf.maxOpenFiles; // лимит на открые файлы size_t blockSize = buf.blockSize; size_t pageSize = buf.pageSize; - size_t maxPathLength = buf.maxPathLength; // лимит на пути и имена файлов + size_t maxPathLength = buf.maxPathLength; // лимит на пути и имена файлов size_t freeBytes = totalBytes - usedBytes; - float freePer = ((float) freeBytes / totalBytes) * 100; - + float freePer = ((float)freeBytes / totalBytes) * 100; + jsonWriteStr(configSetupJson, F("freeBytes"), String(freePer) + "% (" + prettyBytes(freeBytes) + ")"); //SerialPrint("I", F("FS"), "totalBytes=" + String(totalBytes)); @@ -26,12 +32,8 @@ void getFSInfo() { //SerialPrint("I", F("FS"), "maxPathLength=" + String(maxPathLength)); //SerialPrint("I", F("FS"), "freeBytes=" + String(freeBytes)); //SerialPrint("I", F("FS"), "freePer=" + String(freePer)); - } - else { + } else { SerialPrint("E", F("FS"), F("FS info error")); } } - -bool getInfo(FSInfo& info) { - return FileFS.info(info); -} \ No newline at end of file +#endif diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp index 8a948ee7..2f09705d 100644 --- a/src/Utils/WiFiUtils.cpp +++ b/src/Utils/WiFiUtils.cpp @@ -1,11 +1,11 @@ #include "Utils/WiFiUtils.h" + #include "FileSystem.h" void routerConnect() { - WiFi.setAutoConnect(false); WiFi.persistent(false); - + setLedStatus(LED_SLOW); WiFi.mode(WIFI_STA); byte tries = 20; @@ -15,10 +15,13 @@ void routerConnect() { if (_ssid == "" && _password == "") { WiFi.begin(); - } - else { + } else { WiFi.begin(_ssid.c_str(), _password.c_str()); +#ifdef ESP32 + WiFi.setTxPower(WIFI_POWER_19_5dBm); +#else WiFi.setOutputPower(20.5); +#endif SerialPrint("I", "WIFI", "ssid: " + _ssid); } @@ -35,8 +38,7 @@ void routerConnect() { if (WiFi.status() != WL_CONNECTED) { Serial.println(""); startAPMode(); - } - else { + } else { Serial.println(""); SerialPrint("I", "WIFI", "http://" + WiFi.localIP().toString()); jsonWriteStr(configSetupJson, "ip", WiFi.localIP().toString()); @@ -80,7 +82,6 @@ bool startAPMode() { return true; } - boolean RouterFind(String ssid) { bool res = false; int n = WiFi.scanComplete(); @@ -121,24 +122,19 @@ uint8_t RSSIquality() { if (WiFi.status() == WL_CONNECTED) { int rssi = WiFi.RSSI(); if (rssi >= -50) { - res = 6; //"Excellent"; + 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"; } - 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; } @@ -146,33 +142,32 @@ void wifiSignalInit() { ts.add( SYGNAL, 1000 * 60, [&](void*) { SerialPrint("I", "System", printMemoryStatus()); +#ifdef ESP8266 getFSInfo(); +#endif switch (RSSIquality()) { - case 0: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); - break; - case 1: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); - break; - case 2: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); - break; - case 3: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); - break; - case 4: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); - break; - case 5: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); - break; - case 6: - jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); - break; + case 0: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: не подключено к роутеру")); + break; + case 1: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: нет сигнала")); + break; + case 2: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень низкий")); + break; + case 3: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: низкий")); + break; + case 4: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: хороший")); + break; + case 5: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: очень хороший")); + break; + case 6: + jsonWriteStr(configSetupJson, F("signal"), F("Уровень WiFi сигнала: отличный")); + break; } }, nullptr, true); } - - - diff --git a/src/items/vSensorAnalog.cpp b/src/items/vSensorAnalog.cpp index b449c120..ea1ec282 100644 --- a/src/items/vSensorAnalog.cpp +++ b/src/items/vSensorAnalog.cpp @@ -31,8 +31,7 @@ void SensorAnalog::loop() { void SensorAnalog::readAnalog() { int value; #ifdef ESP32 - int pinInt = pin.toInt(); - value = analogRead(pinInt); + value = analogRead(_adcPin); #endif #ifdef ESP8266 value = analogRead(A0); diff --git a/src/main.cpp b/src/main.cpp index ddb16b5c..bd065a05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,11 +23,11 @@ #include "items/vImpulsOut.h" #include "items/vLogging.h" #include "items/vSensorAnalog.h" +#include "items/vSensorBme280.h" +#include "items/vSensorBmp280.h" #include "items/vSensorDallas.h" #include "items/vSensorDht.h" #include "items/vSensorUltrasonic.h" -#include "items/vSensorBme280.h" -#include "items/vSensorBmp280.h" void not_async_actions(); @@ -71,7 +71,9 @@ void setup() { #ifdef SSDP_ENABLED SsdpInit(); #endif +#ifdef ESP8266 getFSInfo(); +#endif testsPerform(); From 746ecc9c7ad984c3cd604774da6464344758aad3 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:04:42 +0100 Subject: [PATCH 84/94] =?UTF-8?q?=D0=BD=D0=B5=D0=BA=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D1=8B=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Consts.h | 23 +++++++++-------------- src/Web.cpp | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/include/Consts.h b/include/Consts.h index 2a228f76..c2ff87dd 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -2,15 +2,12 @@ //===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 274 -#define ESP8266_FLASH_SIZE_1MB false +#define USE_OTA false //===========FileSystem============================================================================================================================================== #define USE_LITTLEFS true //================================================================================================================================================================== #define NUM_BUTTONS 6 -#ifndef LED_BUILTIN -#define LED_BUILTIN 2 -#endif -#define LED_PIN LED_BUILTIN +#define LED_PIN 2 //===========MQTT================================================================================================================================================= #define MQTT_RECONNECT_INTERVAL 20000 //==========Telemetry============================================================================================================================================= @@ -34,9 +31,8 @@ #define telegramEnable #define uartEnable - #ifdef ESP8266 -#ifdef ESP8266_FLASH_SIZE_1MB +#ifdef USE_OTA #define FIRMWARE_NAME "esp8266-1mb" #else #define FIRMWARE_NAME "esp8266" @@ -55,7 +51,7 @@ enum TimerTask_t { WIFI_SCAN, STATISTICS, UPTIME, UDP, - SYGNAL}; + SYGNAL }; enum NotAsyncActions { do_ZERO, @@ -85,26 +81,25 @@ enum ConfigType_t { }; //history //07.11.2020 (SSDP OFF, UDP OFF) -//RAM: [===== ] 46.8% (used 38376 bytes from 81920 bytes) +//RAM: [===== ] 46.8% (used 38376 bytes from 81920 bytes) //Flash: [===== ] 54.2% (used 566004 bytes from 1044464 bytes) -//13.11.2020 (SSDP OFF, UDP OFF) +//13.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.6% (used 38208 bytes from 81920 bytes) //Flash: [===== ] 54.2% (used 566388 bytes from 1044464 bytes) -//15.11.2020 (SSDP OFF, UDP OFF) +//15.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.1% (used 37780 bytes from 81920 bytes) //Flash: [===== ] 54.3% (used 566656 bytes from 1044464 bytes) -//17.11.2020 (SSDP OFF, UDP OFF) +//17.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 45.7% (used 37476 bytes from 81920 bytes) //Flash: [===== ] 54.5% (used 569296 bytes from 1044464 bytes) //RAM: [===== ] 45.6% (used 37336 bytes from 81920 bytes) //Flash: [====== ] 55.3% (used 577396 bytes from 1044464 bytes) - -//eventBuf - буфер событий которые проверяются в сценариях, +//eventBuf - буфер событий которые проверяются в сценариях, //и если событие удовлетворяет какому нибудь условию то выполняются указанные команды //orderBuf - буфер команд которые выполняются сейчас же \ No newline at end of file diff --git a/src/Web.cpp b/src/Web.cpp index ef30e82a..0d531755 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -344,7 +344,7 @@ void web_init() { String msg = ""; - if (ESP8266_FLASH_SIZE_1MB) { + if (USE_OTA) { msg = F("Обновление невозможно, память устройства 1 мб"); } else { if (lastVersion == FIRMWARE_VERSION) { From 7eac8dde3bc08d779f9f812f6ef9bc348d7e2939 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 30 Dec 2020 16:56:26 +0100 Subject: [PATCH 85/94] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B1=D1=8B=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D1=88=D0=B8?= =?UTF-8?q?=D0=B2=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config.json | 4 +-- include/Consts.h | 68 ++++++++++++++++++++++++------------- include/Macro.h | 7 ++++ platformio.ini | 87 +++++++++++++++++++++++++++++------------------- src/Tests.cpp | 11 ++++-- 5 files changed, 115 insertions(+), 62 deletions(-) create mode 100644 include/Macro.h diff --git a/data/config.json b/data/config.json index 57cfd757..5e722090 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", - "routerpass": "hostel3333", + "routerssid": "WLAN1-Y1GYEF", + "routerpass": "2egY69YTA8DDR7En", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "91.204.228.124", diff --git a/include/Consts.h b/include/Consts.h index c2ff87dd..6bb81719 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -1,18 +1,31 @@ #pragma once -//===========Firmware============================================================================================================================================= #define FIRMWARE_VERSION 274 -#define USE_OTA false -//===========FileSystem============================================================================================================================================== + +#ifdef esp8266_4mb +#define FIRMWARE_NAME "esp8266_4mb" #define USE_LITTLEFS true -//================================================================================================================================================================== -#define NUM_BUTTONS 6 +#define USE_OTA true #define LED_PIN 2 -//===========MQTT================================================================================================================================================= +#endif + +#ifdef esp8266_1mb +#define FIRMWARE_NAME "esp8266_1mb" +#define USE_LITTLEFS false +#define USE_OTA false +#define LED_PIN 2 +#endif + +#ifdef esp32_4mb +#define FIRMWARE_NAME "esp32_4mb" +#define USE_LITTLEFS false +#define USE_OTA true +#define LED_PIN 22 +#endif + +#define NUM_BUTTONS 6 #define MQTT_RECONNECT_INTERVAL 20000 -//==========Telemetry============================================================================================================================================= #define TELEMETRY_UPDATE_INTERVAL_MIN 60 -//=========Configuration========================================================================================================================================== #define DEVICE_CONFIG_FILE "s.conf.csv" #define DEVICE_SCENARIO_FILE "s.scen.txt" //=========System parts=========================================================================================================================================== @@ -22,28 +35,16 @@ //#define LAYOUT_IN_RAM //#define UDP_ENABLED //#define SSDP_ENABLED -//=========Sensors enable/disable================================================================================================================================= #define SensorBme280Enabled #define SensorBmp280Enabled #define SensorDhtEnabled #define PwmOutEnable -//=========Features================================================================================================================================= #define telegramEnable #define uartEnable - -#ifdef ESP8266 -#ifdef USE_OTA -#define FIRMWARE_NAME "esp8266-1mb" -#else -#define FIRMWARE_NAME "esp8266" -#endif -#endif - -#ifdef ESP32 -#define FIRMWARE_NAME "esp32" -#endif - //================================================================================================================================================================ + + + enum TimerTask_t { WIFI_SCAN, WIFI_MQTT_CONNECTION_CHECK, TIME, @@ -79,6 +80,27 @@ enum ConfigType_t { CT_CONFIG, CT_SCENARIO }; + + + + + + + + + + + + + + + + + + + + + //history //07.11.2020 (SSDP OFF, UDP OFF) //RAM: [===== ] 46.8% (used 38376 bytes from 81920 bytes) diff --git a/include/Macro.h b/include/Macro.h new file mode 100644 index 00000000..19651b91 --- /dev/null +++ b/include/Macro.h @@ -0,0 +1,7 @@ +//#pragma once +//#define ST(A) #A +//#define STR(A) ST(A) +// +//#ifdef esp32_4mb +//#pragma message STR(esp32_4mb) +//#endif \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 28d42848..d61bb3a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,8 +8,12 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html +;Please use one of definition: +;esp8266_1mb , esp8266_4mb , esp32_4mb [platformio] -default_envs = esp32 +default_envs = esp32_4mb + + [common_env_data] lib_deps_external = @@ -25,42 +29,10 @@ lib_deps_internal = GyverFilters OneWire -[env:esp32] -framework = arduino -board = esp32dev -;board_build.ldscript = eagle.flash.4m2m.ld -platform = https://github.com/platformio/platform-espressif32.git -lib_deps = - ${common_env_data.lib_deps_external} - ${common_env_data.lib_deps_internal} - AsyncTCP - madhephaestus/ESP32Servo - luc-github/ESP32SSDP - CTBot - EspSoftwareSerial -monitor_filters = esp32_exception_decoder -upload_speed = 921600 -monitor_speed = 115200 -;board_build.filesystem = littlefs -;extra_scripts = ./tools/littlefsbuilder.py -[env:esp8266_01_1m] -framework = arduino -board = nodemcuv2 -board_build.ldscript = eagle.flash.1m256.ld -platform = https://github.com/platformio/platform-espressif8266.git -lib_deps = - ${common_env_data.lib_deps_external} - ${common_env_data.lib_deps_internal} - ESPAsyncTCP - ESPAsyncUDP - EspSoftwareSerial - CTBot -monitor_filters = esp8266_exception_decoder -upload_speed = 921600 -monitor_speed = 115200 -[env:esp8266] +[env:esp8266_4mb] +build_flags = -Desp8266_1mb="esp8266_4mb" framework = arduino board = nodemcuv2 board_build.ldscript = eagle.flash.4m1m.ld @@ -76,3 +48,48 @@ monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 board_build.filesystem = littlefs + + + +[env:esp8266_1mb] +build_flags = -Desp8266_1mb="esp8266_1mb" +framework = arduino +board = nodemcuv2 +board_build.ldscript = eagle.flash.1m256.ld +platform = https://github.com/platformio/platform-espressif8266.git +lib_deps = + ${common_env_data.lib_deps_external} + ${common_env_data.lib_deps_internal} + ESPAsyncTCP + ESPAsyncUDP + EspSoftwareSerial + CTBot +monitor_filters = esp8266_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 + + + +[env:esp32_4mb] +build_flags = -Desp32_4mb="esp32_4mb" +framework = arduino +board = esp32dev +platform = https://github.com/platformio/platform-espressif32.git +lib_deps = + ${common_env_data.lib_deps_external} + ${common_env_data.lib_deps_internal} + AsyncTCP + madhephaestus/ESP32Servo + luc-github/ESP32SSDP + CTBot + EspSoftwareSerial +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 + + + + + + + diff --git a/src/Tests.cpp b/src/Tests.cpp index f64fe70e..1e6c305e 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -1,12 +1,19 @@ #include "Tests.h" - +#include "Macro.h" #include "Global.h" #include "ItemsList.h" void testsPerform() { Serial.println("====some tests section===="); - + #ifdef esp32_4mb + Serial.println("esp32_4mb defined"); + #endif + #ifdef esp8266_1mb + Serial.println("esp32_4mb defined"); + #endif + + //Serial.println(STR(esp32_4mb)); Serial.println("==========end============"); } \ No newline at end of file From 0fa7876b80ea7d723dbda873a6a9d568570eb9fc Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:02:11 +0100 Subject: [PATCH 86/94] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Macro.h | 4 +++- platformio.ini | 4 ++-- src/Tests.cpp | 11 +---------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/include/Macro.h b/include/Macro.h index 19651b91..bf1fe7ce 100644 --- a/include/Macro.h +++ b/include/Macro.h @@ -4,4 +4,6 @@ // //#ifdef esp32_4mb //#pragma message STR(esp32_4mb) -//#endif \ No newline at end of file +//#endif + +//Serial.println(STR(esp32_4mb)); \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index d61bb3a2..a74a9eca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,7 +11,7 @@ ;Please use one of definition: ;esp8266_1mb , esp8266_4mb , esp32_4mb [platformio] -default_envs = esp32_4mb +default_envs = esp8266_4mb @@ -32,7 +32,7 @@ lib_deps_internal = [env:esp8266_4mb] -build_flags = -Desp8266_1mb="esp8266_4mb" +build_flags = -Desp8266_4mb="esp8266_4mb" framework = arduino board = nodemcuv2 board_build.ldscript = eagle.flash.4m1m.ld diff --git a/src/Tests.cpp b/src/Tests.cpp index 1e6c305e..6b1d9cad 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -5,15 +5,6 @@ void testsPerform() { Serial.println("====some tests section===="); - - #ifdef esp32_4mb - Serial.println("esp32_4mb defined"); - #endif - #ifdef esp8266_1mb - Serial.println("esp32_4mb defined"); - #endif - - //Serial.println(STR(esp32_4mb)); - + Serial.println("==========end============"); } \ No newline at end of file From e39806e0ac035b958cf169e004a3f7d0a03ad9b5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 2 Jan 2021 03:26:16 +0100 Subject: [PATCH 87/94] =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=B0=D0=BB=D1=8C=D1=82=D0=BD=D1=80=D0=BD?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D0=B2=D0=BD=D1=8B=D0=B9=20=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=B4=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Utils/FileUtils.h | 6 ++++ include/Utils/StringUtils.h | 4 ++- include/items/vLogging.h | 3 +- platformio.ini | 3 +- src/Tests.cpp | 10 ++++-- src/Utils/FileUtils.cpp | 35 +++++++++++++++------ src/Utils/StringUtils.cpp | 19 +++++++++--- src/items/vLogging.cpp | 62 ++++++++++++++++++++++++++++++++++--- 8 files changed, 117 insertions(+), 25 deletions(-) diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h index 3c33557e..7e176723 100644 --- a/include/Utils/FileUtils.h +++ b/include/Utils/FileUtils.h @@ -1,5 +1,6 @@ #pragma once #include + #include "Consts.h" #include "FileSystem.h" @@ -44,6 +45,11 @@ const String writeFile(const String& filename, const String& str); */ const String readFile(const String& filename, size_t max_size); +/* +* Чтение файла в строку с записью его размера +*/ +const String readFileSz(const String& filename, size_t max_size, size_t& size); + /* * Размер файла */ diff --git a/include/Utils/StringUtils.h b/include/Utils/StringUtils.h index 6ba28348..b2c52464 100644 --- a/include/Utils/StringUtils.h +++ b/include/Utils/StringUtils.h @@ -22,7 +22,9 @@ String deleteToMarkerLast(String str, String found); String selectFromMarkerToMarker(String str, String found, int number); -size_t itemsCount(String str, const String& separator); +size_t itemsCount2(String& str, const String& separator); + +size_t itemsCount(String& str, const char* delim); boolean isDigitStr(const String&); diff --git a/include/items/vLogging.h b/include/items/vLogging.h index de217983..61a158db 100644 --- a/include/items/vLogging.h +++ b/include/items/vLogging.h @@ -14,7 +14,7 @@ class LoggingClass { ~LoggingClass(); void loop(); - void execute(String payload); + void execute(String keyOrValue); private: @@ -35,4 +35,5 @@ extern void logging(); extern void loggingExecute(); extern void choose_log_date_and_send(); extern void sendLogData(String file, String topic); +extern void sendLogData2(String file, String topic); extern void cleanLogAndData(); diff --git a/platformio.ini b/platformio.ini index a74a9eca..7af0d773 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,13 +8,12 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -;Please use one of definition: +;To choose board please use one of definition: ;esp8266_1mb , esp8266_4mb , esp32_4mb [platformio] default_envs = esp8266_4mb - [common_env_data] lib_deps_external = bblanchon/ArduinoJson @5.* diff --git a/src/Tests.cpp b/src/Tests.cpp index 6b1d9cad..0ce15233 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -1,10 +1,16 @@ #include "Tests.h" -#include "Macro.h" + #include "Global.h" #include "ItemsList.h" +#include "Macro.h" +#include "Utils/StringUtils.h" void testsPerform() { Serial.println("====some tests section===="); - + + //String str = "Geeks for Geeks "; + // + //Serial.println(itemsCount2(str, " ")); + Serial.println("==========end============"); } \ No newline at end of file diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp index dd8b8a0e..02aa9d9c 100644 --- a/src/Utils/FileUtils.cpp +++ b/src/Utils/FileUtils.cpp @@ -1,9 +1,8 @@ -#include "FileSystem.h" - #include "Utils/FileUtils.h" -#include "Utils\SerialPrint.h" -#include "Utils/StringUtils.h" +#include "FileSystem.h" +#include "Utils/StringUtils.h" +#include "Utils\SerialPrint.h" const String filepath(const String& filename) { return filename.startsWith("/") ? filename : "/" + filename; @@ -22,10 +21,10 @@ void removeFile(const String& filename) { String path = filepath(filename); if (FileFS.exists(path)) { if (!FileFS.remove(path)) { - SerialPrint("I","Files","remove " + path); + SerialPrint("I", "Files", "remove " + path); } } else { - SerialPrint("E","Files","not exist" + path); + SerialPrint("E", "Files", "not exist" + path); } } @@ -33,7 +32,7 @@ File seekFile(const String& filename, size_t position) { String path = filepath(filename); auto file = FileFS.open(path, "r"); if (!file) { - SerialPrint("[E]","Files","open " + path); + SerialPrint("[E]", "Files", "open " + path); } // поставим курсор в начало файла file.seek(position, SeekSet); @@ -79,14 +78,14 @@ const String addFile(const String& filename, const String& str) { bool copyFile(const String& src, const String& dst, bool overwrite) { String srcPath = filepath(src); String dstPath = filepath(dst); - SerialPrint("I","Files","copy " + srcPath + " to " + dstPath); + SerialPrint("I", "Files", "copy " + srcPath + " to " + dstPath); if (!FileFS.exists(srcPath)) { - SerialPrint("[E]","Files","not exist: " + srcPath); + SerialPrint("[E]", "Files", "not exist: " + srcPath); return false; } if (FileFS.exists(dstPath)) { if (!overwrite) { - SerialPrint("[E]","Files","already exist: " + dstPath); + SerialPrint("[E]", "Files", "already exist: " + dstPath); return false; } FileFS.remove(dstPath); @@ -131,6 +130,22 @@ const String readFile(const String& filename, size_t max_size) { return temp; } +const String readFileSz(const String& filename, size_t max_size, size_t& size) { + String path = filepath(filename); + auto file = FileFS.open(path, "r"); + if (!file) { + return "failed"; + } + size = file.size(); + if (size > max_size) { + file.close(); + return "large"; + } + String temp = file.readString(); + file.close(); + return temp; +} + const String getFileSize(const String filename) { String filepath(filename); auto file = FileFS.open(filepath, "r"); diff --git a/src/Utils/StringUtils.cpp b/src/Utils/StringUtils.cpp index a158fa94..765424d8 100644 --- a/src/Utils/StringUtils.cpp +++ b/src/Utils/StringUtils.cpp @@ -1,4 +1,5 @@ #include "Utils/StringUtils.h" + #include "Consts.h" String selectToMarkerLast(String str, String found) { @@ -75,7 +76,7 @@ uint16_t hexStringToUint16(String hex) { } } -size_t itemsCount(String str, const String& separator) { +size_t itemsCount2(String& str, const String& separator) { // если строки поиск нет сразу выход if (str.indexOf(separator) == -1) { return 0; @@ -91,6 +92,19 @@ size_t itemsCount(String str, const String& separator) { return cnt; } +size_t itemsCount(String& str, const char* delim) { + size_t cnt = 0; + char* cstr = new char[str.length() + 1]; + strcpy(cstr, str.c_str()); + char* token; + while ((token = strtok_r(cstr, delim, &cstr))) { + cnt++; + //printf("%s\n", token); + } + delete[] cstr; + return cnt; +} + boolean isDigitStr(const String& str) { for (size_t i = 0; i < str.length(); i++) { if (!isDigit(str.charAt(i))) { @@ -110,6 +124,3 @@ String prettyBytes(size_t size) { else return String(size / 1024.0 / 1024.0 / 1024.0) + "GB"; } - - - diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 49b23c26..e6b999f4 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -36,7 +36,7 @@ void LoggingClass::execute(String keyOrValue) { } else { SerialPrint("E", "Logging", "This value not found on this device"); } - } else { //прилетело из события + } else { //прилетело из события if (isDigitStr(keyOrValue) || keyOrValue.indexOf(".") != -1) { //если это число или дробное число loggingValue = keyOrValue; } else { //если это ключ @@ -49,22 +49,33 @@ void LoggingClass::execute(String keyOrValue) { } String filename = "logs/" + _key + ".txt"; - String logData = readFile(filename, 5120); - size_t lines_cnt = itemsCount(logData, "\r\n"); + size_t sz = 0; - SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " (" + String(lines_cnt, DEC) + ")"); + String logData = readFileSz(filename, 10240, sz); + + size_t lines_cnt = itemsCount2(logData, "\r\n"); + + SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(lines_cnt, DEC) + ", size " + String(sz) + ", heap " + ESP.getFreeHeap()); + + if (logData == "large") { + SerialPrint("E", "Logging", "File is very large"); + } if ((lines_cnt > _maxPoints + 1) || !lines_cnt) { removeFile(filename); lines_cnt = 0; + SerialPrint("E", "Logging", "file been remooved: " + filename + " " + String(lines_cnt) + ">" + String(_maxPoints)); } if (loggingValue != "") { if (lines_cnt > _maxPoints) { //удаляем старую строку и добавляем новую + //for (int i = 0; i < 5; i++) { logData = deleteBeforeDelimiter(logData, "\r\n"); + //} if (timeNow->hasTimeSynced()) { logData += timeNow->getTimeUnix() + " " + loggingValue + "\r\n"; + writeFile(filename, logData); } } else { //просто добавляем новую строку @@ -73,6 +84,7 @@ void LoggingClass::execute(String keyOrValue) { } } } + String buf = "{}"; jsonWriteInt(buf, "x", timeNow->getTimeUnix().toInt()); jsonWriteFloat(buf, "y1", loggingValue.toFloat()); @@ -123,7 +135,7 @@ void choose_log_date_and_send() { } } -void sendLogData(String file, String topic) { +void sendLogData2(String file, String topic) { String log_date = readFile(file, 5120); if (log_date != "failed") { log_date.replace("\r\n", "\n"); @@ -156,6 +168,46 @@ void sendLogData(String file, String topic) { } } +void sendLogData(String file, String topic) { + File configFile = FileFS.open(file, "r"); + if (!configFile) { + return; + } + configFile.seek(0, SeekSet); + int i = 0; + String buf = "{}"; + String json_array; + String unix_time; + String value; + unsigned int psn; + unsigned int sz = configFile.size(); + do { + i++; + psn = configFile.position(); + String line = configFile.readStringUntil('\n'); + unix_time = selectToMarker(line, " "); + jsonWriteInt(buf, "x", unix_time.toInt()); + value = deleteBeforeDelimiter(line, " "); + jsonWriteFloat(buf, "y1", value.toFloat()); + if (unix_time != "" || value != "") { + json_array += buf + ","; + } + if (i >= 100) { + json_array = "{\"status\":[" + json_array + "]}"; + json_array.replace("},]}", "}]}"); + publishChart(topic, json_array); + json_array = ""; + i = 0; + } + } while (psn < sz); + + configFile.close(); + + json_array = "{\"status\":[" + json_array + "]}"; + json_array.replace("},]}", "}]}"); + publishChart(topic, json_array); +} + void cleanLogAndData() { #ifdef ESP8266 auto dir = FileFS.openDir("logs"); From f8d574d15a55339f3aeb28e4015173059ca19b4d Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 2 Jan 2021 03:28:43 +0100 Subject: [PATCH 88/94] change --- include/Utils/StringUtils.h | 2 +- src/Utils/StringUtils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Utils/StringUtils.h b/include/Utils/StringUtils.h index b2c52464..2bf412cc 100644 --- a/include/Utils/StringUtils.h +++ b/include/Utils/StringUtils.h @@ -22,7 +22,7 @@ String deleteToMarkerLast(String str, String found); String selectFromMarkerToMarker(String str, String found, int number); -size_t itemsCount2(String& str, const String& separator); +size_t itemsCount2(String str, const String& separator); size_t itemsCount(String& str, const char* delim); diff --git a/src/Utils/StringUtils.cpp b/src/Utils/StringUtils.cpp index 765424d8..08ec5fa9 100644 --- a/src/Utils/StringUtils.cpp +++ b/src/Utils/StringUtils.cpp @@ -76,7 +76,7 @@ uint16_t hexStringToUint16(String hex) { } } -size_t itemsCount2(String& str, const String& separator) { +size_t itemsCount2(String str, const String& separator) { // если строки поиск нет сразу выход if (str.indexOf(separator) == -1) { return 0; From 673bfe6b6bf21b3fcfdd39706380b0b4143c9611 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 2 Jan 2021 04:27:19 +0100 Subject: [PATCH 89/94] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BD=D0=BE=20=D0=BB=D0=BE=D0=B3=D0=B3=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Utils/FileUtils.h | 6 +++--- src/Utils/FileUtils.cpp | 30 +++++++++++++++++------------- src/items/vLogging.cpp | 29 +++++++++++++---------------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h index 7e176723..686b3a2b 100644 --- a/include/Utils/FileUtils.h +++ b/include/Utils/FileUtils.h @@ -46,14 +46,14 @@ const String writeFile(const String& filename, const String& str); const String readFile(const String& filename, size_t max_size); /* -* Чтение файла в строку с записью его размера +* Посчитать */ -const String readFileSz(const String& filename, size_t max_size, size_t& size); +size_t countLines(const String filename); /* * Размер файла */ -const String getFileSize(const String& filename); +size_t getFileSize(const String filename); bool copyFile(const String& src, const String& dst, bool overwrite = true); diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp index 02aa9d9c..aa8cbaed 100644 --- a/src/Utils/FileUtils.cpp +++ b/src/Utils/FileUtils.cpp @@ -130,31 +130,35 @@ const String readFile(const String& filename, size_t max_size) { return temp; } -const String readFileSz(const String& filename, size_t max_size, size_t& size) { +size_t countLines(const String filename) { + size_t cnt = -1; String path = filepath(filename); auto file = FileFS.open(path, "r"); if (!file) { - return "failed"; + return cnt; } - size = file.size(); - if (size > max_size) { - file.close(); - return "large"; - } - String temp = file.readString(); + file.seek(0, SeekSet); + size_t size = file.size(); + size_t psn; + do { + cnt++; + file.readStringUntil('\n'); + psn = file.position(); + } while (psn < size); file.close(); - return temp; + return cnt; } -const String getFileSize(const String filename) { +size_t getFileSize(const String filename) { + size_t size = -1; String filepath(filename); auto file = FileFS.open(filepath, "r"); if (!file) { - return "failed"; + return size; } - size_t size = file.size(); + size = file.size(); file.close(); - return String(size); + return size; } const String getFSSizeInfo() { diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index e6b999f4..cff4371d 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -49,27 +49,24 @@ void LoggingClass::execute(String keyOrValue) { } String filename = "logs/" + _key + ".txt"; + + size_t cnt = countLines(filename); + size_t sz = getFileSize(filename); - size_t sz = 0; + SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(cnt, DEC) + ", size " + String(sz) + ", heap " + ESP.getFreeHeap()); - String logData = readFileSz(filename, 10240, sz); - - size_t lines_cnt = itemsCount2(logData, "\r\n"); - - SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(lines_cnt, DEC) + ", size " + String(sz) + ", heap " + ESP.getFreeHeap()); - - if (logData == "large") { - SerialPrint("E", "Logging", "File is very large"); - } - - if ((lines_cnt > _maxPoints + 1) || !lines_cnt) { - removeFile(filename); - lines_cnt = 0; - SerialPrint("E", "Logging", "file been remooved: " + filename + " " + String(lines_cnt) + ">" + String(_maxPoints)); + if ((cnt > _maxPoints + 1) || cnt == -1) { + removeFile(filename); + SerialPrint("E", "Logging", "file been remooved: " + filename + " " + String(cnt) + ">" + String(_maxPoints)); + cnt = 0; } if (loggingValue != "") { - if (lines_cnt > _maxPoints) { //удаляем старую строку и добавляем новую + if (cnt > _maxPoints) { //удаляем старую строку и добавляем новую + String logData = readFile(filename, 10240); + if (logData == "large") { + SerialPrint("E", "Logging", "File is very large"); + } //for (int i = 0; i < 5; i++) { logData = deleteBeforeDelimiter(logData, "\r\n"); //} From 401564a7781d6ef8d36b2cc231e4ef64bea09ca0 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sat, 2 Jan 2021 13:07:45 +0100 Subject: [PATCH 90/94] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D1=80=D0=BE=D0=B1=D0=B5=D0=B9=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D1=82=D1=80=D0=B8=D1=86=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D1=87=D0=B8=D1=81=D0=B5=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Utils/StringUtils.h | 4 +++- src/Tests.cpp | 2 ++ src/Utils/StringUtils.cpp | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/Utils/StringUtils.h b/include/Utils/StringUtils.h index 2bf412cc..e98a8f76 100644 --- a/include/Utils/StringUtils.h +++ b/include/Utils/StringUtils.h @@ -26,7 +26,9 @@ size_t itemsCount2(String str, const String& separator); size_t itemsCount(String& str, const char* delim); -boolean isDigitStr(const String&); +boolean isDigitStr(const String& str); + +boolean isDigitDotCommaStr(const String& str); String prettyBytes(size_t size); diff --git a/src/Tests.cpp b/src/Tests.cpp index 0ce15233..b274c711 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -8,6 +8,8 @@ void testsPerform() { Serial.println("====some tests section===="); + //Serial.println(isDigitDotCommaStr("-12552.5555")); + //String str = "Geeks for Geeks "; // //Serial.println(itemsCount2(str, " ")); diff --git a/src/Utils/StringUtils.cpp b/src/Utils/StringUtils.cpp index 08ec5fa9..11e110bd 100644 --- a/src/Utils/StringUtils.cpp +++ b/src/Utils/StringUtils.cpp @@ -114,6 +114,16 @@ boolean isDigitStr(const String& str) { return str.length(); } +boolean isDigitDotCommaStr(const String& str) { + for (size_t i = 0; i < str.length(); i++) { + char latter = str.charAt(i); + if (!isDigit(latter) && latter != '.' && latter != '-') { + return false; + } + } + return true; +} + String prettyBytes(size_t size) { if (size < 1024) return String(size) + "b"; From a8534c4a73b47451b5434157e4e421962d6c0b47 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 3 Jan 2021 00:33:04 +0100 Subject: [PATCH 91/94] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=82=D0=B8=D0=B8=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B3=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config.json | 9 ++--- data/index.json.gz | Bin 516 -> 543 bytes data/set.dev.json.gz | Bin 545 -> 618 bytes data_ungzip/index.json | 6 ++++ data_ungzip/set.dev.json | 19 ++++++++++ include/Class/ScenarioClass3.h | 2 +- src/Tests.cpp | 2 +- src/Web.cpp | 15 +++++--- src/items/vLogging.cpp | 63 +++++++++------------------------ 9 files changed, 60 insertions(+), 56 deletions(-) diff --git a/data/config.json b/data/config.json index 5e722090..1e32fc2a 100644 --- a/data/config.json +++ b/data/config.json @@ -3,8 +3,8 @@ "chipID": "", "apssid": "IoTmanager", "appass": "", - "routerssid": "WLAN1-Y1GYEF", - "routerpass": "2egY69YTA8DDR7En", + "routerssid": "rise", + "routerpass": "hostel3333", "timezone": 1, "ntp": "pool.ntp.org", "mqttServer": "91.204.228.124", @@ -25,11 +25,12 @@ "webpass": "admin", "MqttIn": "0", "MqttOut": "0", - "blink": "1", + "blink": "0", "oneWirePin": "2", "serverip": "http://206.189.49.244", "uart": "0", "uartS": "9600", "uartTX": "12", - "uartRX": "13" + "uartRX": "13", + "grafmax": "0" } \ No newline at end of file diff --git a/data/index.json.gz b/data/index.json.gz index 8c2326efca8f7ad1eb4bb8cddeaaec018becdeb2..1ba12d451d095c2c4e43857ba75eee08cf85003a 100644 GIT binary patch literal 543 zcmV+)0^t20iwFol{_tM}0BLSyWq2-Xb8l_{&6Uq<+b|Tz?}h#k!spsv+9k~jqqjj0 zJq!Xnj2%>4akR>kXDJ;e1hOA&hYfnzZLq`GKj8i7=C!2z7oPv4J=rlSCG()rv1*^p--l$6*VhPf0wo7L(#{TfY;#=kt0hnHL*_lzN;(^t6wS;ixg-fHqjK=lQ6*XB zNvS)A8^N%I10UcFGWZ5F$ckg9QUgIX0$3K|>ey9-0akEZWS+O~lu;bk%4VH+%Rof6 zuvOEIq71p- z6Zlh1ZC8=~C!D|x;}gU-jaW+H2b|$?pfXGq50^R^8k6W9-%kWR@*<@o!Ms4l zzBVdlK1~wshs;ne^ezwY;|H1Pq`TSacwCvdDs!v7^JJ(bYqDON^l+FvPe*peM}O|z hpwGirtKGi0b+@~{eI>W$4{Uu6=f5j5AK)to0050r3Q7O~ literal 516 zcmV+f0{i_RiwFn-?9*Qa0BLSyWq2-Xb8l_{&6PoG)Ib!+?}dJcA?GG>TX(w_q&Hy? zJqQaP#KSb1WXDcsV& z^M3!wBpGjRFy;hCM>4j~9(!8mwBy%Xo|4DHdv1+(wyr5wK%QQc6xVSzd0AddrSh!O z9gv^|m2mJQUf>MB;sR&InNzEQ;+6nP7UkN+HBm$r+zwmjoqJW3gpIP@x&xtMZ2j$gCH2v| z3it^xaZ$X%Iesf%7gK7;TbvhXjFMNyX)z`FXBknqW>*g20Z60E6a0Ph?1kc324WrO zA!tU!Jya$*Dx?3${ABQG(8BmqM)H;k4;~MPEriJnfLkU6p+ubVq=mA1#ym{ypV_yf;zLGd{om?1mhn#Uqz_#H2( zzg&lzk>Oe&BY@QI`F^VSi5D9aE8ztu@hzB4_&iOm9}3`7*?k#4q%WMXX@9%d^Q3{K zE_0{7%XDP4XtQ3K^k`Z-&+>A|Cx7nJpfAHtr`vt7bHBg4`%iAGKf>k)t$zSd<=(-3 G1^@uZ@+N#@OTBXMJdoTXJ z_r7PR|L}naKfj%@h{VFLc@1N+w(Y`L5m;tv<*i7VzxCfq5sU4k!n49z_?l&mFNvfW zHuTkZHB(pmgPN){JDCRNqDlL}%x*}l!#54wTh3m6Q@=pm zu|Dzi2@J<-WS>J11wY7T-2(GN%t;y` z9xnt4iDX%B7nA23(rIN)(w~h<57cipgor=z`q)zw@R>k0*^tleg^xLk)`@QXUDUfn z%}Yd(+|2Hy+^%*U1K$GuTKSHOgwsS2lUgkzJsj3$lGgUCkLnah(oWu!P*86ZkxR%>IpmwA189XccoG21Ygbpoj$_6{1G1}yt>0?mA?zj%6J z`Wc)vb?!QQ)s<^hi@)!^ z&++8(BM*Q6AQdr7l;82fa>cIgSBou0A${ZKequ- z9B}03&v5gb%76-JR#q2XsXWW9$l?7uv~RrloUl*?01@YR#ly)ik0P{ jHY`hpvRq-!(@=^Q;L#?&(umu1BnokT7 diff --git a/data_ungzip/index.json b/data_ungzip/index.json index 6141ccdd..a520b982 100644 --- a/data_ungzip/index.json +++ b/data_ungzip/index.json @@ -42,6 +42,12 @@ "action": "/?set.utilities", "class": "btn btn-block btn-default" }, + { + "type": "link", + "title": "Настройки разработчика", + "action": "/?set.dev", + "class": "btn btn-block btn-default" + }, { "type": "link", "title": "Скачать приложение IoT Manager для android", diff --git a/data_ungzip/set.dev.json b/data_ungzip/set.dev.json index 4815eb95..6a96ef14 100644 --- a/data_ungzip/set.dev.json +++ b/data_ungzip/set.dev.json @@ -39,6 +39,25 @@ { "type": "hr" }, + { + "type": "h3", + "title": "Количество точек графика выгружаемое за раз" + }, + { + "type": "input", + "title": "ip address", + "name": "graf-arg", + "state": "{{grafmax}}" + }, + { + "type": "button", + "title": "{{ButSave}}", + "action": "set?grafmax=[[graf-arg]]", + "class": "btn btn-block btn-default" + }, + { + "type": "hr" + }, { "type": "h3", "name": "reset-block", diff --git a/include/Class/ScenarioClass3.h b/include/Class/ScenarioClass3.h index 13937370..9c72a0a8 100644 --- a/include/Class/ScenarioClass3.h +++ b/include/Class/ScenarioClass3.h @@ -29,7 +29,7 @@ class Scenario { String setEventSign = selectFromMarkerToMarker(condition, " ", 1); String setEventValue = selectFromMarkerToMarker(condition, " ", 2); - if (!isDigitStr(setEventValue)) { + if (!isDigitDotCommaStr(setEventValue)) { if (setEventValue.indexOf("+-") != -1) { String setEventValueName = selectToMarker(setEventValue, "+-"); String gisteresisValue = selectToMarkerLast(setEventValue, "+-"); diff --git a/src/Tests.cpp b/src/Tests.cpp index b274c711..7ba1141c 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -11,7 +11,7 @@ void testsPerform() { //Serial.println(isDigitDotCommaStr("-12552.5555")); //String str = "Geeks for Geeks "; - // + //Serial.println(itemsCount2(str, " ")); Serial.println("==========end============"); diff --git a/src/Web.cpp b/src/Web.cpp index 0d531755..a1ed35ea 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -325,13 +325,20 @@ void web_init() { serverIP = jsonReadStr(configSetupJson, "serverip"); request->send(200); } - //set?order=button_1 + //set?order=button_1 if (request->hasArg("order")) { String order = request->getParam("order")->value(); - order.replace("_"," "); + order.replace("_", " "); orderBuf += order + ","; request->send(200); } + + if (request->hasArg("grafmax")) { + int value = request->getParam("grafmax")->value().toInt(); + jsonWriteInt(configSetupJson, "grafmax", value); + saveConfig(); + request->send(200); + } }); server.on("/order", HTTP_GET, [](AsyncWebServerRequest* request) { @@ -345,8 +352,6 @@ void web_init() { String msg = ""; if (USE_OTA) { - msg = F("Обновление невозможно, память устройства 1 мб"); - } else { if (lastVersion == FIRMWARE_VERSION) { msg = F("Актуальная версия прошивки уже установлена."); } else if (lastVersion > FIRMWARE_VERSION) { @@ -358,6 +363,8 @@ void web_init() { } else if (lastVersion < FIRMWARE_VERSION) { msg = F("Ошибка версии. Попробуйте повторить позже..."); } + } else { + msg = F("Обновление невозможно, память устройства 1 мб"); } String tmp = "{}"; diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index cff4371d..56f02735 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -36,8 +36,8 @@ void LoggingClass::execute(String keyOrValue) { } else { SerialPrint("E", "Logging", "This value not found on this device"); } - } else { //прилетело из события - if (isDigitStr(keyOrValue) || keyOrValue.indexOf(".") != -1) { //если это число или дробное число + } else { //прилетело из события + if (isDigitDotCommaStr(keyOrValue)) { //если это число или дробное число loggingValue = keyOrValue; } else { //если это ключ if (getValue(_loggingValueKey) != "no value") { @@ -49,21 +49,22 @@ void LoggingClass::execute(String keyOrValue) { } String filename = "logs/" + _key + ".txt"; - + size_t cnt = countLines(filename); size_t sz = getFileSize(filename); - SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(cnt, DEC) + ", size " + String(sz) + ", heap " + ESP.getFreeHeap()); + SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(cnt, DEC) + ", size " + String(sz)); if ((cnt > _maxPoints + 1) || cnt == -1) { - removeFile(filename); + removeFile(filename); SerialPrint("E", "Logging", "file been remooved: " + filename + " " + String(cnt) + ">" + String(_maxPoints)); cnt = 0; } if (loggingValue != "") { - if (cnt > _maxPoints) { //удаляем старую строку и добавляем новую - String logData = readFile(filename, 10240); + if (cnt > _maxPoints) { //удаляем старую строку и добавляем новую + String logData = readFile(filename, 20480); //10240 + SerialPrint("I", "Logging", "Free heap " + ESP.getFreeHeap()); if (logData == "large") { SerialPrint("E", "Logging", "File is very large"); } @@ -132,39 +133,6 @@ void choose_log_date_and_send() { } } -void sendLogData2(String file, String topic) { - String log_date = readFile(file, 5120); - if (log_date != "failed") { - log_date.replace("\r\n", "\n"); - log_date.replace("\r", "\n"); - String buf = "{}"; - String json_array; - String unix_time; - String value; - while (log_date.length()) { - String tmp = selectToMarker(log_date, "\n"); - log_date = deleteBeforeDelimiter(log_date, "\n"); - unix_time = selectToMarker(tmp, " "); - jsonWriteInt(buf, "x", unix_time.toInt()); - value = deleteBeforeDelimiter(tmp, " "); - jsonWriteFloat(buf, "y1", value.toFloat()); - if (log_date.length() < 3) { - json_array += buf; - } else { - json_array += buf + ","; - } - buf = "{}"; - } - unix_time = ""; - value = ""; - log_date = ""; - json_array = "{\"status\":[" + json_array + "]}"; - //SerialPrint("I", "module", json_array); - - publishChart(topic, json_array); - } -} - void sendLogData(String file, String topic) { File configFile = FileFS.open(file, "r"); if (!configFile) { @@ -189,12 +157,15 @@ void sendLogData(String file, String topic) { if (unix_time != "" || value != "") { json_array += buf + ","; } - if (i >= 100) { - json_array = "{\"status\":[" + json_array + "]}"; - json_array.replace("},]}", "}]}"); - publishChart(topic, json_array); - json_array = ""; - i = 0; + int grafmax = jsonReadInt(configSetupJson, "grafmax"); + if (grafmax != 0) { + if (i >= grafmax) { + json_array = "{\"status\":[" + json_array + "]}"; + json_array.replace("},]}", "}]}"); + publishChart(topic, json_array); + json_array = ""; + i = 0; + } } } while (psn < sz); From 965b09cc78f247129c9277933af904fb77c371a0 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 3 Jan 2021 01:20:38 +0100 Subject: [PATCH 92/94] =?UTF-8?q?=D0=94=D0=BE=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/items/vLogging.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 56f02735..51f8a3f7 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -55,16 +55,16 @@ void LoggingClass::execute(String keyOrValue) { SerialPrint("I", "Logging", "http://" + WiFi.localIP().toString() + "/" + filename + " lines " + String(cnt, DEC) + ", size " + String(sz)); - if ((cnt > _maxPoints + 1) || cnt == -1) { + if ((cnt > _maxPoints + 1)) { removeFile(filename); SerialPrint("E", "Logging", "file been remooved: " + filename + " " + String(cnt) + ">" + String(_maxPoints)); cnt = 0; } if (loggingValue != "") { - if (cnt > _maxPoints) { //удаляем старую строку и добавляем новую + if (cnt >= _maxPoints) { //удаляем старую строку и добавляем новую String logData = readFile(filename, 20480); //10240 - SerialPrint("I", "Logging", "Free heap " + ESP.getFreeHeap()); + SerialPrint("I", "Logging", "Free heap " + String(ESP.getFreeHeap())); if (logData == "large") { SerialPrint("E", "Logging", "File is very large"); } From 6ae001232d979dfcf6a8723d5e7a82ffd4811d62 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 3 Jan 2021 02:14:12 +0100 Subject: [PATCH 93/94] telegram --- data/config.json | 1 + data/set.telegram.json.gz | Bin 857 -> 907 bytes data_ungzip/set.telegram.json | 10 ++++++++++ src/FileSystem.cpp | 2 +- src/Telegram.cpp | 7 +++++-- src/Web.cpp | 6 ++++++ src/items/vLogging.cpp | 6 +++--- 7 files changed, 26 insertions(+), 6 deletions(-) diff --git a/data/config.json b/data/config.json index 1e32fc2a..139fc773 100644 --- a/data/config.json +++ b/data/config.json @@ -21,6 +21,7 @@ "telegramApi": "1416711569:AAEI0j83GmXqwzb_gnK1B0Am0gDwZoJt5xo", "telegonof": "0", "teleginput": "0", + "autos": "1", "weblogin": "admin", "webpass": "admin", "MqttIn": "0", diff --git a/data/set.telegram.json.gz b/data/set.telegram.json.gz index 8386cf0920a5893d9689e849e263f2c930853178..e8810e9b6b47105c6d1a6e5b6fae0e90e9459881 100644 GIT binary patch literal 907 zcmV;619bc!iwFpg5Ak0F0CQz@E_7vVWoL3>Z7ynaZ*Bm!R_{*RFcAMfiFdF-8(#?j znWh$)*w7?COhVhfRsG;LZt69)v&3OVs1i^iG!2buFTg7lwu%B>_X_qK?d&v7Cnamq zn4&cHoqcz|-<{7rn0kcJbeXdXb%N;}D(E>#79!DnKt$-xzYjQ@o{Lp3W{&xp_rpk~ zHO0Bel#2r}q;0xZP`7xc0yeBAG?*_fno-@3HOuk*nHjiFtXPTXPr>ydN`?^0Gv_wXVVuQ6r2 z*!j~DE(^+;UbB$qz9ble*DATZT+^e8L#g^>mz1ldTr2U$h+`nas049|!x^unp^@L@ zt-MoT)T!*MV|9kqvFyk@c)zPd*_OR@e2}!~L%SP=!f;$Hj%)}776ZcsV%CxrNza^ zd7B2Fi}!Ps!F52!qMe{91!<(FW8J2E$b_&lTL&DJS%P~Ih1bXr<|PhX)+5pE=6VJ0 zNPTZ|e`B`f;&KgSUTmVoeVh0>wB$8Vz+GyineCVQ$)iYR`PimmkV7k817xt>@p;H> zOIw7EGq2Y4lSdx5ZOR-l75SAHvagQx6@lQux787Ji9=`sH>PpKIfA|_J0S1KYuVO! ziE6QuKP-FQBmRwH}5q39QOokQa>ds<+&3%o!9wWgCcTCMVLWUSgVU8 zIW;u)jLZ7ynaZ*BmsR^4jbFciKY^c_UX)|lT)-UG@sn8|}!B;|%P@ zg<>2ZopW^l^(lu7j}R)fIBQeOFH}%nuYpsHSSvmefxGhF=d7?CiyX|X@MpyfB9WFf zmLpRR_Q8<0=@@?3;O#cpFv@7ME|)^8YsQk*c>OFS94i*A#p|cwdJ?4|Oe9f;!G|%4 z1D9y0_6F!qnN&C-_wbP6F6s9ZSrR68h$jq_v~l2wLL~1mXW4fs>!f>l5s25AvOR45 z>j<|5I+9oNEbZJe{L=hkS=9z zAadm^|3IE2$oCG(YjrBe@`t)mpFnzw)Uh1H^9`0m`5Uxuva6A}g>oc|ta>CG4^t3( zJuTaJ+KY#~sAXe;c6a8Q$*fySp|#Cj)4-mUo?;}=livSh-`?9rf_Dg;Yv?}Fi;4Md zmu9Chqm(0$W*`J-nV9hw?`PpfI6#T7c!HAf8jUAWIYAT=5%+#LGn?Z<6xl~=3cFG+ zl^$13>bnj;s89ybN9JamV<-jbrM9DH(tQ+$u({NQJSZ~+cOeS5SDmbz9BSE(SW8>A zHrxmLj^N(rQq#e$4)EOAM3Z|a@hYh7_K?pVYNExRm-?xqNMv}}q=8>SYiI6OjET54af$p8g7yZJn3&5dp&j8Gh!1o9k07aTs zgIdwusuguP-~(mcn%jgNeX`e54v0r!Dx)Dv8yuJWRF7|^O?$+w>i%uO5ILkRLLwTh z_0mjFLz%G}vh-*e*r67yvgetNewMessage(msg)) { SerialPrint("->", "Telegram", "chat ID: " + String(msg.sender.id) + ", msg: " + String(msg.text)); - jsonWriteInt(configSetupJson, "chatId", msg.sender.id); - saveConfig(); + if (jsonReadBool(configSetupJson, "autos")) { + jsonWriteInt(configSetupJson, "chatId", msg.sender.id); + saveConfig(); + } telegramMsgParse(String(msg.text)); } } @@ -61,6 +63,7 @@ void telegramMsgParse(String msg) { myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), list); SerialPrint("<-", "Telegram", "chat ID: " + String(jsonReadInt(configSetupJson, "chatId")) + "\n" + list); } else { + myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), "ID: " + chipId + ", Name: " + jsonReadStr(configSetupJson, F("name"))); myBot->sendMessage(jsonReadInt(configSetupJson, "chatId"), F("Wrong order, use /all to get all values, /get_id to get value, or /set_id_value to set value")); } } diff --git a/src/Web.cpp b/src/Web.cpp index a1ed35ea..50343648 100644 --- a/src/Web.cpp +++ b/src/Web.cpp @@ -255,6 +255,12 @@ void web_init() { saveConfig(); request->send(200); } + if (request->hasArg("autos")) { + bool value = request->getParam("autos")->value().toInt(); + jsonWriteBool(configSetupJson, "autos", value); + saveConfig(); + request->send(200); + } if (request->hasArg("chatId")) { jsonWriteStr(configSetupJson, "chatId", request->getParam("chatId")->value()); saveConfig(); diff --git a/src/items/vLogging.cpp b/src/items/vLogging.cpp index 51f8a3f7..9edf1710 100644 --- a/src/items/vLogging.cpp +++ b/src/items/vLogging.cpp @@ -62,15 +62,15 @@ void LoggingClass::execute(String keyOrValue) { } if (loggingValue != "") { - if (cnt >= _maxPoints) { //удаляем старую строку и добавляем новую + if (cnt >= _maxPoints) { //удаляем старую строку и добавляем новую String logData = readFile(filename, 20480); //10240 SerialPrint("I", "Logging", "Free heap " + String(ESP.getFreeHeap())); if (logData == "large") { SerialPrint("E", "Logging", "File is very large"); } - //for (int i = 0; i < 5; i++) { + logData = deleteBeforeDelimiter(logData, "\r\n"); - //} + if (timeNow->hasTimeSynced()) { logData += timeNow->getTimeUnix() + " " + loggingValue + "\r\n"; From 2d2148d2c4668df1fbbbdac5db43ecfaf3427ca6 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <49808844+DmitryBorisenko33@users.noreply.github.com> Date: Sun, 3 Jan 2021 19:44:09 +0100 Subject: [PATCH 94/94] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=BA=D0=B8=D1=85=20DHT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/items/vSensorDht.h | 6 +++--- src/items/vSensorDht.cpp | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/items/vSensorDht.h b/include/items/vSensorDht.h index 608db0a8..5f57f3b7 100644 --- a/include/items/vSensorDht.h +++ b/include/items/vSensorDht.h @@ -5,8 +5,6 @@ #include "Global.h" #include "GyverFilters.h" -extern DHTesp* dht; - class SensorDht; typedef std::vector MySensorDhtVector; @@ -24,13 +22,15 @@ class SensorDht { SensorDht(const paramsDht& paramsTmp, const paramsDht& paramsHum); ~SensorDht(); + DHTesp* dht; + void loop(); void readTmpHum(); private: paramsDht _paramsTmp; paramsDht _paramsHum; - + unsigned long prevMillis; unsigned long difference; }; diff --git a/src/items/vSensorDht.cpp b/src/items/vSensorDht.cpp index 04dd4e80..5c79d74c 100644 --- a/src/items/vSensorDht.cpp +++ b/src/items/vSensorDht.cpp @@ -6,15 +6,11 @@ #include "Class/LineParsing.h" #include "Global.h" -DHTesp* dht = nullptr; - SensorDht::SensorDht(const paramsDht& paramsTmp, const paramsDht& paramsHum) { _paramsTmp = paramsDht(paramsTmp); _paramsHum = paramsDht(paramsHum); - if (!dht) { - dht = new DHTesp(); - } + dht = new DHTesp(); if (_paramsHum.type == "dht11") { dht->setup(_paramsHum.pin, DHTesp::DHT11); @@ -92,5 +88,7 @@ void dhtSensor() { if (firstTime) mySensorDht = new MySensorDhtVector(); firstTime = false; mySensorDht->push_back(SensorDht(paramsTmp, paramsHum)); + + enterCnt = -1; } }