diff --git a/.vscode/settings.json b/.vscode/settings.json index 89f6be4b..61cd5f78 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "vector": "cpp", "string_view": "cpp", "initializer_list": "cpp", - "ranges": "cpp" + "ranges": "cpp", + "thread": "cpp" } } \ No newline at end of file diff --git a/data_svelte/build/bundle.css.gz b/data_svelte/build/bundle.css.gz index c361c2f9..bf923a99 100644 Binary files a/data_svelte/build/bundle.css.gz and b/data_svelte/build/bundle.css.gz differ diff --git a/data_svelte/build/bundle.js.gz b/data_svelte/build/bundle.js.gz index 06ffd07e..52ed0f77 100644 Binary files a/data_svelte/build/bundle.js.gz and b/data_svelte/build/bundle.js.gz differ diff --git a/data_svelte/index.html b/data_svelte/index.html index b036526e..f9d231aa 100644 --- a/data_svelte/index.html +++ b/data_svelte/index.html @@ -4,12 +4,12 @@ - IoT Manager 4.3.4 + IoT Manager 4.3.5 - + - + diff --git a/data_svelte/items.json b/data_svelte/items.json index 84e13441..dfc47a9c 100644 --- a/data_svelte/items.json +++ b/data_svelte/items.json @@ -7,7 +7,7 @@ "header": "Виртуальные элементы" }, { - "name": "1. Логирование в график", + "name": "1. График", "type": "Writing", "subtype": "Loging", "id": "log", @@ -20,7 +20,20 @@ "points": 300 }, { - "name": "2. Таймер", + "name": "2. График дневного расхода", + "type": "Writing", + "subtype": "LogingDaily", + "id": "log", + "widget": "chart3", + "page": "Графики", + "descr": "Температура", + "num": 2, + "int": 1, + "logid": "t", + "points": 365 + }, + { + "name": "3. Таймер", "type": "Writing", "subtype": "Timer", "id": "timer", @@ -32,10 +45,10 @@ "ticker": 1, "repeat": 1, "needSave": 0, - "num": 2 + "num": 3 }, { - "name": "3. Окно ввода числа (переменная)", + "name": "4. Окно ввода числа (переменная)", "type": "Reading", "subtype": "Variable", "id": "value", @@ -44,10 +57,10 @@ "descr": "Введите число", "int": "0", "val": "0.0", - "num": 3 + "num": 4 }, { - "name": "4. Окно ввода времени", + "name": "5. Окно ввода времени", "type": "Reading", "subtype": "Variable", "id": "time", @@ -56,10 +69,10 @@ "descr": "Введите время", "int": "0", "val": "02:00", - "num": 4 + "num": 5 }, { - "name": "5. Окно ввода даты", + "name": "6. Окно ввода даты", "type": "Reading", "subtype": "Variable", "id": "time", @@ -68,10 +81,10 @@ "descr": "Введите дату", "int": "0", "val": "24.05.2022", - "num": 5 + "num": 6 }, { - "name": "6. Окно ввода текста", + "name": "7. Окно ввода текста", "type": "Reading", "subtype": "Variable", "id": "txt", @@ -80,10 +93,10 @@ "descr": "Введите текст", "int": "0", "val": "текст", - "num": 6 + "num": 7 }, { - "name": "7. Виртуальная кнопка", + "name": "8. Виртуальная кнопка", "type": "Reading", "subtype": "VButton", "id": "vbtn", @@ -92,13 +105,26 @@ "descr": "Кнопка", "int": "0", "val": "0", - "num": 7 + "num": 8 }, { "header": "Сенсоры" }, { - "name": "8. AHT20 Температура", + "name": "9. Acs712 Ток", + "type": "Reading", + "subtype": "Acs712", + "id": "amp", + "widget": "anydataAmp", + "page": "Сенсоры", + "descr": "Ток", + "round": 3, + "pin": 39, + "int": 5, + "num": 9 + }, + { + "name": "10. AHT20 Температура", "type": "Reading", "subtype": "Aht20t", "id": "Temp20", @@ -108,10 +134,10 @@ "int": 15, "addr": "0x38", "round": 1, - "num": 8 + "num": 10 }, { - "name": "9. AHT20 Влажность", + "name": "11. AHT20 Влажность", "type": "Reading", "subtype": "Aht20h", "id": "Hum20", @@ -121,10 +147,10 @@ "int": 15, "addr": "0x38", "round": 1, - "num": 9 + "num": 11 }, { - "name": "10. Аналоговый сенсор", + "name": "12. Аналоговый сенсор", "type": "Reading", "subtype": "AnalogAdc", "id": "t", @@ -138,10 +164,10 @@ "pin": 0, "int": 15, "avgSteps": 1, - "num": 10 + "num": 12 }, { - "name": "11. BME280 Температура", + "name": "13. BME280 Температура", "type": "Reading", "subtype": "Bme280t", "id": "tmp3", @@ -151,10 +177,10 @@ "int": 15, "addr": "0x77", "round": 1, - "num": 11 + "num": 13 }, { - "name": "12. BME280 Давление", + "name": "14. BME280 Давление", "type": "Reading", "subtype": "Bme280p", "id": "Press3", @@ -164,10 +190,10 @@ "int": 15, "addr": "0x77", "round": 1, - "num": 12 + "num": 14 }, { - "name": "13. BME280 Влажность", + "name": "15. BME280 Влажность", "type": "Reading", "subtype": "Bme280h", "id": "Hum3", @@ -177,10 +203,10 @@ "int": 15, "addr": "0x77", "round": 1, - "num": 13 + "num": 15 }, { - "name": "14. BMP280 Температура", + "name": "16. BMP280 Температура", "type": "Reading", "subtype": "Bmp280t", "id": "tmp3", @@ -190,10 +216,10 @@ "int": 15, "addr": "0x77", "round": 1, - "num": 14 + "num": 16 }, { - "name": "15. BMP280 Давление", + "name": "17. BMP280 Давление", "type": "Reading", "subtype": "Bmp280p", "id": "Press3", @@ -203,10 +229,10 @@ "int": 15, "addr": "0x77", "round": 1, - "num": 15 + "num": 17 }, { - "name": "16. DHT11 Температура", + "name": "18. DHT11 Температура", "type": "Reading", "subtype": "Dht1122t", "id": "tmp3", @@ -216,10 +242,10 @@ "int": 15, "pin": 0, "senstype": "dht11", - "num": 16 + "num": 18 }, { - "name": "17. DHT11 Влажность", + "name": "19. DHT11 Влажность", "type": "Reading", "subtype": "Dht1122h", "id": "Hum3", @@ -229,10 +255,10 @@ "int": 15, "pin": 0, "senstype": "dht11", - "num": 17 + "num": 19 }, { - "name": "18. DS18B20 Температура", + "name": "20. DS18B20 Температура", "type": "Reading", "subtype": "Ds18b20", "id": "dstmp", @@ -244,10 +270,10 @@ "index": 0, "addr": "", "round": 1, - "num": 18 + "num": 20 }, { - "name": "19. GY21 Температура", + "name": "21. GY21 Температура", "type": "Reading", "subtype": "GY21t", "id": "tmp4", @@ -256,10 +282,10 @@ "descr": "Температура", "round": 1, "int": 15, - "num": 19 + "num": 21 }, { - "name": "20. GY21 Влажность", + "name": "22. GY21 Влажность", "type": "Reading", "subtype": "GY21h", "id": "Hum4", @@ -268,10 +294,10 @@ "descr": "Влажность", "round": 1, "int": 15, - "num": 20 + "num": 22 }, { - "name": "21. HDC1080 Температура", + "name": "23. HDC1080 Температура", "type": "Reading", "subtype": "Hdc1080t", "id": "Temp1080", @@ -281,10 +307,10 @@ "int": 15, "addr": "0x40", "round": 1, - "num": 21 + "num": 23 }, { - "name": "22. HDC1080 Влажность", + "name": "24. HDC1080 Влажность", "type": "Reading", "subtype": "Hdc1080h", "id": "Hum1080", @@ -294,10 +320,10 @@ "int": 15, "addr": "0x40", "round": 1, - "num": 22 + "num": 24 }, { - "name": "23. MAX6675 Температура", + "name": "25. MAX6675 Температура", "type": "Reading", "subtype": "Max6675t", "id": "maxtmp", @@ -308,71 +334,71 @@ "DO": 12, "CS": 13, "CLK": 14, - "num": 23 + "num": 25 }, { - "name": "24. PZEM 004t Напряжение", + "name": "26. PZEM 004t Напряжение", "type": "Reading", "subtype": "Pzem004v", - "id": "V", + "id": "v", "widget": "anydataVlt", "page": "Сенсоры", "descr": "Напряжение", "int": 15, "addr": "0xF8", - "num": 24 + "num": 26 }, { - "name": "25. PZEM 004t Сила тока", + "name": "27. PZEM 004t Сила тока", "type": "Reading", "subtype": "Pzem004a", - "id": "A", + "id": "a", "widget": "anydataAmp", "page": "Сенсоры", "descr": "Сила тока", "int": 15, "addr": "0xF8", - "num": 25 + "num": 27 }, { - "name": "26. PZEM 004t Мощность", + "name": "28. PZEM 004t Мощность", "type": "Reading", "subtype": "Pzem004w", - "id": "W", + "id": "w", "widget": "anydataWt", "page": "Сенсоры", "descr": "Мощность", "int": 15, "addr": "0xF8", - "num": 26 + "num": 28 }, { - "name": "27. PZEM 004t Энергия", + "name": "29. PZEM 004t Энергия", "type": "Reading", "subtype": "Pzem004wh", - "id": "Wh", + "id": "wh", "widget": "anydataWth", "page": "Сенсоры", "descr": "Энергия", "int": 15, "addr": "0xF8", - "num": 27 + "num": 29 }, { - "name": "28. PZEM 004t Частота", + "name": "30. PZEM 004t Частота", "type": "Reading", "subtype": "Pzem004hz", - "id": "Hz", + "id": "hz", "widget": "anydataHtz", "page": "Сенсоры", - "descr": "Энергия", + "descr": "Частота", "int": 15, - "addr": "0x77", - "num": 28 + "addr": "0xF8", + "num": 30 }, { - "name": "29. Сканер кнопок 433 MHz", - "num": 29, + "name": "31. Сканер кнопок 433 MHz", + "num": 31, "type": "Reading", "subtype": "RCswitch", "id": "rsw", @@ -381,7 +407,7 @@ "pinTx": 12 }, { - "name": "30. Sht20 Температура", + "name": "32. Sht20 Температура", "type": "Reading", "subtype": "Sht20t", "id": "tmp2", @@ -390,10 +416,10 @@ "descr": "Температура", "int": 15, "round": 1, - "num": 30 + "num": 32 }, { - "name": "31. Sht20 Влажность", + "name": "33. Sht20 Влажность", "type": "Reading", "subtype": "Sht20h", "id": "Hum2", @@ -402,10 +428,10 @@ "descr": "Влажность", "int": 15, "round": 1, - "num": 31 + "num": 33 }, { - "name": "32. Sht30 Температура", + "name": "34. Sht30 Температура", "type": "Reading", "subtype": "Sht30t", "id": "tmp30", @@ -414,10 +440,10 @@ "descr": "SHT30 Температура", "int": 15, "round": 1, - "num": 32 + "num": 34 }, { - "name": "33. Sht30 Влажность", + "name": "35. Sht30 Влажность", "type": "Reading", "subtype": "Sht30h", "id": "Hum30", @@ -426,11 +452,11 @@ "descr": "SHT30 Влажность", "int": 15, "round": 1, - "num": 33 + "num": 35 }, { - "name": "34. HC-SR04 Ультразвуковой дальномер", - "num": 34, + "name": "36. HC-SR04 Ультразвуковой дальномер", + "num": 36, "type": "Reading", "subtype": "Sonar", "id": "sonar", @@ -442,7 +468,7 @@ "int": 5 }, { - "name": "35. UART", + "name": "37. UART", "type": "Reading", "subtype": "UART", "page": "", @@ -452,13 +478,13 @@ "tx": 12, "rx": 13, "speed": 9600, - "num": 35 + "num": 37 }, { "header": "Исполнительные устройства" }, { - "name": "36. Кнопка подключенная к пину", + "name": "38. Кнопка подключенная к пину", "type": "Writing", "subtype": "ButtonIn", "id": "btn", @@ -471,10 +497,10 @@ "pinMode": "INPUT", "debounceDelay": 50, "fixState": 0, - "num": 36 + "num": 38 }, { - "name": "37. Управление пином", + "name": "39. Управление пином", "type": "Writing", "subtype": "ButtonOut", "id": "btn", @@ -484,10 +510,10 @@ "int": 0, "inv": 0, "pin": 2, - "num": 37 + "num": 39 }, { - "name": "38. Сервопривод", + "name": "40. Сервопривод", "type": "Writing", "subtype": "IoTServo", "id": "servo", @@ -498,10 +524,10 @@ "pin": 12, "apin": -1, "amap": "0, 4096, 0, 180", - "num": 38 + "num": 40 }, { - "name": "39. Расширитель портов Mcp23017", + "name": "41. Расширитель портов Mcp23017", "type": "Reading", "subtype": "Mcp23017", "id": "Mcp", @@ -511,10 +537,10 @@ "int": "0", "addr": "0x20", "index": 1, - "num": 39 + "num": 41 }, { - "name": "40. MP3 плеер", + "name": "42. MP3 плеер", "type": "Reading", "subtype": "Mp3", "id": "mp3", @@ -524,10 +550,10 @@ "int": 1, "pins": "14,12", "volume": 20, - "num": 40 + "num": 42 }, { - "name": "41. PWM ESP8266", + "name": "43. PWM ESP8266", "type": "Writing", "subtype": "Pwm8266", "id": "pwm", @@ -539,10 +565,10 @@ "freq": 5000, "val": 0, "apin": -1, - "num": 41 + "num": 43 }, { - "name": "42. Телеграм-Лайт", + "name": "44. Телеграм-Лайт", "type": "Writing", "subtype": "TelegramLT", "id": "tg", @@ -551,13 +577,13 @@ "descr": "", "token": "", "chatID": "", - "num": 42 + "num": 44 }, { "header": "Экраны" }, { - "name": "43. LCD экран 2004", + "name": "45. LCD экран 2004", "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", @@ -569,10 +595,10 @@ "size": "20,4", "coord": "0,0", "id2show": "id датчика", - "num": 43 + "num": 45 }, { - "name": "44. LCD экран 1602", + "name": "46. LCD экран 1602", "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", @@ -584,6 +610,6 @@ "size": "16,2", "coord": "0,0", "id2show": "id датчика", - "num": 44 + "num": 46 } ] \ No newline at end of file diff --git a/data_svelte/widgets.json b/data_svelte/widgets.json index 9480d4a2..cab0dbe2 100644 --- a/data_svelte/widgets.json +++ b/data_svelte/widgets.json @@ -47,7 +47,7 @@ "name": "anydataWth", "label": "Энергия", "widget": "anydata", - "after": "Wt/hr", + "after": "kWt/Hr", "icon": "speedometer" }, { @@ -103,34 +103,27 @@ }, { "name": "chart1", - "label": "График1", + "label": "График без точек", "widget": "chart", "dateFormat": "HH:mm", - "maxCount": 255, + "maxCount": 86400, "pointRadius": 0 }, { "name": "chart2", - "label": "График2", + "label": "График с точками", "widget": "chart", - "maxCount": 255, + "maxCount": 86400, "dateFormat": "HH:mm" }, { "name": "chart3", - "label": "График3", + "label": "График Дневной", "widget": "chart", "dateFormat": "DD.MM.YYYY", - "maxCount": 255, + "maxCount": 86400, "type": "bar" }, - { - "name": "chart4", - "label": "График4", - "widget": "chart", - "maxCount": 255, - "dateFormat": "DD.MM.YYYY" - }, { "name": "fillgauge", "label": "Бочка", @@ -210,10 +203,10 @@ "status": 0 }, { - "name": "anydataVlt", - "label": "Вольты", + "name": "anydataPpm", + "label": "PPM", "widget": "anydata", - "after": "Vlt", + "after": "ppm", "icon": "speedometer" }, { diff --git a/include/Const.h b/include/Const.h index a164f11a..6e328d7d 100644 --- a/include/Const.h +++ b/include/Const.h @@ -1,7 +1,7 @@ #pragma once //Версия прошивки -#define FIRMWARE_VERSION 426 +#define FIRMWARE_VERSION 427 #ifdef esp8266_4mb #define FIRMWARE_NAME "esp8266_4mb" diff --git a/include/StandWebServer.h b/include/StandWebServer.h index 29ce3b26..67281907 100644 --- a/include/StandWebServer.h +++ b/include/StandWebServer.h @@ -10,5 +10,6 @@ extern void handleFileUpload(); extern void handleFileDelete(); extern void handleFileCreate(); extern void handleFileList(); +void printDirectory(File dir, String& out); //#endif #endif diff --git a/lib/PZEMSensor/include/PZEMSensor.h b/lib/PZEMSensor/include/PZEMSensor.h index e29362cc..25ddf175 100644 --- a/lib/PZEMSensor/include/PZEMSensor.h +++ b/lib/PZEMSensor/include/PZEMSensor.h @@ -1,7 +1,6 @@ #pragma once #include - #include #define PZEM_DEFAULT_ADDR 0xF8 @@ -22,13 +21,13 @@ class PZEMSensor { PZEMSensor(Stream *serial, uint16_t addr = PZEM_DEFAULT_ADDR); ~PZEMSensor(); - PZEM_Info* values(); + PZEM_Info *values(bool &online); bool setAddress(uint8_t addr); uint8_t getAddress(); bool setPowerAlarm(uint16_t watts); bool getPowerAlarm(); bool reset(); - void search(); + bool search(); // Get most up to date values from device registers and cache them bool refresh(); diff --git a/lib/PZEMSensor/src/PZEMSensor.cpp b/lib/PZEMSensor/src/PZEMSensor.cpp index cb9d297d..c8924410 100644 --- a/lib/PZEMSensor/src/PZEMSensor.cpp +++ b/lib/PZEMSensor/src/PZEMSensor.cpp @@ -43,10 +43,13 @@ PZEMSensor::PZEMSensor(Stream *port, uint16_t addr) { init(); } -PZEM_Info *PZEMSensor::values() { +PZEM_Info *PZEMSensor::values(bool &online) { // Update vales if necessary if (!refresh()) { _values = PZEM_Info(); + online = false; + } else { + online = true; } return &_values; } @@ -62,7 +65,7 @@ PZEM_Info *PZEMSensor::values() { * @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) @@ -295,7 +298,8 @@ uint16_t PZEMSensor::CRC16(const uint8_t *data, uint16_t len) { return crc; } -void PZEMSensor::search() { +bool PZEMSensor::search() { + bool ret = false; static uint8_t response[7]; for (uint16_t addr = 0x01; addr <= 0xF8; addr++) { sendCmd8(CMD_RIR, 0x00, 0x01, false, addr); @@ -303,8 +307,9 @@ void PZEMSensor::search() { // Something went wrong continue; } else { - Serial.print("Device on addr: "); - Serial.print(addr); + Serial.println("Pzem " + String(addr)); + ret = true; } } + return ret; } diff --git a/myProfile.json b/myProfile.json index c59a5521..5a5f56cd 100644 --- a/myProfile.json +++ b/myProfile.json @@ -28,7 +28,11 @@ "modules": { "Виртуальные элементы": [ { - "path": "src\\modules\\virtual\\Logging", + "path": "src\\modules\\virtual\\Loging", + "active": true + }, + { + "path": "src\\modules\\virtual\\LogingDaily", "active": true }, { @@ -45,6 +49,10 @@ } ], "Сенсоры": [ + { + "path": "src\\modules\\sensors\\Acs712", + "active": true + }, { "path": "src\\modules\\sensors\\Ads1115", "active": false diff --git a/platformio.ini b/platformio.ini index b565b69f..49db09ca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -61,6 +61,7 @@ lib_deps = https://github.com/JonasGMorsch/GY-21.git ClosedCube HDC1080 adafruit/MAX6675 library + mandulaj/PZEM-004T-v30 rc-switch @ ^2.6.4 robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 @@ -70,10 +71,12 @@ lib_deps = dfrobot/DFRobotDFPlayerMini @ ^1.0.5 marcoschwartz/LiquidCrystal_I2C@^1.1.4 build_src_filter = - + + + + + + + + + + + + + diff --git a/src/Main.cpp b/src/Main.cpp index 9fd43e63..6a24c9a1 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -80,6 +80,12 @@ void setup() { // test Serial.println("-------test start--------"); + + // File dir = FileFS.open("/", "r"); + // String out; + // printDirectory(dir, out); + // Serial.println(out); + //=======проверка очереди из структур================= // myDB = new IoTDB; diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index 52902620..8d9bfa88 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -134,9 +134,10 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { publishWidgets(); publishState(); + //обращение к логированию из ядра //отправка данных графиков for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { - if ((*it)->getSubtype() == "Loging") { + if ((*it)->getSubtype() == "Loging" || "LogingDaily") { (*it)->setPublishDestination(TO_MQTT); (*it)->publishValue(); } diff --git a/src/StandWebServer.cpp b/src/StandWebServer.cpp index 83139b5b..6a846158 100644 --- a/src/StandWebServer.cpp +++ b/src/StandWebServer.cpp @@ -11,42 +11,36 @@ void standWebServerInit() { HTTP.serveStatic("/bundle.css.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год HTTP.serveStatic("/favicon.png", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.on("/devicelist.json", HTTP_GET, []() { - HTTP.send(200, "application/json", devListHeapJson); - }); - - HTTP.on("/settings.h.json", HTTP_GET, []() { - HTTP.send(200, "application/json", settingsFlashJson); - }); - - HTTP.on("/settings.f.json", HTTP_GET, []() { - HTTP.send(200, "application/json", readFile(F("settings.json"), 20000)); - }); - - HTTP.on("/params.json", HTTP_GET, []() { - String json = getParamsJson(); - HTTP.send(200, "application/json", json); - }); - - HTTP.on("/errors.json", HTTP_GET, []() { - HTTP.send(200, "application/json", errorsHeapJson); - }); - - HTTP.on("/config.json", HTTP_GET, []() { - HTTP.send(200, "application/json", readFile(F("config.json"), 20000)); - }); - - HTTP.on("/layout.json", HTTP_GET, []() { - HTTP.send(200, "application/json", readFile(F("layout.json"), 20000)); - }); - - HTTP.on("/restart", HTTP_GET, []() { - // ESP.restart(); - HTTP.send(200, "text/plain", "ok"); - }); + // HTTP.on("/devicelist.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", devListHeapJson); + // }); + // HTTP.on("/settings.h.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", settingsFlashJson); + // }); + // HTTP.on("/settings.f.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", readFile(F("settings.json"), 20000)); + // }); + // HTTP.on("/params.json", HTTP_GET, []() { + // String json = getParamsJson(); + // HTTP.send(200, "application/json", json); + // }); + // HTTP.on("/errors.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", errorsHeapJson); + // }); + // HTTP.on("/config.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", readFile(F("config.json"), 20000)); + // }); + // HTTP.on("/layout.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", readFile(F("layout.json"), 20000)); + // }); + // HTTP.on("/restart", HTTP_GET, []() { + // // ESP.restart(); + // HTTP.send(200, "text/plain", "ok"); + // }); // Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше // httpUpdater.setup(&HTTP); + // Запускаем HTTP сервер HTTP.begin(); @@ -95,7 +89,6 @@ void standWebServerInit() { HTTP.send(200, "text/plain", ""); }, handleFileUpload); - //#endif // called when the url is not defined here HTTP.onNotFound([]() { @@ -155,7 +148,6 @@ String getContentType(String filename) { return "text/plain"; } -//#ifdef REST_FILE_OPERATIONS // Здесь функции для работы с файловой системой void handleFileUpload() { if (HTTP.uri() != "/edit") return; @@ -205,10 +197,6 @@ void handleFileCreate() { } void handleFileList() { - if (!HTTP.hasArg("list")) { - HTTP.send(500, "text/plain", "BAD ARGS"); - return; - } File dir = FileFS.open("/", "r"); String output = "["; File entry; @@ -226,5 +214,22 @@ void handleFileList() { Serial.println(output); HTTP.send(200, "text/json", output); } -//#endif + +void printDirectory(File dir, String& out) { + while (true) { + File entry = dir.openNextFile(); + if (!entry) { + break; + } + if (entry.isDirectory()) { + out += entry.name(); + out += "/"; + printDirectory(entry, out); + } else { + out += entry.name(); + out += "\r\n"; + } + } +} + #endif diff --git a/src/WsServer.cpp b/src/WsServer.cpp index 256f0a8b..80f09870 100644 --- a/src/WsServer.cpp +++ b/src/WsServer.cpp @@ -73,13 +73,14 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) //отвечаем на запрос графиков if (headerStr == "/charts|") { + //обращение к логированию из ядра //отправка данных графиков только в выбранный сокет for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { //сбрасываем даты графиков // if ((*it)->getID().endsWith("-date")) { // (*it)->setTodayDate(); //} - if ((*it)->getSubtype() == "Loging") { + if ((*it)->getSubtype() == "Loging" || "LogingDaily") { (*it)->setPublishDestination(TO_WS, num); (*it)->publishValue(); } diff --git a/src/classes/IoTItem.cpp b/src/classes/IoTItem.cpp index 7287dc1e..34355e05 100644 --- a/src/classes/IoTItem.cpp +++ b/src/classes/IoTItem.cpp @@ -96,7 +96,7 @@ void IoTItem::regEvent(String value, String consoleInfo = "") { publishStatusMqtt(_id, value); publishStatusWs(_id, value); - SerialPrint("i", "Sensor " + consoleInfo, "'" + _id + "' data: " + value + "'"); + SerialPrint("i", "Sensor", consoleInfo + " '" + _id + "' data: " + value + "'"); // проверка если global установлен то шлем всем о событии // if (_global) { @@ -150,8 +150,6 @@ bool IoTItem::isGpioDriver() { return false; } - - //сетевое общение==================================================================================================================================== externalVariable::externalVariable(String parameters) : IoTItem(parameters) { diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 74aa4ea8..aa0e9fdb 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -205,11 +205,11 @@ class BinaryExprAST : public ExprAST { if (!lhs->isDecimal || !rhs->isDecimal) { if (lhs->isDecimal) - lhsStr = lhs->valD; + lhsStr = (String)lhs->valD; else lhsStr = lhs->valS; if (rhs->isDecimal) - rhsStr = rhs->valD; + rhsStr = (String)rhs->valD; else rhsStr = rhs->valS; switch (Op) { diff --git a/src/modules/API.cpp b/src/modules/API.cpp index b141dc71..467f8588 100644 --- a/src/modules/API.cpp +++ b/src/modules/API.cpp @@ -1,9 +1,11 @@ #include "ESPConfiguration.h" void* getAPI_Loging(String subtype, String params); +void* getAPI_LogingDaily(String subtype, String params); void* getAPI_Timer(String subtype, String params); void* getAPI_Variable(String subtype, String params); void* getAPI_VButton(String subtype, String params); +void* getAPI_Acs712(String subtype, String params); void* getAPI_Aht20(String subtype, String params); void* getAPI_AnalogAdc(String subtype, String params); void* getAPI_Bme280(String subtype, String params); @@ -31,9 +33,11 @@ void* getAPI_Lcd2004(String subtype, String params); void* getAPI(String subtype, String params) { void* tmpAPI; if ((tmpAPI = getAPI_Loging(subtype, params)) != nullptr) return tmpAPI; +if ((tmpAPI = getAPI_LogingDaily(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Timer(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Variable(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_VButton(subtype, params)) != nullptr) return tmpAPI; +if ((tmpAPI = getAPI_Acs712(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Aht20(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_AnalogAdc(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Bme280(subtype, params)) != nullptr) return tmpAPI; diff --git a/src/modules/sensors/Acs712/Acs712.cpp b/src/modules/sensors/Acs712/Acs712.cpp new file mode 100644 index 00000000..79d9bc76 --- /dev/null +++ b/src/modules/sensors/Acs712/Acs712.cpp @@ -0,0 +1,76 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +extern IoTGpio IoTgpio; + +class Acs712 : public IoTItem +{ +private: + unsigned int _pin; + const unsigned long _sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains + const unsigned long _numSamples = 250UL; // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up + const unsigned long _sampleInterval = _sampleTime / _numSamples; // the sampling interval, must be longer than then ADC conversion time + int _adc_zero1; //Переменная автоматической калибровки + +public: + + Acs712(String parameters) : IoTItem(parameters) + { + String tmp; + jsonRead(parameters, "pin", tmp); + _pin = tmp.toInt(); + _adc_zero1 = determineVQ(_pin); + } + + void doByInterval() + { + + unsigned long currentAcc = 0; + unsigned int count = 0; + unsigned long prevMicros = micros() - _sampleInterval; + while (count < _numSamples) + { + if (micros() - prevMicros >= _sampleInterval) + { + int adc_raw = IoTgpio.analogRead(_pin) - _adc_zero1; + currentAcc += (unsigned long)(adc_raw * adc_raw); + ++count; + prevMicros += _sampleInterval; + } + } + #ifdef ESP32 + value.valD = int(sqrt((float)currentAcc / (float)_numSamples) * (75.7576 / 4095.0)); + #else + value.valD = int(sqrt((float)currentAcc / (float)_numSamples) * (75.7576 / 1023.0)); + #endif + regEvent(value.valD, "Acs712"); + } + + int determineVQ(int PIN) + { + long VQ = 0; + // read 5000 samples to stabilise value + for (int i = 0; i < 5000; i++) + { + VQ += IoTgpio.analogRead(PIN); + //delay(1); // depends on sampling (on filter capacitor), can be 1/80000 (80kHz) max. + } + VQ /= 5000; + return int(VQ); + } + + + ~Acs712(){}; +}; + +void *getAPI_Acs712(String subtype, String param) +{ + if (subtype == F("Acs712")) + { + return new Acs712(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/Acs712/modinfo.json b/src/modules/sensors/Acs712/modinfo.json new file mode 100644 index 00000000..6f112490 --- /dev/null +++ b/src/modules/sensors/Acs712/modinfo.json @@ -0,0 +1,36 @@ +{ + "menuSection": "Сенсоры", + "configItem": [ + { + "name": "Acs712 Ток", + "type": "Reading", + "subtype": "Acs712", + "id": "amp", + "widget": "anydataAmp", + "page": "Сенсоры", + "descr": "Ток", + "round": 3, + "pin": 39, + "int": 5 + } + ], + "about": { + "authorName": "Yuriy Kuneev", + "authorContact": "https://t.me/Kuneev07", + "authorGit": "", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "Acs712", + "moduleVersion": "1.0", + "moduleDesc": "Позволяет получить текущее значение тока на аналоговом пине с помощью модуля Acs712.", + "propInfo": { + "pin": "Аналоговый GPIO номер, к которому подключен датчик.", + "int": "Количество секунд между опросами датчика." + } + }, + "defActive": true, + "devices": { + "esp32_4mb": [], + "esp8266_4mb": [] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Mhz19/Mhz19.cpp b/src/modules/sensors/Mhz19/Mhz19.cpp index 5a09930b..88f7bb9e 100644 --- a/src/modules/sensors/Mhz19/Mhz19.cpp +++ b/src/modules/sensors/Mhz19/Mhz19.cpp @@ -11,20 +11,30 @@ extern IoTGpio IoTgpio; -int rxPinCO2 ; // зеленый провод сенсора к прописаному по умолчанию D7 (GPIO13) -int txPinCO2 ; // синий провод сенсора к прописаному по умолчанию D6 (GPIO12) -SoftwareSerial* swSerialCO2 = nullptr; +// SoftwareSerial* swSerialCO2 = nullptr; +int rxPinCO2; // зеленый провод сенсора к прописаному по умолчанию D7 (GPIO13) +int txPinCO2; // синий провод сенсора к прописаному по умолчанию D6 (GPIO12) +#ifdef ESP8266 + +SoftwareSerial *swSerialCO2 = nullptr; +#endif +#ifdef ESP32 +#include +HardwareSerial swSerialCO2(1); +#endif + int MHZ19_request(int request); void MHZ19uart_init(); -bool MHZ19uart_flag = true; -int MHZ19C_PREHEATING_TIME = 2 * 30 * 1000; // покажет реальные данные после прогрева, через 2 мин. +bool MHZ19uartInit_flag = true; +bool MHZ19uartUpdateInputs_flag = true; +unsigned int preheating = 2 * 60; // покажет реальные данные после прогрева, через 2 мин. int temperature = 0; bool temperatureUpdated = false; int prevRange = 5000; -int range = 5000; // по умолнчанию стоит шкала 5000 (не 2000 как в мануале) +int range = 5000; // по умолнчанию стоит шкала 5000 (не 2000 как в мануале) bool rangeChaged = false; int prevABC = 1; int ABC = 1; @@ -32,32 +42,52 @@ bool ABCchanged = false; //Это файл сенсора, в нем осуществляется чтение сенсора. -class Mhz19uart : public IoTItem { - private: +class Mhz19uart : public IoTItem +{ +private: //======================================================================================================= // Секция переменных. - public: +public: //======================================================================================================= - Mhz19uart(String parameters) : IoTItem(parameters) { + Mhz19uart(String parameters) : IoTItem(parameters) + { rxPinCO2 = jsonReadInt(parameters, "rxPin"); txPinCO2 = jsonReadInt(parameters, "txPin"); + preheating = jsonReadInt(parameters, "warmUp"); range = jsonReadInt(parameters, "range"); ABC = jsonReadInt(parameters, "ABC"); - if (!swSerialCO2) swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); + MHZ19uartInit_flag = true; + MHZ19uartUpdateInputs_flag = true; +#ifdef ESP8266 + if (!swSerialCO2) + swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); +#endif +#ifdef ESP32 +// HardwareSerial swSerialCO2(1); +// HardwareSerial swSerialCO2(2); +// swSerialCO2.begin(9600); +#endif } //======================================================================================================= // doByInterval() - void doByInterval() { + void doByInterval() + { MHZ19uart_init(); - if (millis() > MHZ19C_PREHEATING_TIME) { - Serial.println("Start checkUARTCO2"); - value.valD = MHZ19_request(1); + + if (millis() > preheating * 1000) + { + // Serial.println("Start checkUARTCO2"); + int reply = MHZ19_request(1); + if (reply) + { + value.valD = reply; + regEvent(value.valD, "Mhz19uart"); + } } - regEvent(value.valD, "Mhz19uart"); //обязательный вызов хотяб один } //======================================================================================================= @@ -68,64 +98,111 @@ class Mhz19uart : public IoTItem { //если сенсор предполагает использование общего объекта библиотеки для нескольких экземпляров сенсора, то в данной функции необходимо предусмотреть //создание и контроль соответствующих глобальных переменных -//замер по ШИМ создает задержку. вызываем его нечасто, по умолчанию раз в 5 минут -class Mhz19pwm : public IoTItem { - private: +//замер по ШИМ создает задержку. вызываем его нечасто, по умолчанию раз в 5 минут +class Mhz19pwm : public IoTItem +{ +private: //======================================================================================================= // Секция переменных. - int pwmPin; // желтый провод сенсора к прописаному по умолчанию D8 (GPIO15) + int pwmPin = 15; // желтый провод сенсора к прописаному по умолчанию D8 (GPIO15) + int maxRetriesNotAvailable = 10; - public: +public: //======================================================================================================= - Mhz19pwm(String parameters) : IoTItem(parameters) { + Mhz19pwm(String parameters) : IoTItem(parameters) + { pwmPin = jsonReadInt(parameters, "pin"); + preheating = jsonReadInt(parameters, "warmUp"); + maxRetriesNotAvailable = jsonReadInt(parameters, "maxRetriesNotAvailable"); } //======================================================================================================= - void doByInterval() { + void doByInterval() + { MHZ19pwm_init(); - if (millis() > MHZ19C_PREHEATING_TIME) // + + if (millis() > preheating * 1000) // { - Serial.println("Start checkPWM_CO2"); - value.valD = MHZ19pwm_request(); + // Serial.println("Start checkPWM_CO2"); + int reply = MHZ19pwm_request(); + if (reply) + { + SerialPrint("E", "Sensor Mhz19pwm", "MHZ19pwm_init reply = " + String(reply)); + value.valD = reply; + regEvent(value.valD, "Mhz19pwm"); + } } - regEvent(value.valD, "Mhz19pwm"); //обязательный вызов хотяб один } //======================================================================================================= - void MHZ19pwm_init() { + void MHZ19pwm_init() + { static bool MHZ19pwm_flag = true; - if (MHZ19pwm_flag) { + if (MHZ19pwm_flag) + { pinMode(pwmPin, INPUT); MHZ19pwm_flag = false; } } - int MHZ19pwm_request() { + int MHZ19pwm_request() + { int reply; - Serial.println("Запрос замера по PWM запущен"); - unsigned long th, tl, ppm = 0, ppm2 = 0, ppm3 = 0; - do { + // Serial.println("Запрос замера по PWM запущен"); + unsigned long th = 0, tl, ppm = 0, ppm2 = 0, ppm3 = 0; + // int pwmPinReading1 = digitalRead(pwmPin); + // SerialPrint("E", "Sensor Mhz19uart", "pwmPinReading1 = " + String(pwmPinReading1)); + /* + do + { th = pulseIn(pwmPin, HIGH, 1004000) / 1000; tl = 1004 - th; - ppm2 = 2000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 2000ppm - ppm3 = 5000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 5000ppm + ppm2 = 2000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 2000ppm + ppm3 = 5000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 5000ppm } while (th == 0); + */ + for (int i = 0; i < maxRetriesNotAvailable; i++) + { + th = pulseIn(pwmPin, HIGH, 1004000) / 1000; - // Serial.print(th); - // Serial.println(" <- Milliseconds PWM is HIGH"); - - if (range == 2000) { - reply = ppm2; - Serial.print(ppm2); - Serial.println(" <- ppm2 (PWM) with 2000ppm as limit"); - } else { - reply = ppm3; - Serial.print(ppm3); - Serial.println(" <- ppm3 (PWM) with 5000ppm as limit"); + if (th != 0) + { + tl = 1004 - th; + ppm2 = 2000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 2000ppm + ppm3 = 5000 * (th - 2) / (th + tl - 4); // расчёт для диапазона от 0 до 5000ppm + if (i > 1) + SerialPrint("E", "Sensor Mhz19pwm", "Got reading from PWM pin after " + String(i) + " tries"); + break; + } + // Serial.println(i); } - Serial.println("Completed checkPwmCO2"); + if (!th) + { + SerialPrint("E", "Sensor Mhz19pwm", "No reading from PWM pin. Check wiring."); + reply = 0; + } + else + { + + // Serial.print(th); + // Serial.println(" <- Milliseconds PWM is HIGH"); + if (range == 2000) + { + reply = ppm2; + // Serial.print(ppm2); + // Serial.println(" <- ppm2 (PWM) with 2000ppm as limit"); + } + else + { + reply = ppm3; + // Serial.print(ppm3); + // Serial.println(" <- ppm3 (PWM) with 5000ppm as limit"); + } + // Serial.println("Completed checkPwmCO2"); + } + // int pwmPinReading2 = digitalRead(pwmPin); + // SerialPrint("E", "Sensor Mhz19uart", "pwmPinReading2 = " + String(pwmPinReading2)); return reply; } @@ -134,35 +211,51 @@ class Mhz19pwm : public IoTItem { //====================TEMP=================================================================================== -class Mhz19temp : public IoTItem { - private: +class Mhz19temp : public IoTItem +{ +private: //======================================================================================================= // Секция переменных. - public: +public: //====================TEMP=================================================================================== - Mhz19temp(String parameters) : IoTItem(parameters) { + Mhz19temp(String parameters) : IoTItem(parameters) + { rxPinCO2 = jsonReadInt(parameters, "rxPin"); txPinCO2 = jsonReadInt(parameters, "txPin"); - range = jsonReadInt(parameters, "range"); - ABC = jsonReadInt(parameters, "ABC"); - if (!swSerialCO2) swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); + +#ifdef ESP8266 + if (!swSerialCO2) + swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); +#endif +#ifdef ESP32 +// HardwareSerial swSerialCO2(1); +#endif } //======================================================================================================= - void doByInterval() { + void doByInterval() + { + int reply; + // Serial.println("Start Mhz19temp doByInterval"); - if (temperatureUpdated) { - value.valD = temperature; + if (temperatureUpdated) + { + reply = temperature; temperatureUpdated = false; - } else { - MHZ19uart_init(); - Serial.println("Start temperature request"); - if (MHZ19_request(13)) { - value.valD = temperature; - }; // change } - regEvent(value.valD, "Mhz19temp"); //обязательный вызов хотяб один + else + { + MHZ19uart_init(); + // Serial.println("Start temperature request"); + reply = MHZ19_request(13); + } + + if (reply) + { + value.valD = reply; + regEvent(value.valD, "Mhz19temp"); + } } //======================================================================================================= @@ -171,375 +264,404 @@ class Mhz19temp : public IoTItem { ~Mhz19temp(){}; }; -//=======================Range================ -class Mhz19range : public IoTItem { - private: - public: - Mhz19range(String parameters) : IoTItem(parameters) { - rxPinCO2 = jsonReadInt(parameters, "rxPin"); - txPinCO2 = jsonReadInt(parameters, "txPin"); - range = jsonReadInt(parameters, "range"); - ABC = jsonReadInt(parameters, "ABC"); - if (!swSerialCO2) swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); - } - - void doByInterval() { - if (range != prevRange) { - MHZ19uart_init(); - Serial.println("Start change range"); - - if (range == 2000) { - if (MHZ19_request(9)) { - prevRange = 2000; - value.valD = 2000; - } // change range to 2000 - } else { - if (MHZ19_request(10)) { - prevRange = 5000; - value.valD = 5000; - } // change range to 5000 - } - } else { - value.valD = prevRange; - } - regEvent(value.valD, "Mhz19range"); //обязательный вызов хотяб один - } - - ~Mhz19range(){}; -}; - -//===================ABC================= - -class Mhz19ABC : public IoTItem { - private: - public: - Mhz19ABC(String parameters) : IoTItem(parameters) { - rxPinCO2 = jsonReadInt(parameters, "rxPin"); - txPinCO2 = jsonReadInt(parameters, "txPin"); - range = jsonReadInt(parameters, "range"); - ABC = jsonReadInt(parameters, "ABC"); - if (!swSerialCO2) swSerialCO2 = new SoftwareSerial(rxPinCO2, txPinCO2); - } - - void doByInterval() { - if (ABC != prevABC) { - if (ABC == 1) { - if (MHZ19_request(7)) { - prevABC = 1; - value.valD = 1; - } // change ABC to 1 - } else { - if (MHZ19_request(8)) { - prevABC = 0; - value.valD = 0; - } // change ABC to 0 - } - } else { - value.valD = prevABC; - } - regEvent(value.valD, "Mhz19ABC"); //обязательный вызов хотяб один - } - - ~Mhz19ABC(){}; -}; - -///============== end of classes========================== - -void *getAPI_Mhz19(String subtype, String param) { - if (subtype == F("Mhz19uart")) { +void *getAPI_Mhz19(String subtype, String param) +{ + if (subtype == F("Mhz19uart")) + { return new Mhz19uart(param); - } else if (subtype == F("Mhz19pwm")) { + } + else if (subtype == F("Mhz19pwm")) + { return new Mhz19pwm(param); - } else if (subtype == F("Mhz19temp")) { + } + else if (subtype == F("Mhz19temp")) + { return new Mhz19temp(param); - } else if (subtype == F("Mhz19range")) { - return new Mhz19range(param); - } else if (subtype == F("Mhz19ABC")) { - return new Mhz19ABC(param); - } else { + } + else + { return nullptr; } } -void MHZ19uart_init() { - if (MHZ19uart_flag) { - int reply; +void MHZ19uart_init() +{ + if (MHZ19uartInit_flag) + { + +#ifdef ESP8266 swSerialCO2->begin(9600); - +#endif +#ifdef ESP32 + // if(swSerialCO2.available()) { Serial.println("ok"); } + swSerialCO2.begin(9600, SERIAL_8N1, rxPinCO2, txPinCO2); +#endif delay(50); + int reply; + reply = MHZ19_request(2); // show range, for test of uart only + // Serial.print("show range reply = "); + // Serial.println(reply); - reply = MHZ19_request(2); // show range, for test of uart only - Serial.print("show range reply = "); - Serial.println(reply); - - if (reply) { - MHZ19uart_flag = false; + if (reply) + { + prevRange = reply; + MHZ19uartInit_flag = false; } } - if (!MHZ19uart_flag) { - static int prevABC; + if (!MHZ19uartInit_flag && ABC != prevABC) + { int reply; - if (ABC != prevABC) { - if (ABC) { - reply = MHZ19_request(7); // ABC on - } else { - reply = MHZ19_request(8); // ABC off + if (ABC != prevABC) + { + if (ABC) + { + reply = MHZ19_request(7); // ABC on } - Serial.print("ABC change reply = "); - Serial.println(reply); + else + { + reply = MHZ19_request(8); // ABC off + } + // Serial.print("ABC change reply = "); + // Serial.println(reply); } - if (reply) { + if (reply) + { prevABC = ABC; } } - static bool MHZ19_range_flag = true; - if (MHZ19_range_flag && !MHZ19uart_flag && (millis() > 30 * 1000)) { + if (range != prevRange && !MHZ19uartInit_flag) + { int reply; - if (range == 2000) { - reply = MHZ19_request(9); // Установка шкалы 0-2000 - // 255 155 0 0 7 208 0 3 139 - } else { - reply = MHZ19_request(10); // Установка шкалы 0-5000 - // 255 155 0 0 19 136 0 3 199 + if (range == 2000) + { + reply = MHZ19_request(9); // Установка шкалы 0-2000 - 255 155 0 0 7 208 0 3 139 } - Serial.print("Scale change reply = "); - Serial.println(reply); - if (reply) { - reply = MHZ19_request(2); // show range - Serial.print("show range reply = "); - Serial.println(reply); - - MHZ19_range_flag = false; + else + { + reply = MHZ19_request(10); // Установка шкалы 0-5000 - 255 155 0 0 19 136 0 3 199 + } + // Serial.print("Scale change reply = "); + // Serial.println(reply); + if (reply) + { + // reply = MHZ19_request(2); // show range + // Serial.print("show range reply = "); + // Serial.println(reply); + prevRange = range; + // MHZ19_range_flag = false; } } } -int MHZ19_request(int request) { +int MHZ19_request(int request) +{ int reply; - Serial.print("prevRange = "); - Serial.println(prevRange); + // Serial.print("prevRange = "); + // Serial.println(prevRange); - byte uartReqSamplePpm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // PPM + byte uartReqSamplePpm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // PPM // 255 1 134 0 0 0 0 0 121 // Response 255 134 1 148 67 0 0 0 162 - byte uartReqSampleABCon[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; // ABC logic on - // 255 1 121 160 0 0 0 0 230 - // Response 255 121 1 0 0 0 0 0 134 - byte uartReqSampleABCoff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; // ABC logic off - byte uartReqSampleABCstatus[9] = {0xFF, 0x01, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82}; // ABC logic status - + byte uartReqSampleABCon[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; // ABC logic on + // 255 1 121 160 0 0 0 0 230 + // Response 255 121 1 0 0 0 0 0 134 + byte uartReqSampleABCoff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; // ABC logic off + byte uartReqSampleABCstatus[9] = {0xFF, 0x01, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82}; // ABC logic status byte uartReqSample1000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x03, 0xE8, 0x7B}; - byte uartReqSample2000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F}; // задаёт диапазон 0 - 2000ppm - byte uartReqSample3000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x0B, 0xB8, 0xA3}; // задаёт диапазон 0 - 2000ppm - - byte uartReqSample5000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB}; // задаёт диапазон 0 - 5000ppm - // 255 1 153 0 0 0 19 136 203 - // Response 255 153 1 0 0 0 0 0 102 - byte uartReqSampleRequestRange[9] = {0xFF, 0x01, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64}; // запрос диапазона - // request // 255 1 155 0 0 0 0 0 100 - // reply // 255 1 155 0 0 0 0 0 100 - byte uartReqSampleZeroPnt[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}; // !!ZERO POINT CALIBRATION - byte uartReqSampleReset[9] = {0xFF, 0x01, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72}; // reset - - byte uartReqSampleReset1[9] = {0xFF, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87}; // reset - did not work out + byte uartReqSample2000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x8F}; // задаёт диапазон 0 - 2000ppm + byte uartReqSample3000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x0B, 0xB8, 0xA3}; // задаёт диапазон 0 - 2000ppm + byte uartReqSample5000Range[9] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88, 0xCB}; // задаёт диапазон 0 - 5000ppm + // 255 1 153 0 0 0 19 136 203 + // Response 255 153 1 0 0 0 0 0 102 + byte uartReqSampleRequestRange[9] = {0xFF, 0x01, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64}; // запрос диапазона + // request // 255 1 155 0 0 0 0 0 100 + // reply // 255 1 155 0 0 0 0 0 100 + byte uartReqSampleZeroPnt[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78}; // !!ZERO POINT CALIBRATION + byte uartReqSampleReset[9] = {0xFF, 0x01, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72}; // reset + byte uartReqSampleReset1[9] = {0xFF, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87}; // reset - did not work out byte request_cmd[9]; // byte c; - switch (request) { - // случаи 3-7 для перезапуска сенсора - case 1: { - Serial.println("Запрос No.1 - отправлен. Запрос замера по UART"); - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSamplePpm[i]; - } - } break; + switch (request) + { + // случаи 3-7 для перезапуска сенсора + case 1: + { + // Serial.println("Запрос No.1 - отправлен. Запрос замера по UART"); + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSamplePpm[i]; + } + } + break; - case 2: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSampleRequestRange[i]; - } - Serial.println("Запрос No.2 - отправлен. Запрос шкалы"); - } break; + case 2: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSampleRequestRange[i]; + } + // Serial.println("Запрос No.2 - отправлен. Запрос шкалы"); + } + break; + + case 3: + { + // код для запуска сенсора с работой по UART (запуск таймера) + // Serial.println("Запрос No.3 - отправлен. Сенсор по UART запущен"); + } + break; + case 4: + { + // код для остановке сенсора с работой по UART (остановка таймера) + // Serial.println("Запрос No.4 - отправлен. Сенсор по UART остановлен"); + } + break; + case 5: + { + // код для запуска сенсора с работой по PWM (запуск таймера) + // Serial.println("Запрос No.5 - отправлен. Сенсор по PWM запущен"); + } + break; + case 6: + { + // код для остановки сенсора с работой по PWM (остановка таймера) + // Serial.println("Запрос No.6 - отправлен. Сенсор по PWM остановлен"); + } + break; + case 7: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSampleABCon[i]; + } + // Serial.println("Запрос No.7 - отправлен. Включаем функцию атокалибровки"); + } + break; + case 8: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSampleABCoff[i]; + } + // Serial.println("Запрос No.8 - отправлен. Выключаем функцию атокалибровки"); + } + break; + case 9: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSample2000Range[i]; + } + // Serial.println("Запрос No.9 - отправлен. Установливаем шкалу 0-2000"); + } + break; + case 10: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSample5000Range[i]; + } + // Serial.println("Запрос No.10 - отправлен. Установливаем шкалу 0-5000"); + } + break; + case 11: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSampleZeroPnt[i]; + } + // Serial.println("Запрос No.11 - отправлен. Калибровка. Установливаем нулевой уровень"); + } + break; + case 12: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSampleReset1[i]; + } + // Serial.println("Запрос No.11 - отправлен. Запрос на Сброс"); + } + break; + case 13: + { + for (int i = 0; i < 9; i++) + { + request_cmd[i] = uartReqSamplePpm[i]; + } + // Serial.println("Запрос No.13 - отправлен. Запрос по Температуре"); + } + break; + default: + // byte c = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + // if nothing else matches, do the default + // default is optional + break; + } + +#ifdef ESP8266 + swSerialCO2->write(request_cmd, 9); +#endif +#ifdef ESP32 + swSerialCO2.write(request_cmd, 9); +#endif + /* + Serial.print("Request : "); + for (int i = 0; i < 9; i++) + { + Serial.print(" "); + Serial.print(request_cmd[i]); + } + Serial.println(" "); + */ + delay(5); + unsigned char response[9]; + +#ifdef ESP8266 + swSerialCO2->readBytes(response, 9); +#endif + +#ifdef ESP32 + swSerialCO2.readBytes(response, 9); +#endif + /* + Serial.print("Response :"); + for (int i = 0; i < 9; i++) + { + Serial.print(" "); + Serial.print(response[i]); + } + Serial.println(" "); + */ + byte crc = 0; + for (int i = 1; i < 8; i++) + crc += response[i]; + crc = 255 - crc; + crc += 1; + + if (!(response[0] == 0xFF && response[8] == crc)) + { + String msg = "Range CRC error: " + String(crc) + " / " + String(response[8]) + " Check wiring"; + SerialPrint("E", "Sensor Mhz19uart", msg); + reply = 0; + MHZ19uartInit_flag = true; // + } + else + { + // Serial.println("No CRC errors"); + switch (request) + { + case 1: + { + unsigned int responseHigh = (unsigned int)response[2]; + unsigned int responseLow = (unsigned int)response[3]; + unsigned int ppm = (256 * responseHigh) + responseLow; + + // Serial.print("CO2 UART = "); + // Serial.println(ppm); + + temperature = response[4] - 44; // - 40; + // Serial.print("Temperature = "); + // Serial.println(temperature); + temperatureUpdated = true; + + reply = ppm; + } + break; + + case 2: + { + + unsigned int responseHigh = (unsigned int)response[4]; + unsigned int responseLow = (unsigned int)response[5]; + unsigned int scale = (256 * responseHigh) + responseLow; + reply = scale; + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.2 - сработал. Шкала получена - " + String(scale)); + } + break; + case 3: + { + // Serial.println("Case 3 - OK"); + reply = 1; + } + break; + case 4: + { + // Serial.println("Case 4 - OK"); + reply = 1; + } + break; + case 5: + { + // Serial.println("Case 5 - OK"); + reply = 1; + } + break; + case 6: + { + // Serial.println("Запрос No.6 - сработал."); + reply = 1; + } + break; + case 7: + { + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.7 - сработал. ABC включен"); + reply = 1; + } + break; + case 8: + { + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.8 - сработал. ABC выключен"); + reply = 1; + } + break; + case 9: + { + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.9 - сработал. Установлена шкала 0-2000"); + reply = 1; + prevRange = 2000; + } + break; + case 10: + { + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.10 - сработал. Установлена шкала 0-5000"); + reply = 1; + prevRange = 5000; + } + break; + case 11: + { + reply = 1; + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.11 - сработал. Калибровка. Установлен нулевой уровень"); + } + break; + + case 12: + { + reply = 1; + SerialPrint("i", "Sensor Mhz19uart", "Запрос No.12 - сработал. Сброс произошел"); + } + break; + + case 13: + { + temperature = response[4] - 44; // - 40; + reply = temperature; + // SerialPrint("i", "Sensor Mhz19uart", "Запрос No.13 - сработал. Температура получена - " + String(temperature)); + // Serial.print("Запрос No.13 - сработал. Температура получена - "); + // Serial.println(temperature); + } + break; - case 3: { - // код для запуска сенсора с работой по UART (запуск таймера) - Serial.println("Запрос No.3 - отправлен. Сенсор по UART запущен"); - } break; - case 4: { - // код для остановке сенсора с работой по UART (остановка таймера) - Serial.println("Запрос No.4 - отправлен. Сенсор по UART остановлен"); - } break; - case 5: { - // код для запуска сенсора с работой по PWM (запуск таймера) - Serial.println("Запрос No.5 - отправлен. Сенсор по PWM запущен"); - } break; - case 6: { - // код для остановки сенсора с работой по PWM (остановка таймера) - Serial.println("Запрос No.6 - отправлен. Сенсор по PWM остановлен"); - } break; - case 7: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSampleABCon[i]; - } - Serial.println("Запрос No.7 - отправлен. Включаем функцию атокалибровки"); - } break; - case 8: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSampleABCoff[i]; - } - Serial.println("Запрос No.8 - отправлен. Выключаем функцию атокалибровки"); - } break; - case 9: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSample2000Range[i]; - } - Serial.println("Запрос No.9 - отправлен. Установливаем шкалу 0-2000"); - } break; - case 10: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSample5000Range[i]; - } - Serial.println("Запрос No.10 - отправлен. Установливаем шкалу 0-5000"); - } break; - case 11: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSampleZeroPnt[i]; - } - Serial.println("Запрос No.11 - отправлен. Калибровка. Установливаем нулевой уровень"); - } break; - case 12: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSampleReset1[i]; - } - Serial.println("Запрос No.11 - отправлен. Запрос на Сброс"); - } break; - case 13: { - for (int i = 0; i < 9; i++) { - request_cmd[i] = uartReqSamplePpm[i]; - } - Serial.println("Запрос No.13 - отправлен. Запрос по Температуре"); - } break; default: // byte c = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; // if nothing else matches, do the default // default is optional break; - } - - swSerialCO2->write(request_cmd, 9); - - Serial.print("Request : "); - for (int i = 0; i < 9; i++) { - Serial.print(" "); - Serial.print(request_cmd[i]); - } - Serial.println(" "); - - delay(50); - - unsigned char response[9]; - - Serial.print("Response :"); - swSerialCO2->readBytes(response, 9); - - for (int i = 0; i < 9; i++) { - Serial.print(" "); - Serial.print(response[i]); - } - Serial.println(" "); - - byte crc = 0; - for (int i = 1; i < 8; i++) crc += response[i]; - crc = 255 - crc; - crc += 1; - - if (!(response[0] == 0xFF && response[8] == crc)) { - Serial.println("Range CRC error: " + String(crc) + " / " + String(response[8])); - reply = 0; - } else { - // Serial.println("No CRC errors"); - switch (request) { - case 1: { - unsigned int responseHigh = (unsigned int)response[2]; - unsigned int responseLow = (unsigned int)response[3]; - unsigned int ppm = (256 * responseHigh) + responseLow; - - Serial.print("CO2 UART = "); - Serial.println(ppm); - - temperature = response[4] - 44; // - 40; - Serial.print("Temperature = "); - Serial.println(temperature); - temperatureUpdated = true; - - reply = ppm; - } break; - - case 2: { - reply = 1; - Serial.println("Case 2 - OK. На запрос шкалы пришел ответ"); - } break; - case 3: { - // Serial.println("Case 3 - OK"); - reply = 1; - } break; - case 4: { - // Serial.println("Case 4 - OK"); - reply = 1; - } break; - case 5: { - // Serial.println("Case 5 - OK"); - reply = 1; - } break; - case 6: { - Serial.println("Case 6 - OK"); - reply = 1; - } break; - case 7: { - Serial.println("Case 7 - OK. ABC включен"); - reply = 1; - } break; - case 8: { - Serial.println("Case 8 - OK. ABC выключен"); - reply = 1; - } break; - case 9: { - Serial.println("Case 9 - OK. Установлена шкала 0-2000"); - reply = 1; - prevRange = 2000; - } break; - case 10: { - Serial.println("Case 9 - OK. Установлена шкала 0-5000"); - reply = 1; - prevRange = 5000; - } break; - case 11: { - reply = 1; - Serial.println("Запрос No.11 - сработал. Калибровка. Установлен нулевой уровень"); - } break; - - case 12: { - reply = 1; - Serial.println("Запрос No.12 - сработал. Сброс произошел"); - } break; - - case 13: { - reply = 1; - temperature = response[4] - 44; // - 40; - - Serial.println("Запрос No.12 - сработал. Температура получена"); - Serial.println(temperature); - } break; - - default: - // byte c = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; - // if nothing else matches, do the default - // default is optional - break; } } // Serial.print("reply = "); diff --git a/src/modules/sensors/Mhz19/modinfo.json b/src/modules/sensors/Mhz19/modinfo.json index fcf848f9..7da1c1fa 100644 --- a/src/modules/sensors/Mhz19/modinfo.json +++ b/src/modules/sensors/Mhz19/modinfo.json @@ -2,7 +2,7 @@ "menuSection": "Сенсоры", "configItem": [ { - "name": "MHZ-19 CO2 (UART)", + "name": "MHZ-19 CO2 UART", "type": "Reading", "subtype": "Mhz19uart", "id": "co2uart", @@ -12,7 +12,7 @@ "plus": 0, "multiply": 1, "round": 1, - "pin": 0, + "warmUp": 120, "rxPin": 13, "txPin": 12, "int": 15, @@ -20,7 +20,7 @@ "ABC": 1 }, { - "name": "MHZ-19 CO2 (PWM)", + "name": "MHZ-19 CO2 PWM", "type": "Reading", "subtype": "Mhz19pwm", "id": "co2pwm", @@ -30,11 +30,13 @@ "plus": 0, "multiply": 1, "round": 1, + "warmUp": 120, "pin": 15, + "maxRetriesNotAvailable": 10, "int": 300 }, { - "name": "Cенсор температуры от MHZ-19 UART", + "name": "MHZ-19 Температура UART", "type": "Reading", "subtype": "Mhz19temp", "id": "Mhz19temp", @@ -46,64 +48,28 @@ "round": 1, "rxPin": 13, "txPin": 12, - "ABC": 1, - "int": 30 - }, - { - "name": "Рабочий диапазон от MHZ-19 UART", - "type": "Reading", - "subtype": "Mhz19range", - "id": "Mhz19range", - "widget": "anydataPpm", - "page": "Сенсоры", - "descr": "Диапазон", - "plus": 0, - "multiply": 1, - "round": 1, - "rxPin": 13, - "txPin": 12, - "range": 5000, - "ABC": 1, - "int": 30 - }, - { - "name": "Автокалибровка от MHZ-19 UART", - "type": "Reading", - "subtype": "Mhz19ABC", - "id": "Mhz19ABC", - "widget": "anydataDef", - "page": "Сенсоры", - "descr": "ABC", - "rxPin": 13, - "txPin": 12, - "range": 5000, - "ABC": 1, "int": 30 } ], "about": { "authorName": "Alex K", "authorContact": "https://t.me/cmche", - "authorGit": "", + "authorGit": "https://github.com/CHE77/Mhz19forIotManager", "specialThanks": "", "moduleName": "Mhz19", - "moduleVersion": "1.0", - "usedRam": 15, - "subTypes": [ - "Mhz19uart", - "Mhz19pwm", - "Mhz19temp", - "Mhz19range", - "Mhz19ABC" - ], - "title": "Датчик температуры и CO2 с Mhz19", - "moduleDesc": "Позволяет получить значения температуры и CO2 с Mhz19.", + "moduleVersion": "2.0 - можно переназначать пины", + "moduleDesc": "Позволяет получить значения уровня концетрации CO2 с Mhz19 по UART и/или ШИМ. Замер по ШИМ может производить 1-2 сек. задержки", "propInfo": { + "plus": "поправочный коэффиент +c", + "multiply": "поправочный коэффиент k*", + "round": "округление", "int": "Количество секунд между опросами датчика.", - "rxPin": "", - "txPin": "", - "range": "", - "ABC": "" + "rxPin": "Esp8266: GPIO 13 - D7, ESP32: GPIO 19 - RX1, > MHZ19: TXD зеленый провод", + "txPin": "Esp8266: GPIO 12 - D6, ESP32: GPIO 18 - TX1, > MHZ19: RXD синий провод", + "range": "Шкала по умолчанию 0-5000ppm. Также можно выбрать 2000", + "ABC": "Автокалибровка. По умолчанию включена. Раз в сутки на 20 мин. надо выставлять на свежий воздух.", + "pin": "пин получения значений по ШИМ. Esp8266: GPIO 15 - D8, ESP32: GPIO 21, > MHZ19: PWM желтый провод", + "maxRetriesNotAvailable": "Максимальное количество попыток опроса сенсора по ШИМ. (может задерживать контроллер)" } }, "defActive": false, diff --git a/src/modules/sensors/Pzem004t/Pzem004t.cpp b/src/modules/sensors/Pzem004t/Pzem004t.cpp index 1bbf4bbd..8f0dab93 100644 --- a/src/modules/sensors/Pzem004t/Pzem004t.cpp +++ b/src/modules/sensors/Pzem004t/Pzem004t.cpp @@ -5,11 +5,10 @@ #include "PZEMSensor.h" #include "modules/sensors/UART/Uart.h" -PZEMSensor* pzem; - class Pzem004v : public IoTItem { private: String addr; + PZEMSensor* pzem; public: Pzem004v(String parameters) : IoTItem(parameters) { @@ -21,17 +20,147 @@ class Pzem004v : public IoTItem { void doByInterval() { if (pzem) { - value.valD = pzem->values()->voltage; - regEvent(value.valD, "Pzem Voltage"); + bool online = false; + value.valD = pzem->values(online)->voltage; + if (online) { + regEvent(value.valD, "Pzem V"); + } else { + regEvent(NAN, "Pzem V"); + SerialPrint("E", "Pzem", "V error"); + } } } ~Pzem004v(){}; }; +class Pzem004a : public IoTItem { + private: + String addr; + PZEMSensor* pzem; + + public: + Pzem004a(String parameters) : IoTItem(parameters) { + addr = jsonReadStr(parameters, "addr"); + if (myUART) { + pzem = new PZEMSensor(myUART, hexStringToUint8(addr)); + } + } + + void doByInterval() { + if (pzem) { + bool online = false; + value.valD = pzem->values(online)->current; + if (online) { + regEvent(value.valD, "Pzem A"); + } else { + regEvent(NAN, "Pzem A"); + SerialPrint("E", "Pzem", "A error"); + } + } + } + + ~Pzem004a(){}; +}; + +class Pzem004w : public IoTItem { + private: + String addr; + PZEMSensor* pzem; + + public: + Pzem004w(String parameters) : IoTItem(parameters) { + addr = jsonReadStr(parameters, "addr"); + if (myUART) { + pzem = new PZEMSensor(myUART, hexStringToUint8(addr)); + } + } + + void doByInterval() { + if (pzem) { + bool online = false; + value.valD = pzem->values(online)->power; + if (online) { + regEvent(value.valD, "Pzem W"); + } else { + regEvent(NAN, "Pzem W"); + SerialPrint("E", "Pzem", "W error"); + } + } + } + + ~Pzem004w(){}; +}; + +class Pzem004wh : public IoTItem { + private: + String addr; + PZEMSensor* pzem; + + public: + Pzem004wh(String parameters) : IoTItem(parameters) { + addr = jsonReadStr(parameters, "addr"); + if (myUART) { + pzem = new PZEMSensor(myUART, hexStringToUint8(addr)); + } + } + + void doByInterval() { + if (pzem) { + bool online = false; + value.valD = pzem->values(online)->energy; + if (online) { + regEvent(value.valD, "Pzem Wh"); + } else { + regEvent(NAN, "Pzem Wh"); + SerialPrint("E", "Pzem", "Wh error"); + } + } + } + + ~Pzem004wh(){}; +}; + +class Pzem004hz : public IoTItem { + private: + String addr; + PZEMSensor* pzem; + + public: + Pzem004hz(String parameters) : IoTItem(parameters) { + addr = jsonReadStr(parameters, "addr"); + if (myUART) { + pzem = new PZEMSensor(myUART, hexStringToUint8(addr)); + } + } + + void doByInterval() { + if (pzem) { + bool online = false; + value.valD = pzem->values(online)->freq; + if (online) { + regEvent(value.valD, "Pzem Hz"); + } else { + regEvent(NAN, "Pzem Hz"); + SerialPrint("E", "Pzem", "Hz error"); + } + } + } + + ~Pzem004hz(){}; +}; + void* getAPI_Pzem004(String subtype, String param) { if (subtype == F("Pzem004v")) { return new Pzem004v(param); + } else if (subtype == F("Pzem004a")) { + return new Pzem004a(param); + } else if (subtype == F("Pzem004w")) { + return new Pzem004w(param); + } else if (subtype == F("Pzem004wh")) { + return new Pzem004wh(param); + } else if (subtype == F("Pzem004hz")) { + return new Pzem004hz(param); } else { return nullptr; } diff --git a/src/modules/sensors/Pzem004t/modinfo.json b/src/modules/sensors/Pzem004t/modinfo.json index 1f9d223e..d89f3d71 100644 --- a/src/modules/sensors/Pzem004t/modinfo.json +++ b/src/modules/sensors/Pzem004t/modinfo.json @@ -5,7 +5,7 @@ "name": "PZEM 004t Напряжение", "type": "Reading", "subtype": "Pzem004v", - "id": "V", + "id": "v", "widget": "anydataVlt", "page": "Сенсоры", "descr": "Напряжение", @@ -16,7 +16,7 @@ "name": "PZEM 004t Сила тока", "type": "Reading", "subtype": "Pzem004a", - "id": "A", + "id": "a", "widget": "anydataAmp", "page": "Сенсоры", "descr": "Сила тока", @@ -27,7 +27,7 @@ "name": "PZEM 004t Мощность", "type": "Reading", "subtype": "Pzem004w", - "id": "W", + "id": "w", "widget": "anydataWt", "page": "Сенсоры", "descr": "Мощность", @@ -38,7 +38,7 @@ "name": "PZEM 004t Энергия", "type": "Reading", "subtype": "Pzem004wh", - "id": "Wh", + "id": "wh", "widget": "anydataWth", "page": "Сенсоры", "descr": "Энергия", @@ -49,12 +49,12 @@ "name": "PZEM 004t Частота", "type": "Reading", "subtype": "Pzem004hz", - "id": "Hz", + "id": "hz", "widget": "anydataHtz", "page": "Сенсоры", - "descr": "Энергия", + "descr": "Частота", "int": 15, - "addr": "0x77" + "addr": "0xF8" } ], "about": { @@ -72,16 +72,20 @@ "Pzem004wh", "Pzem004hz" ], - "title": "Счетчик электроэнергии PZEM 004t версии 3.0 (с модбасом)", + "title": "Счетчик электроэнергии PZEM 004 t версии 3.0 (с модбасом). Возможно подключение трех счетчиков к одной esp для трехфазных сетей. Для этого нужно настроить разные адреса modbus в платах pzem", "moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры", "propInfo": { "addr": "Адрес modbus", - "int": "Количество секунд между опросами датчика" + "int": "Количество секунд между опросами датчика. Желателно устанавливать разные интервалы для параметров что бы опросы происходили в разное время." } }, "defActive": true, "devices": { - "esp32_4mb": [], - "esp8266_4mb": [] + "esp32_4mb": [ + "mandulaj/PZEM-004T-v30" + ], + "esp8266_4mb": [ + "mandulaj/PZEM-004T-v30" + ] } } \ No newline at end of file diff --git a/src/modules/sensors/Sds011/Sds011.cpp b/src/modules/sensors/Sds011/Sds011.cpp index ab569788..fb081e38 100644 --- a/src/modules/sensors/Sds011/Sds011.cpp +++ b/src/modules/sensors/Sds011/Sds011.cpp @@ -14,55 +14,86 @@ extern IoTGpio IoTgpio; #include "SdsDustSensor.h" -//встроена в ядро для 8266, для 32 по этому же имени обращаемся к другой библиотеке plerup/EspSoftwareSerial -#include +//#define __DEBUG_SDS_DUST_SENSOR__ + +int rxPinSDS; // Esp8266: 14/D5 – подключаем к Tx сенсора +int txPinSDS; // Esp8266: 16/D0 – подключаем к Rx сенсора + +// SdsDustSensor sds(rxPinSDS, txPinSDS); +SdsDustSensor *sds = nullptr; -// to do убрать глобальный экземпляр -#ifdef ESP8266 -int rxPinSDS = 13; // D7 – подключаем к Tx сенсора -int txPinSDS = 12; // D6 – подключаем к Rx сенсора -SdsDustSensor sds(rxPinSDS, txPinSDS); -#endif #ifdef ESP32 #include HardwareSerial sdsSerial(2); -SdsDustSensor sds(sdsSerial); +// SdsDustSensor sds(sdsSerial); #endif -unsigned int warmUp; -unsigned int period; +int retryDelayMs = 5; +int maxRetriesNotAvailable = 100; +unsigned int purge = 30; +unsigned int interval = 300; +unsigned int purgeDelay = 270; +unsigned int continuous = 0; +bool startUp = true; +TickerScheduler ts_sds(2); + +enum TimerTask_t_sds +{ + WAKEUP, + PRINT +}; + +int firstSensor = 0; bool SDS011_init_flag = true; void SDS011_init(); -float Sds011request(int sensorID); +void Sds011request(int sensorID); + +IoTItem *item_Sds011_25 = nullptr; // pointer +IoTItem *item_Sds011_10 = nullptr; //Это файл сенсора, в нем осуществляется чтение сенсора. //для добавления сенсора вам нужно скопировать этот файл и заменить в нем текст AnalogAdc на название вашего сенсора //Название должно быть уникальным, коротким и отражать суть сенсора. -class Sds011_25 : public IoTItem { - private: +class Sds011_25 : public IoTItem +{ +private: //======================================================================================================= // Секция переменных. //Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы //впоследствии использовать их в loop и setup // unsigned int _pin; - public: +public: //======================================================================================================= // setup() //это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора. //Такие как ...begin и подставлять в них параметры полученные из web интерфейса. //Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции: // jsonReadStr, jsonReadBool, jsonReadInt - Sds011_25(String parameters) : IoTItem(parameters) { - // _pin = jsonReadInt(parameters, "pin"); -#ifdef ESP8266 + Sds011_25(String parameters) : IoTItem(parameters) + { + item_Sds011_25 = this; + rxPinSDS = jsonReadInt(parameters, "rxPin"); txPinSDS = jsonReadInt(parameters, "txPin"); -#endif - warmUp = jsonReadInt(parameters, "warmUp"); // сек. пробужнение должен быть больше - period = jsonReadInt(parameters, "period"); // сек. время зарогрева/продувки, затем идут замеры + + purge = jsonReadInt(parameters, "purge"); // сек. пробужнение должен быть больше + interval = jsonReadInt(parameters, "int"); // сек. время зарогрева/продувки, затем идут замеры + continuous = jsonReadInt(parameters, "continuousMode"); // сек. время зарогрева/продувки, затем идут замеры + maxRetriesNotAvailable = jsonReadInt(parameters, "maxRetriesNotAvailable"); // сек. время зарогрева/продувки, затем идут замеры + retryDelayMs = jsonReadInt(parameters, "retryDelayMs"); // сек. время зарогрева/продувки, затем идут замеры + if (continuous) + { + SerialPrint("i", "Sensor Sds011", "Continuous mode"); + ts_sds.remove(PRINT); + ts_sds.remove(WAKEUP); + } + purgeDelay = interval - purge; + + SDS011_init(); + firstSensor = 0; } //======================================================================================================= // doByInterval() @@ -72,41 +103,70 @@ class Sds011_25 : public IoTItem { //если у сенсора несколько величин то делайте несколько regEvent //не используйте delay - помните, что данный loop общий для всех модулей. Если у вас планируется длительная операция, постарайтесь разбить ее на порции //и выполнить за несколько тактов - void doByInterval() { + void doByInterval() + { SDS011_init(); - Serial.println("request from 25"); - value.valD = Sds011request(25); - - regEvent(value.valD, "Sds011_25"); //обязательный вызов хотяб один + // SerialPrint("i", "Sensor Sds011", "Request for 2.5"); + Sds011request(25); + // value.valD = Sds011request(25); + // regEvent(value.valD, "Sds011_25"); //обязательный вызов хотяб один } - ~Sds011_25(){}; }; //////////////////////////////////// for PM 10//= -class Sds011_10 : public IoTItem { - private: +class Sds011_10 : public IoTItem +{ +private: //======================================================================================================= // Секция переменных. //Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы //впоследствии использовать их в loop и setup - public: +public: //======================================================================================================= // setup() - //это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора. - //Такие как ...begin и подставлять в них параметры полученные из web интерфейса. - //Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции: - // jsonReadStr, jsonReadBool, jsonReadInt - Sds011_10(String parameters) : IoTItem(parameters) { -// _pin = jsonReadInt(parameters, "pin"); -#ifdef ESP8266 + + Sds011_10(String parameters) : IoTItem(parameters) + { + item_Sds011_10 = this; + rxPinSDS = jsonReadInt(parameters, "rxPin"); txPinSDS = jsonReadInt(parameters, "txPin"); -#endif - warmUp = jsonReadInt(parameters, "warmUp"); // сек. пробужнение должен быть больше - period = jsonReadInt(parameters, "period"); // сек. время зарогрева/продувки, затем идут замеры + + purge = jsonReadInt(parameters, "purge"); // сек. пробужнение должен быть больше + interval = jsonReadInt(parameters, "int"); // сек. время зарогрева/продувки, затем идут замеры + continuous = jsonReadInt(parameters, "continuousMode"); // сек. время зарогрева/продувки, затем идут замеры + if (continuous) + { + SerialPrint("i", "Sensor Sds011", "Continuous mode"); + ts_sds.remove(PRINT); + ts_sds.remove(WAKEUP); + } + else + { + SerialPrint("i", "Sensor Sds011", "WakeUp mode"); + } + purgeDelay = interval - purge; + SDS011_init(); + firstSensor = 0; + } + + //луп выполняющий переодическое дерганье + void loop() + { + ts_sds.update(); + if (enableDoByInt) + { + currentMillis = millis(); + difference = currentMillis - prevMillis; + if (difference >= _interval) + { + prevMillis = millis(); + this->doByInterval(); + } + } } //======================================================================================================= // doByInterval() @@ -117,12 +177,13 @@ class Sds011_10 : public IoTItem { //не используйте delay - помните, что данный loop общий для всех модулей. Если у вас планируется длительная операция, постарайтесь разбить ее на порции //и выполнить за несколько тактов - void doByInterval() { + void doByInterval() + { SDS011_init(); - Serial.println("request from 10"); - value.valD = Sds011request(10); - - regEvent(value.valD, "Sds011_10"); //обязательный вызов хотяб один + // SerialPrint("i", "Sensor Sds011", "Request for 10ppm"); + Sds011request(10); + // value.valD = Sds011request(10); + // regEvent(value.valD, "Sds011_10"); //обязательный вызов хотяб один } ~Sds011_10(){}; }; @@ -130,71 +191,148 @@ class Sds011_10 : public IoTItem { //после замены названия сенсора, на функцию можно не обращать внимания //если сенсор предполагает использование общего объекта библиотеки для нескольких экземпляров сенсора, то в данной функции необходимо предусмотреть //создание и контроль соответствующих глобальных переменных -void* getAPI_Sds011(String subtype, String param) { - if (subtype == F("Sds011_25")) { +void *getAPI_Sds011(String subtype, String param) +{ + if (subtype == F("Sds011_25")) + { return new Sds011_25(param); - } else if (subtype == F("Sds011_10")) { + } + else if (subtype == F("Sds011_10")) + { return new Sds011_10(param); - } else { + } + else + { return nullptr; } } -float Sds011request(int sensorID) { - float reply = 0; - static int a = 0; - static float pm25 = 0; - static float pm10 = 0; - static int startMillis = millis(); +void Sds011request(int sensorID) +{ + float pm25 = 0; + float pm10 = 0; - if (a == 0) { - Serial.print("SDS011 ... warmUp = "); - Serial.print(warmUp); - Serial.print(" period = "); - Serial.println(period); - sds.wakeup(); - startMillis = millis(); - a = a + 1; - } - if (a == 1 && millis() >= (startMillis + warmUp * 1000)) { - PmResult pm = sds.readPm(); - if (pm.isOk()) { + if (firstSensor == 0) + firstSensor = sensorID; + + if (firstSensor == sensorID) + { + if (!continuous) + { + ts_sds.remove(PRINT); + ts_sds.remove(WAKEUP); + } + + PmResult pm = sds->readPm(); + // SerialPrint("i", "Sensor Sds011", "sds.readPm()"); + if (pm.isOk()) + { pm25 = pm.pm25; pm10 = pm.pm10; - Serial.print("PM2.5 = "); - Serial.print(pm25); - Serial.print(" PM10 = "); - Serial.println(pm10); - a = a + 1; - sds.sleep(); - } else { - Serial.print("Could not read values from sensor 25, reason: "); - Serial.println(pm.statusToString()); - a = a + 1; + // SerialPrint("i", "Sensor Sds011", pm.toString()); + + if (item_Sds011_25 && pm25) + { + item_Sds011_25->value.valD = pm25; + item_Sds011_25->regEvent(item_Sds011_25->value.valD, "Sds011_25"); + } + + if (item_Sds011_10 && pm10) + { + item_Sds011_10->value.valD = pm10; + item_Sds011_10->regEvent(item_Sds011_10->value.valD, "Sds011_10"); + } + } + else + { + String msg = "Could not read values from sensor. Reason: " + pm.statusToString(); + SerialPrint("E", "Sensor Sds011", msg); + SDS011_init_flag = true; + } + + if (!continuous) + { + sds->sleep(); + SerialPrint("i", "Sensor Sds011", "sleep"); + String msg = "wakeUp planned in " + String(purgeDelay) + " seconds"; + SerialPrint("i", "Sensor Sds011", msg); + ts_sds.add( + PRINT, purgeDelay * 1000, [](void *) + { SerialPrint("i", "Sensor Sds011", "delayed wakeUp"); }, + nullptr, false); + ts_sds.add( + WAKEUP, purgeDelay * 1000, [](void *) + { sds->wakeup(); }, + nullptr, false); } } - if (a > 1 && millis() >= (startMillis + period * 1000)) { - Serial.println("end of period for pm25"); - a = 0; + else + { + //пропускаем вызов от второго сенсора } - if (sensorID == 25) { - reply = pm25; - } - if (sensorID == 10) { - reply = pm10; - } - return reply; + + return; } -void SDS011_init() { - if (SDS011_init_flag) { - sds.begin(); - Serial.println(sds.queryFirmwareVersion().toString()); // prints firmware version - // Serial.println(sds.setActiveReportingMode().toString()); // - String ReportingMode = sds.setActiveReportingMode().toString(); - Serial.println(ReportingMode); - if (ReportingMode == "Mode: active") { +void SDS011_init() +{ + if (SDS011_init_flag) + { + +#ifdef ESP8266 + + if (!sds) + { + Serial.println("no sds, creating"); + sds = new SdsDustSensor(rxPinSDS, txPinSDS, retryDelayMs, maxRetriesNotAvailable); + } + else + { + Serial.println("sds already created"); + } + + sds->begin(9600); + delay(200); // +#endif + +#ifdef ESP32 + sdsSerial.begin(9600, SERIAL_8N1, rxPinSDS, txPinSDS); + delay(200); + if (!sds) + { + Serial.println("no sds, creating"); + sds = new SdsDustSensor(sdsSerial, retryDelayMs, maxRetriesNotAvailable); + } + else + { + Serial.println("sds already created"); + } +#endif + + if (startUp) + { + sds->wakeup(); + SerialPrint("i", "Sensor Sds011", "wakeup on startUp"); // уже начинаем продувку + startUp = false; + } + String msg = sds->queryFirmwareVersion().toString(); // + SerialPrint("i", "Sensor Sds011", msg); + String ReportingMode = sds->setActiveReportingMode().toString(); + + if (ReportingMode == "Mode: active") + { + SerialPrint("i", "Sensor Sds011", ReportingMode); SDS011_init_flag = false; + if (continuous) + { + sds->wakeup(); + SerialPrint("i", "Sensor Sds011", "wakeUp if continuous"); + } + } + else + { + ReportingMode += " - check wiring!"; + SerialPrint("E", "Sensor Sds011", ReportingMode); } } } diff --git a/src/modules/sensors/Sds011/modinfo.json b/src/modules/sensors/Sds011/modinfo.json index cca5ad1f..ba7d0022 100644 --- a/src/modules/sensors/Sds011/modinfo.json +++ b/src/modules/sensors/Sds011/modinfo.json @@ -2,7 +2,7 @@ "menuSection": "Сенсоры", "configItem": [ { - "name": "SDS011 PM25 Датчик пыли", + "name": "SDS011 PM25 Пыль", "type": "Reading", "subtype": "Sds011_25", "id": "pmuart25", @@ -12,14 +12,16 @@ "plus": 0, "multiply": 1, "round": 10, - "rxPin": 13, - "txPin": 12, - "int": 15, - "warmUp": 30, - "period": 300 + "rxPin": 14, + "txPin": 16, + "int": 270, + "purge": 30, + "continuousMode": 0, + "maxRetriesNotAvailable": 100, + "retryDelayMs": 5 }, { - "name": "SDS011 PM10 Датчик пыли", + "name": "SDS011 PM10 Пыль", "type": "Reading", "subtype": "Sds011_10", "id": "pmuart10", @@ -29,33 +31,34 @@ "plus": 0, "multiply": 1, "round": 10, - "rxPin": 13, - "txPin": 12, - "int": 15, - "warmUp": 30, - "period": 300 + "rxPin": 14, + "txPin": 16, + "int": 270, + "purge": 30, + "continuousMode": 0, + "maxRetriesNotAvailable": 100, + "retryDelayMs": 5 } ], "about": { "authorName": "Alex K", "authorContact": "https://t.me/cmche", - "authorGit": "", + "authorGit": "https://github.com/CHE77/SDS011forIotManager", "specialThanks": "", "moduleName": "Sds011", - "moduleVersion": "1.0", - "usedRam": 15, - "subTypes": [ - "Sds011_25", - "Sds011_10" - ], - "title": "Датчик пыли", + "moduleVersion": "2.0 - можно переназначать пины, за один опрос - обновляются два элемента", "moduleDesc": "Позволяет получить значения концентрации пыли в воздухе с Sds011.", "propInfo": { - "int": "Количество секунд между опросами датчика.", - "rxPin": "", - "txPin": "", - "warmUp": "", - "period": "" + "plus": "поправочный коэффиент +c", + "multiply": "поправочный коэффиент k*", + "round": "округление", + "rxPin": "Esp8266: GPIO 14 - D5, ESP32: GPIO 16 - RX2, > подключаем к TXD сенсора", + "txPin": "Esp8266: GPIO 16 - D0, ESP32: GPIO 17 - TX2, > подключаем к RXD сенсора", + "int": "Количество секунд между опросами датчика", + "purge": "Время продувки сенсора перед замером. Cек.", + "continuousMode": "1 - Непрерывный режим, 0 - Режим с остановкой (щедящий)", + "maxRetriesNotAvailable": "Количество попыток ожидания ответа сенсора при опросе (не нужно менять)", + "retryDelayMs": "Задержка между попытками, миллисекунды (не нужно менять)" } }, "defActive": false, diff --git a/src/modules/sensors/UART/modinfo.json b/src/modules/sensors/UART/modinfo.json index 07b095ff..705a20cf 100644 --- a/src/modules/sensors/UART/modinfo.json +++ b/src/modules/sensors/UART/modinfo.json @@ -25,8 +25,8 @@ "subTypes": [ "SoftUART" ], - "title": "Софтовый uart для esp8266 или harware uart для esp32", - "moduleDesc": "Используется вместе с Pzem004t, в последствии будет доработан для связи с arduino платами", + "title": "Software uart для esp8266 или hardware uart для esp32", + "moduleDesc": "Используется вместе с Pzem004t или с другими работающими по uart сенсорами, в последствии будет доработан для связи с arduino платами", "propInfo": { "tx": "TX пин", "rx": "RX пин", diff --git a/src/modules/virtual/Logging/Loging.cpp b/src/modules/virtual/Loging/Loging.cpp similarity index 100% rename from src/modules/virtual/Logging/Loging.cpp rename to src/modules/virtual/Loging/Loging.cpp diff --git a/src/modules/virtual/Logging/modinfo.json b/src/modules/virtual/Loging/modinfo.json similarity index 79% rename from src/modules/virtual/Logging/modinfo.json rename to src/modules/virtual/Loging/modinfo.json index 42f2e768..354fb517 100644 --- a/src/modules/virtual/Logging/modinfo.json +++ b/src/modules/virtual/Loging/modinfo.json @@ -2,7 +2,7 @@ "menuSection": "Виртуальные элементы", "configItem": [ { - "name": "Логирование в график", + "name": "График", "type": "Writing", "subtype": "Loging", "id": "log", @@ -24,7 +24,7 @@ "moduleVersion": "3.0", "usedRam": 15, "title": "Логирование в график", - "moduleDesc": "Расширение позволяющее логировать любую величину в график. Графики доступны в мобильном приложении и в веб интерфейсе. Данные графиков хранятся в встроенной памяти esp. В окне ввода даты нужно выбрать день, историю которого вы хотите посмотреть. Старые файлы будут удаляться автоматически после того как объем оставшейся flesh памяти устройства будет менее 20 процентов", + "moduleDesc": "Расширение позволяющее логировать любую величину в график. Графики доступны в мобильном приложении и в веб интерфейсе. Данные графиков хранятся в встроенной памяти esp. В окне ввода даты можно выбирать день, историю которого вы хотите посмотреть. Старые файлы будут удаляться автоматически после того как объем оставшейся flesh памяти устройства будет менее 20 процентов", "propInfo": { "int": "Интервал логирования в мнутах, рекомендуется для esp8266 использоать интервал не менее 5-ти минут", "logid": "ID величины которую будем логировать", diff --git a/src/modules/virtual/LogingDaily/LogingDaily.cpp b/src/modules/virtual/LogingDaily/LogingDaily.cpp new file mode 100644 index 00000000..62e82e0d --- /dev/null +++ b/src/modules/virtual/LogingDaily/LogingDaily.cpp @@ -0,0 +1,282 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include "ESPConfiguration.h" +#include "NTP.h" + +class LogingDaily : public IoTItem { + private: + String logid; + String id; + String filesList = ""; + + int _publishType = -2; + int _wsNum = -1; + + int points; + + IoTItem *dateIoTItem; + + String prevDate = ""; + bool firstTimeDate = true; + + unsigned long interval; + + public: + LogingDaily(String parameters) : IoTItem(parameters) { + jsonRead(parameters, F("logid"), logid); + jsonRead(parameters, F("id"), id); + jsonRead(parameters, F("points"), points); + + if (points > 365) { + points = 365; + SerialPrint("E", F("LogingDaily"), "'" + id + "' user set more points than allowed, value reset to 365"); + } + jsonRead(parameters, F("int"), interval); + interval = interval * 1000 * 60; //приводим к милисекундам + } + + void doByInterval() { + if (hasDayChanged()) { + execute(); + } + } + + void execute() { + //если объект логгирования не был создан + if (!isItemExist(logid)) { + SerialPrint("E", F("LogingDaily"), "'" + id + "' LogingDaily object not exist, return"); + return; + } + + String value = getItemValue(logid); + + //если значение логгирования пустое + if (value == "") { + SerialPrint("E", F("LogingDaily"), "'" + id + "' LogingDaily value is empty, return"); + return; + } + + //если время не было получено из интернета + if (!isTimeSynch) { + SerialPrint("E", F("LogingDaily"), "'" + id + "' Сant LogingDaily - time not synchronized, return"); + return; + } + + String logData; + + float currentValue = value.toFloat(); + //прочитаем предудущее значение + float prevValue = readDataDB(id + "-v").toFloat(); + //сохраним в базу данных текущее значение, понадобится в следующие сутки + saveDataDB(id + "-v", value); + + float difference = currentValue - prevValue; + + jsonWriteInt(logData, "x", unixTime); + jsonWriteFloat(logData, "y1", difference); + + //прочитаем путь к файлу последнего сохранения + String filePath = readDataDB(id); + + //если данные о файле отсутствуют, создадим новый + if (filePath == "failed" || filePath == "") { + SerialPrint("E", F("LogingDaily"), "'" + id + "' file path not found, start create new file"); + createNewFileWithData(logData); + return; + } + + //считаем количество строк и определяем размер файла + size_t size = 0; + int lines = countJsonObj(filePath, size); + SerialPrint("i", F("LogingDaily"), "'" + id + "' " + "lines = " + String(lines) + ", size = " + String(size)); + + //если количество строк до заданной величины и дата не менялась + if (lines <= points && !hasDayChanged()) { + //просто добавим в существующий файл новые данные + addNewDataToExistingFile(filePath, logData); + //если больше или поменялась дата то создадим следующий файл + } else { + createNewFileWithData(logData); + } + } + + void createNewFileWithData(String &logData) { + logData = logData + ","; + + String path = "/lgd/" + id + "/" + id + ".txt"; //создадим путь вида /lgd/id/id.txt + //создадим пустой файл + if (writeEmptyFile(path) != "sucсess") { + SerialPrint("E", F("LogingDaily"), "'" + id + "' file writing error, return"); + return; + } + + //запишем в него данные + if (addFile(path, logData) != "sucсess") { + SerialPrint("E", F("LogingDaily"), "'" + id + "' data writing error, return"); + return; + } + //запишем путь к нему в базу данных + if (saveDataDB(id, path) != "sucсess") { + SerialPrint("E", F("LogingDaily"), "'" + id + "' db file writing error, return"); + return; + } + SerialPrint("i", F("LogingDaily"), "'" + id + "' file created http://" + WiFi.localIP().toString() + path); + } + + void addNewDataToExistingFile(String &path, String &logData) { + logData = logData + ","; + if (addFile(path, logData) != "sucсess") { + SerialPrint("i", F("LogingDaily"), "'" + id + "' file writing error, return"); + return; + }; + SerialPrint("i", F("LogingDaily"), "'" + id + "' LogingDaily in file http://" + WiFi.localIP().toString() + path); + } + + bool hasDayChanged() { + bool changed = false; + String currentDate = getTodayDateDotFormated(); + if (!firstTimeDate) { + if (prevDate != currentDate) { + changed = true; + SerialPrint("i", F("NTP"), "Change day event"); +#if defined(ESP8266) + FileFS.gc(); +#endif +#if defined(ESP32) +#endif + } + } + firstTimeDate = false; + prevDate = currentDate; + return changed; + } + + void publishValue() { + String dir = "/lgd/" + id; + filesList = getFilesList(dir); + + SerialPrint("i", F("LogingDaily"), "file list: " + filesList); + + int f = 0; + + while (filesList.length()) { + String path = selectToMarker(filesList, ";"); + + path = "/lgd/" + id + path; + + f++; + + if (_publishType == TO_MQTT) { + publishChartFileToMqtt(path); + } else if (_publishType == TO_WS) { + publishChartToWs(path, _wsNum, 1000); + } else if (_publishType == TO_MQTT_WS) { + publishChartFileToMqtt(path); + publishChartToWs(path, _wsNum, 1000); + } + SerialPrint("i", F("LogingDaily"), String(f) + ") " + path + ", sent"); + + filesList = deleteBeforeDelimiter(filesList, ";"); + } + } + + void clearHistory() { + String dir = "/lgd/" + id; + cleanDirectory(dir); + } + + bool publishChartFileToMqtt(String path) { + File configFile = FileFS.open(path, FILE_READ); + if (!configFile) { + SerialPrint("E", F("LogingDaily"), path + " file reading error, json not created, return"); + return false; + } + String oneSingleJson = configFile.readString(); + configFile.close(); + String topic = mqttRootDevice + "/" + id; + oneSingleJson = "{\"maxCount\":" + String(calculateMaxCount()) + ",\"topic\":\"" + topic + "\",\"status\":[" + oneSingleJson + "]}"; + oneSingleJson.replace("},]}", "}]}"); + SerialPrint("i", "LogingDaily", "json size: " + String(oneSingleJson.length())); + publishChartMqtt(id, oneSingleJson); + return true; + } + + //особая функция отправки графиков в веб + void publishChartToWs(String filename, int num, size_t frameSize) { + String json; + jsonWriteStr(json, "topic", mqttRootDevice + "/" + id); + jsonWriteInt(json, "maxCount", calculateMaxCount()); + + String st = "/st/chart.json|"; + if (num == -1) { + standWebSocket.broadcastTXT(st); + } else { + standWebSocket.sendTXT(num, st); + } + String path = filepath(filename); + auto file = FileFS.open(path, "r"); + if (!file) { + SerialPrint(F("E"), F("FS"), F("reed file error")); + return; + } + size_t fileSize = file.size(); + SerialPrint(F("i"), F("FS"), "Send file '" + String(filename) + "', file size: " + String(fileSize)); + uint8_t payload[frameSize]; + int countRead = file.read(payload, sizeof(payload)); + while (countRead > 0) { + if (num == -1) { + standWebSocket.broadcastBIN(payload, countRead); + } else { + standWebSocket.sendBIN(num, payload, countRead); + } + countRead = file.read(payload, sizeof(payload)); + } + file.close(); + String end = "/end/chart.json|" + json; + if (num == -1) { + standWebSocket.broadcastTXT(end); + } else { + standWebSocket.sendTXT(num, end); + } + } + + void publishChartToWsSinglePoint(String value) { + String topic = mqttRootDevice + "/" + id; + String json = "{\"maxCount\":" + String(calculateMaxCount()) + ",\"topic\":\"" + topic + "\",\"status\":[{\"x\":" + String(unixTime) + ",\"y1\":" + value + "}]}"; + String pk = "/string/chart.json|" + json; + standWebSocket.broadcastTXT(pk); + } + + void setPublishDestination(int publishType, int wsNum = -1) { + _publishType = publishType; + _wsNum = wsNum; + } + + String getValue() { + return ""; + } + + void loop() { + if (enableDoByInt) { + currentMillis = millis(); + difference = currentMillis - prevMillis; + if (difference >= interval) { + prevMillis = millis(); + this->doByInterval(); + } + } + } + + //просто максимальное количество точек + int calculateMaxCount() { + return 86400; + } +}; + +void *getAPI_LogingDaily(String subtype, String param) { + if (subtype == F("LogingDaily")) { + return new LogingDaily(param); + } else { + return nullptr; + } +} diff --git a/src/modules/virtual/LogingDaily/modinfo.json b/src/modules/virtual/LogingDaily/modinfo.json new file mode 100644 index 00000000..ed6fe3c3 --- /dev/null +++ b/src/modules/virtual/LogingDaily/modinfo.json @@ -0,0 +1,39 @@ +{ + "menuSection": "Виртуальные элементы", + "configItem": [ + { + "name": "График дневного расхода", + "type": "Writing", + "subtype": "LogingDaily", + "id": "log", + "widget": "chart3", + "page": "Графики", + "descr": "Температура", + "num": 1, + "int": 1, + "logid": "t", + "points": 365 + } + ], + "about": { + "authorName": "Dmitry Borisenko", + "authorContact": "https://t.me/Dmitry_Borisenko", + "authorGit": "https://github.com/DmitryBorisenko33", + "specialThanks": "@itsid1 @Valiuhaaa Serg", + "moduleName": "LogingDaily", + "moduleVersion": "3.0", + "usedRam": 15, + "title": "График дневного расхода", + "moduleDesc": "Расширение позволяющее логировать накопительные величины и видеть их дневное изменение. Графики доступны в мобильном приложении и в веб интерфейсе. Данные графиков хранятся в встроенной памяти esp", + "propInfo": { + "int": "Интервал логирования в мнутах, частота проверки смены суток в минутах. Не рекомендуется менять", + "logid": "ID накопительной величины которую будем логировать", + "points": "Максимальное количество точек" + } + }, + "defActive": true, + "devices": { + "esp32_4mb": [], + "esp8266_4mb": [] + } +} \ No newline at end of file diff --git a/src/utils/FileUtils.cpp b/src/utils/FileUtils.cpp index f9698874..f7f33332 100644 --- a/src/utils/FileUtils.cpp +++ b/src/utils/FileUtils.cpp @@ -214,9 +214,10 @@ String readDataDB(String id) { void cleanLogs() { SerialPrint("i", "Files", "cleanLogs"); cleanDirectory("/db"); + //обращение к логированию из ядра //очистка данных всех экземпляров графиков for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { - if ((*it)->getSubtype() == "Loging") { + if ((*it)->getSubtype() == "Loging" || "LogingDaily") { (*it)->clearHistory(); } }