Merge branch 'ver4dev' of https://github.com/biveraxe/IoTManager into ver4dev

This commit is contained in:
2023-04-28 19:45:07 +03:00
19 changed files with 1170 additions and 281 deletions

Binary file not shown.

View File

@@ -382,7 +382,23 @@
}, },
{ {
"global": 0, "global": 0,
"name": "26. PZEM 004t Напряжение", "name": "26. Аналоговый счетчик импульсов",
"type": "Writing",
"subtype": "Impulse",
"id": "impulse",
"widget": "anydataDef",
"page": "Счетчики",
"descr": "Импульсов",
"needSave": 0,
"int": 1,
"pin": 16,
"pinMode": "INPUT",
"debounceDelay": 3,
"num": 26
},
{
"global": 0,
"name": "27. PZEM 004t Напряжение",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004v", "subtype": "Pzem004v",
"id": "v", "id": "v",
@@ -392,11 +408,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 26 "num": 27
}, },
{ {
"global": 0, "global": 0,
"name": "27. PZEM 004t Сила тока", "name": "28. PZEM 004t Сила тока",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004a", "subtype": "Pzem004a",
"id": "a", "id": "a",
@@ -406,11 +422,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 27 "num": 28
}, },
{ {
"global": 0, "global": 0,
"name": "28. PZEM 004t Мощность", "name": "29. PZEM 004t Мощность",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004w", "subtype": "Pzem004w",
"id": "w", "id": "w",
@@ -420,11 +436,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 28 "num": 29
}, },
{ {
"global": 0, "global": 0,
"name": "29. PZEM 004t Энергия", "name": "30. PZEM 004t Энергия",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004wh", "subtype": "Pzem004wh",
"id": "wh", "id": "wh",
@@ -434,11 +450,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 29 "num": 30
}, },
{ {
"global": 0, "global": 0,
"name": "30. PZEM 004t Частота", "name": "31. PZEM 004t Частота",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004hz", "subtype": "Pzem004hz",
"id": "hz", "id": "hz",
@@ -448,11 +464,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 30 "num": 31
}, },
{ {
"global": 0, "global": 0,
"name": "31. PZEM 004t Косинус", "name": "32. PZEM 004t Косинус",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004pf", "subtype": "Pzem004pf",
"id": "pf", "id": "pf",
@@ -462,11 +478,11 @@
"int": 15, "int": 15,
"addr": "0xF8", "addr": "0xF8",
"round": 1, "round": 1,
"num": 31 "num": 32
}, },
{ {
"global": 0, "global": 0,
"name": "32. PZEM настройка", "name": "33. PZEM настройка",
"type": "Reading", "type": "Reading",
"subtype": "Pzem004cmd", "subtype": "Pzem004cmd",
"id": "set", "id": "set",
@@ -478,11 +494,11 @@
"changeaddr": 0, "changeaddr": 0,
"setaddr": "0x01", "setaddr": "0x01",
"reset": 0, "reset": 0,
"num": 32 "num": 33
}, },
{ {
"global": 0, "global": 0,
"name": "33. Часы реального времени", "name": "34. Часы реального времени",
"type": "Reading", "type": "Reading",
"subtype": "RTC", "subtype": "RTC",
"id": "rtc", "id": "rtc",
@@ -498,11 +514,11 @@
"int": 5, "int": 5,
"btn-setUTime": "0", "btn-setUTime": "0",
"btn-setSysTime": "nil", "btn-setSysTime": "nil",
"num": 33 "num": 34
}, },
{ {
"global": 0, "global": 0,
"name": "34. Sht20 Температура", "name": "35. Sht20 Температура",
"type": "Reading", "type": "Reading",
"subtype": "Sht20t", "subtype": "Sht20t",
"id": "tmp2", "id": "tmp2",
@@ -511,11 +527,11 @@
"descr": "Температура", "descr": "Температура",
"int": 15, "int": 15,
"round": 1, "round": 1,
"num": 34 "num": 35
}, },
{ {
"global": 0, "global": 0,
"name": "35. Sht20 Влажность", "name": "36. Sht20 Влажность",
"type": "Reading", "type": "Reading",
"subtype": "Sht20h", "subtype": "Sht20h",
"id": "Hum2", "id": "Hum2",
@@ -524,11 +540,11 @@
"descr": "Влажность", "descr": "Влажность",
"int": 15, "int": 15,
"round": 1, "round": 1,
"num": 35 "num": 36
}, },
{ {
"global": 0, "global": 0,
"name": "36. Sht30 Температура", "name": "37. Sht30 Температура",
"type": "Reading", "type": "Reading",
"subtype": "Sht30t", "subtype": "Sht30t",
"id": "tmp30", "id": "tmp30",
@@ -537,11 +553,11 @@
"descr": "SHT30 Температура", "descr": "SHT30 Температура",
"int": 15, "int": 15,
"round": 1, "round": 1,
"num": 36 "num": 37
}, },
{ {
"global": 0, "global": 0,
"name": "37. Sht30 Влажность", "name": "38. Sht30 Влажность",
"type": "Reading", "type": "Reading",
"subtype": "Sht30h", "subtype": "Sht30h",
"id": "Hum30", "id": "Hum30",
@@ -550,12 +566,12 @@
"descr": "SHT30 Влажность", "descr": "SHT30 Влажность",
"int": 15, "int": 15,
"round": 1, "round": 1,
"num": 37 "num": 38
}, },
{ {
"global": 0, "global": 0,
"name": "38. HC-SR04 Ультразвуковой дальномер", "name": "39. HC-SR04 Ультразвуковой дальномер",
"num": 38, "num": 39,
"type": "Reading", "type": "Reading",
"subtype": "Sonar", "subtype": "Sonar",
"id": "sonar", "id": "sonar",
@@ -567,7 +583,7 @@
"int": 5 "int": 5
}, },
{ {
"name": "39. UART", "name": "40. UART",
"type": "Reading", "type": "Reading",
"subtype": "UART", "subtype": "UART",
"page": "", "page": "",
@@ -579,14 +595,14 @@
"line": 2, "line": 2,
"speed": 9600, "speed": 9600,
"eventFormat": 0, "eventFormat": 0,
"num": 39 "num": 40
}, },
{ {
"header": "Исполнительные устройства" "header": "Исполнительные устройства"
}, },
{ {
"global": 0, "global": 0,
"name": "40. Кнопка подключенная к пину", "name": "41. Кнопка подключенная к пину",
"type": "Writing", "type": "Writing",
"subtype": "ButtonIn", "subtype": "ButtonIn",
"id": "btn", "id": "btn",
@@ -601,11 +617,11 @@
"debounceDelay": 50, "debounceDelay": 50,
"fixState": 0, "fixState": 0,
"inv": 0, "inv": 0,
"num": 40 "num": 41
}, },
{ {
"global": 0, "global": 0,
"name": "41. Управление пином", "name": "42. Управление пином",
"type": "Writing", "type": "Writing",
"subtype": "ButtonOut", "subtype": "ButtonOut",
"needSave": 0, "needSave": 0,
@@ -616,11 +632,11 @@
"int": 0, "int": 0,
"inv": 0, "inv": 0,
"pin": 2, "pin": 2,
"num": 41 "num": 42
}, },
{ {
"global": 0, "global": 0,
"name": "42. Сервопривод", "name": "43. Сервопривод",
"type": "Writing", "type": "Writing",
"subtype": "IoTServo", "subtype": "IoTServo",
"id": "servo", "id": "servo",
@@ -631,11 +647,11 @@
"pin": 12, "pin": 12,
"apin": -1, "apin": -1,
"amap": "0, 4096, 0, 180", "amap": "0, 4096, 0, 180",
"num": 42 "num": 43
}, },
{ {
"global": 0, "global": 0,
"name": "43. Расширитель портов Mcp23017", "name": "44. Расширитель портов Mcp23017",
"type": "Reading", "type": "Reading",
"subtype": "Mcp23017", "subtype": "Mcp23017",
"id": "Mcp", "id": "Mcp",
@@ -645,11 +661,11 @@
"int": "0", "int": "0",
"addr": "0x20", "addr": "0x20",
"index": 1, "index": 1,
"num": 43 "num": 44
}, },
{ {
"global": 0, "global": 0,
"name": "44. MP3 плеер", "name": "45. MP3 плеер",
"type": "Reading", "type": "Reading",
"subtype": "Mp3", "subtype": "Mp3",
"id": "mp3", "id": "mp3",
@@ -659,11 +675,11 @@
"int": 1, "int": 1,
"pins": "14,12", "pins": "14,12",
"volume": 20, "volume": 20,
"num": 44 "num": 45
}, },
{ {
"global": 0, "global": 0,
"name": "45. Сенсорная кнопка", "name": "46. Сенсорная кнопка",
"type": "Writing", "type": "Writing",
"subtype": "Multitouch", "subtype": "Multitouch",
"id": "impulse", "id": "impulse",
@@ -677,11 +693,11 @@
"pinMode": "INPUT", "pinMode": "INPUT",
"debounceDelay": 50, "debounceDelay": 50,
"PWMDelay": 500, "PWMDelay": 500,
"num": 45 "num": 46
}, },
{ {
"global": 0, "global": 0,
"name": "46. Расширитель портов Pcf8574", "name": "47. Расширитель портов Pcf8574",
"type": "Reading", "type": "Reading",
"subtype": "Pcf8574", "subtype": "Pcf8574",
"id": "Pcf", "id": "Pcf",
@@ -691,11 +707,11 @@
"int": "0", "int": "0",
"addr": "0x20", "addr": "0x20",
"index": 1, "index": 1,
"num": 46 "num": 47
}, },
{ {
"global": 0, "global": 0,
"name": "47. PWM ESP8266", "name": "48. PWM ESP8266",
"type": "Writing", "type": "Writing",
"subtype": "Pwm8266", "subtype": "Pwm8266",
"id": "pwm", "id": "pwm",
@@ -707,11 +723,11 @@
"freq": 5000, "freq": 5000,
"val": 0, "val": 0,
"apin": -1, "apin": -1,
"num": 47 "num": 48
}, },
{ {
"global": 0, "global": 0,
"name": "48. Телеграм-Лайт", "name": "49. Телеграм-Лайт",
"type": "Writing", "type": "Writing",
"subtype": "TelegramLT", "subtype": "TelegramLT",
"id": "tg", "id": "tg",
@@ -720,14 +736,14 @@
"descr": "", "descr": "",
"token": "", "token": "",
"chatID": "", "chatID": "",
"num": 48 "num": 49
}, },
{ {
"header": "Экраны" "header": "Экраны"
}, },
{ {
"global": 0, "global": 0,
"name": "49. LCD экран 2004", "name": "50. LCD экран 2004",
"type": "Reading", "type": "Reading",
"subtype": "Lcd2004", "subtype": "Lcd2004",
"id": "Lcd", "id": "Lcd",
@@ -739,10 +755,10 @@
"size": "20,4", "size": "20,4",
"coord": "0,0", "coord": "0,0",
"id2show": "id датчика", "id2show": "id датчика",
"num": 49 "num": 50
}, },
{ {
"name": "50. LCD экран 1602", "name": "51. LCD экран 1602",
"type": "Reading", "type": "Reading",
"subtype": "Lcd2004", "subtype": "Lcd2004",
"id": "Lcd", "id": "Lcd",
@@ -754,6 +770,6 @@
"size": "16,2", "size": "16,2",
"coord": "0,0", "coord": "0,0",
"id2show": "id датчика", "id2show": "id датчика",
"num": 50 "num": 51
} }
] ]

View File

@@ -41,7 +41,25 @@
"label": "Ватты", "label": "Ватты",
"widget": "anydata", "widget": "anydata",
"after": "Wt", "after": "Wt",
"icon": "speedometer" "icon": "speedometer",
"color": [
{
"level": 0,
"value": ""
},
{
"level": 200,
"value": "#009933"
},
{
"level": 2000,
"value": "#FF9900"
},
{
"level": 4000,
"value": "red"
}
]
}, },
{ {
"name": "anydataWth", "name": "anydataWth",
@@ -62,7 +80,42 @@
"label": "Температура", "label": "Температура",
"widget": "anydata", "widget": "anydata",
"after": "°С", "after": "°С",
"icon": "thermometer" "icon": "thermometer",
"font": "OCR A Std",
"color": [
{
"level": -20,
"value": "#0000CC"
},
{
"level": -10,
"value": "#0000CC"
},
{
"level": 0,
"value": "#0000CC"
},
{
"level": 12,
"value": "#3366FF"
},
{
"level": 16,
"value": "#33CCFF"
},
{
"level": 18,
"value": "#009933"
},
{
"level": 30,
"value": "#FF9900"
},
{
"level": 40,
"value": "red"
}
]
}, },
{ {
"name": "anydataMm", "name": "anydataMm",
@@ -230,6 +283,13 @@
"after": "mWt", "after": "mWt",
"icon": "speedometer" "icon": "speedometer"
}, },
{
"name": "anydataCm",
"label": "Сантиметры",
"widget": "anydata",
"after": "cm",
"icon": "speedometer"
},
{ {
"name": "nil", "name": "nil",
"label": "Без виджета" "label": "Без виджета"

Binary file not shown.

View File

@@ -4,12 +4,17 @@
#ifdef STANDARD_WEB_SERVER #ifdef STANDARD_WEB_SERVER
extern void standWebServerInit(); extern void standWebServerInit();
extern bool handleFileRead(String path); extern bool handleFileRead(String path);
extern String getContentType(String filename); //extern String getContentType(String filename);
//#ifdef REST_FILE_OPERATIONS //#ifdef REST_FILE_OPERATIONS
extern void handleFileUpload(); extern void handleFileUpload();
extern void handleFileDelete(); extern void handleFileDelete();
extern void handleFileCreate(); extern void handleFileCreate();
extern void handleFileList(); extern void handleFileList();
void printDirectory(File dir, String& out); //void printDirectory(File dir, String& out);
extern void handleStatus();
extern void handleGetEdit();
extern void replyOK ();
extern void handleNotFound ();
//#endif //#endif
#endif #endif

View File

@@ -136,6 +136,10 @@
"path": "src/modules/sensors/Hx711", "path": "src/modules/sensors/Hx711",
"active": false "active": false
}, },
{
"path": "src/modules/sensors/Impulse",
"active": true
},
{ {
"path": "src/modules/sensors/Ina219", "path": "src/modules/sensors/Ina219",
"active": false "active": false

View File

@@ -94,7 +94,7 @@ build_src_filter =
[env:esp8266_2mb_ota] [env:esp8266_2mb_ota]
lib_deps = lib_deps =
${common_env_data.lib_deps_external} ${common_env_data.lib_deps_external}
${env:esp8266_2mb_fromitems.lib_deps} ${env:esp8266_2mb_ota_fromitems.lib_deps}
ESPAsyncUDP ESPAsyncUDP
build_flags = -Desp8266_2mb_ota="esp8266_2mb_ota" build_flags = -Desp8266_2mb_ota="esp8266_2mb_ota"
framework = arduino framework = arduino
@@ -110,7 +110,7 @@ build_src_filter =
+<classes/*.cpp> +<classes/*.cpp>
+<utils/*.cpp> +<utils/*.cpp>
+<modules/*.cpp> +<modules/*.cpp>
${env:esp8266_2mb_fromitems.build_src_filter} ${env:esp8266_2mb_ota_fromitems.build_src_filter}
[env:esp8285_1mb] [env:esp8285_1mb]
lib_deps = lib_deps =
@@ -232,6 +232,7 @@ build_src_filter =
[env:esp8266_2mb_ota_fromitems] [env:esp8266_2mb_ota_fromitems]
lib_deps = lib_deps =
adafruit/Adafruit BME280 Library
plerup/EspSoftwareSerial plerup/EspSoftwareSerial
build_src_filter = build_src_filter =
+<modules/virtual/Cron> +<modules/virtual/Cron>
@@ -241,6 +242,7 @@ build_src_filter =
+<modules/virtual/Variable> +<modules/virtual/Variable>
+<modules/virtual/VariableColor> +<modules/virtual/VariableColor>
+<modules/virtual/VButton> +<modules/virtual/VButton>
+<modules/sensors/Bme280>
+<modules/sensors/Pzem004t> +<modules/sensors/Pzem004t>
+<modules/sensors/UART> +<modules/sensors/UART>
+<modules/exec/TelegramLT> +<modules/exec/TelegramLT>
@@ -332,6 +334,7 @@ build_src_filter =
+<modules/sensors/Bmp280> +<modules/sensors/Bmp280>
+<modules/sensors/Dht1122> +<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20> +<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Pzem004t> +<modules/sensors/Pzem004t>
+<modules/sensors/RTC> +<modules/sensors/RTC>
+<modules/sensors/Sht20> +<modules/sensors/Sht20>

View File

@@ -1,9 +1,17 @@
#include "StandWebServer.h" #include "StandWebServer.h"
#ifdef STANDARD_WEB_SERVER #ifdef STANDARD_WEB_SERVER
File fsUploadFile; File uploadFile;
String unsupportedFiles = String();
void standWebServerInit() { static const char TEXT_PLAIN[] PROGMEM = "text/plain";
static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR";
static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound";
//static bool fsOK;
//const char* fsName = "LittleFS";
void standWebServerInit()
{
// Кэшировать файлы для быстрой работы // Кэшировать файлы для быстрой работы
HTTP.serveStatic("/bundle.js", FileFS, "/", "max-age=31536000"); // кеширование на 1 год HTTP.serveStatic("/bundle.js", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
HTTP.serveStatic("/bundle.css", FileFS, "/", "max-age=31536000"); // кеширование на 1 год HTTP.serveStatic("/bundle.css", FileFS, "/", "max-age=31536000"); // кеширование на 1 год
@@ -41,18 +49,18 @@ void standWebServerInit() {
HTTP.on("/set", HTTP_GET, []() { HTTP.on("/set", HTTP_GET, []() {
if (HTTP.hasArg(F("routerssid")) && WiFi.getMode() == WIFI_AP) { if (HTTP.hasArg(F("routerssid")) && WiFi.getMode() == WIFI_AP) {
jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid"))); jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid")));
syncSettingsFlashJson(); syncSettingsFlashJson();
HTTP.send(200, "text/plain", "ok"); HTTP.send(200, "text/plain", "ok");
} }
if (HTTP.hasArg(F("routerpass")) && WiFi.getMode() == WIFI_AP) { if (HTTP.hasArg(F("routerpass")) && WiFi.getMode() == WIFI_AP) {
jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass"))); jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass")));
syncSettingsFlashJson(); syncSettingsFlashJson();
HTTP.send(200, "text/plain", "ok"); HTTP.send(200, "text/plain", "ok");
} }
}); });
// Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше // Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше
// httpUpdater.setup(&HTTP); // httpUpdater.setup(&HTTP);
@@ -60,193 +68,640 @@ void standWebServerInit() {
// Запускаем HTTP сервер // Запускаем HTTP сервер
HTTP.begin(); HTTP.begin();
//#ifdef REST_FILE_OPERATIONS
// SPIFFS.begin();
// {
// Dir dir = SPIFFS.openDir("/");
// while (dir.next()) {
// String fileName = dir.fileName();
// size_t fileSize = dir.fileSize();
// }
// }
// HTTP страницы для работы с FFS // HTTP страницы для работы с FFS
// list directory ////////////////////////////////
// WEB SERVER INIT
// Filesystem status
HTTP.on("/status", HTTP_GET, handleStatus);
// List directory
HTTP.on("/list", HTTP_GET, handleFileList); HTTP.on("/list", HTTP_GET, handleFileList);
//загрузка редактора editor // Load editor
HTTP.on("/edit", HTTP_GET, []() { HTTP.on("/edit", HTTP_GET, handleGetEdit);
if (!HTTP.args()) {
if (!handleFileRead("/edit.htm")) HTTP.send(404, "text/plain", "FileNotFound");
}
if (HTTP.hasArg("list")) { // Create file
handleFileList();
}
if (HTTP.hasArg("edit")) {
if (!handleFileRead(HTTP.arg("edit"))) HTTP.send(404, "text/plain", "FileNotFound");
}
if (HTTP.hasArg("download")) {
if (!handleFileRead(HTTP.arg("download"))) HTTP.send(404, "text/plain", "FileNotFound");
}
});
//Создание файла
HTTP.on("/edit", HTTP_PUT, handleFileCreate); HTTP.on("/edit", HTTP_PUT, handleFileCreate);
//Удаление файла // Delete file
HTTP.on("/edit", HTTP_DELETE, handleFileDelete); HTTP.on("/edit", HTTP_DELETE, handleFileDelete);
//Изменение файла // Upload file
HTTP.on( // - first callback is called after the request has ended with all parsed arguments
"/edit", HTTP_POST, []() { // - second callback handles file upload at that location
HTTP.send(200, "text/plain", ""); HTTP.on("/edit", HTTP_POST, replyOK, handleFileUpload);
},
handleFileUpload);
// called when the url is not defined here // Default handler for all URIs not defined above
HTTP.onNotFound([]() { // Use it to read files from filesystem
if (!handleFileRead(HTTP.uri())) HTTP.onNotFound(handleNotFound);
HTTP.send(404, "text/plain", "FileNotFound");
});
} }
bool handleFileRead(String path) { ////////////////////////////////
path = "/" + path; // Utils to return HTTP codes, and determine content-type
if (path.endsWith("/")) path += "index.html";
String contentType = getContentType(path); void replyOK()
String pathWithGz = path + ".gz"; {
if (FileFS.exists(pathWithGz) || FileFS.exists(path)) { HTTP.send(200, FPSTR(TEXT_PLAIN), "");
if (FileFS.exists(pathWithGz)) }
path += ".gz";
void replyOKWithMsg(String msg)
{
HTTP.send(200, FPSTR(TEXT_PLAIN), msg);
}
void replyNotFound(String msg)
{
HTTP.send(404, FPSTR(TEXT_PLAIN), msg);
}
void replyBadRequest(String msg)
{
// DBG_OUTPUT_PORT.println(msg);
HTTP.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n");
}
void replyServerError(String msg)
{
// DBG_OUTPUT_PORT.println(msg);
HTTP.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n");
}
/*
Return the FS type, status and size info
*/
void handleStatus()
{
// DBG_OUTPUT_PORT.println("handleStatus");
String json;
json.reserve(128);
json = "{\"type\":\"";
json += FS_NAME;
json += "\", \"isOk\":";
#ifdef ESP8266
FSInfo fs_info;
FileFS.info(fs_info);
json += F("\"true\", \"totalBytes\":\"");
json += fs_info.totalBytes;
json += F("\", \"usedBytes\":\"");
json += fs_info.usedBytes;
json += "\"";
#endif
#ifdef ESP32
json += F("\"true\", \"totalBytes\":\"");
json += String(FileFS.totalBytes());
json += F("\", \"usedBytes\":\"");
json += String(FileFS.usedBytes());
json += "\"";
#endif
json += F(",\"unsupportedFiles\":\"");
json += unsupportedFiles;
json += "\"}";
HTTP.send(200, "application/json", json);
}
#ifdef ESP32
String getContentType(String filename) {
if (HTTP.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
}
return "text/plain";
}
#endif
/*
Read the given file from the filesystem and stream it back to the client
*/
bool handleFileRead(String path)
{
// DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
if (path.endsWith("/"))
{
path += "index.html";
}
String contentType;
if (HTTP.hasArg("download"))
{
contentType = F("application/octet-stream");
}
else
{
#ifdef ESP32
contentType = getContentType(path);
#endif
#ifdef ESP8266
contentType = mime::getContentType(path);
#endif
}
if (!FileFS.exists(path))
{
// File not found, try gzip version
path = path + ".gz";
}
if (FileFS.exists(path))
{
File file = FileFS.open(path, "r"); File file = FileFS.open(path, "r");
if (contentType == "application/octet-stream") if (HTTP.streamFile(file, contentType) != file.size())
HTTP.sendHeader("Content-Disposition", "attachment;filename=" + (String)file.name()); {
HTTP.streamFile(file, contentType); // DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
file.close(); file.close();
return true; return true;
} }
return false; return false;
} }
String getContentType(String filename) { /*
if (HTTP.hasArg("download")) As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed,
return "application/octet-stream"; return the path of the closest parent still existing
else if (filename.endsWith(".htm")) */
return "text/html"; String lastExistingParent(String path)
else if (filename.endsWith(".html")) {
return "text/html"; while (!path.isEmpty() && !FileFS.exists(path))
else if (filename.endsWith(".txt")) {
return "text/plain"; if (path.lastIndexOf('/') > 0)
else if (filename.endsWith(".json")) {
return "application/json"; path = path.substring(0, path.lastIndexOf('/'));
else if (filename.endsWith(".css")) }
return "text/css"; else
else if (filename.endsWith(".js")) {
return "application/javascript"; path = String(); // No slash => the top folder does not exist
else if (filename.endsWith(".png")) }
return "image/png"; }
else if (filename.endsWith(".gif")) // DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path);
return "image/gif"; return path;
else if (filename.endsWith(".jpg"))
return "image/jpeg";
else if (filename.endsWith(".ico"))
return "image/x-icon";
else if (filename.endsWith(".xml"))
return "text/xml";
else if (filename.endsWith(".pdf"))
return "application/x-pdf";
else if (filename.endsWith(".zip"))
return "application/x-zip";
else if (filename.endsWith(".gz"))
return "application/x-gzip";
return "text/plain";
} }
// Здесь функции для работы с файловой системой /*
void handleFileUpload() { Handle a file upload request
if (HTTP.uri() != "/edit") return; */
HTTPUpload& upload = HTTP.upload(); void handleFileUpload()
if (upload.status == UPLOAD_FILE_START) { {
if (HTTP.uri() != "/edit")
{
return;
}
HTTPUpload &upload = HTTP.upload();
if (upload.status == UPLOAD_FILE_START)
{
String filename = upload.filename; String filename = upload.filename;
if (!filename.startsWith("/")) filename = "/" + filename; // Make sure paths always start with "/"
fsUploadFile = FileFS.open(filename, "w"); if (!filename.startsWith("/"))
filename = String(); {
} else if (upload.status == UPLOAD_FILE_WRITE) { filename = "/" + filename;
// Serial.print("handleFileUpload Data: "); Serial.println(upload.currentSize); }
if (fsUploadFile) // DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
fsUploadFile.write(upload.buf, upload.currentSize); uploadFile = FileFS.open(filename, "w");
} else if (upload.status == UPLOAD_FILE_END) { if (!uploadFile)
if (fsUploadFile) {
fsUploadFile.close(); return replyServerError(F("CREATE FAILED"));
}
// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
}
else if (upload.status == UPLOAD_FILE_WRITE)
{
if (uploadFile)
{
size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize);
if (bytesWritten != upload.currentSize)
{
return replyServerError(F("WRITE FAILED"));
}
}
// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
}
else if (upload.status == UPLOAD_FILE_END)
{
if (uploadFile)
{
uploadFile.close();
}
// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
} }
} }
void handleFileDelete() {
if (HTTP.args() == 0) return HTTP.send(500, "text/plain", "BAD ARGS"); #ifdef ESP8266
void deleteRecursive(String path)
{
File file = FileFS.open(path, "r");
bool isDir = file.isDirectory();
file.close();
// If it's a plain file, delete it
if (!isDir)
{
FileFS.remove(path);
return;
}
Dir dir = FileFS.openDir(path);
while (dir.next())
{
deleteRecursive(path + '/' + dir.fileName());
}
// Then delete the folder itself
FileFS.rmdir(path);
}
#endif
#ifdef ESP32
struct treename{
uint8_t type;
char *name;
};
void deleteRecursive( String path ){
fs::File dir = FileFS.open( path );
if(!dir.isDirectory()){
Serial.printf("%s is a file\n", path);
dir.close();
Serial.printf( "result of removing file %s: %d\n", path, FileFS.remove( path ) );
return;
}
Serial.printf("%s is a directory\n", path);
fs::File entry, nextentry;
while ( entry = dir.openNextFile() ){
if ( entry.isDirectory() ){
deleteRecursive( entry.path() );
} else{
String tmpname = path+"/"+strdup( entry.name() ); // buffer file name
entry.close();
Serial.printf( "result of removing file %s: %d\n", tmpname, FileFS.remove( tmpname ) );
}
}
dir.close();
Serial.printf( "result of removing directory %s: %d\n", path, FileFS.rmdir( path ) );
}
#endif
/*
Handle a file deletion request
Operation | req.responseText
---------------+--------------------------------------------------------------
Delete file | parent of deleted file, or remaining ancestor
Delete folder | parent of deleted folder, or remaining ancestor
*/
void handleFileDelete()
{
String path = HTTP.arg(0); String path = HTTP.arg(0);
if (path == "/") if (path.isEmpty() || path == "/")
return HTTP.send(500, "text/plain", "BAD PATH"); {
return replyBadRequest("BAD PATH");
}
// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
if (!FileFS.exists(path)) if (!FileFS.exists(path))
return HTTP.send(404, "text/plain", "FileNotFound"); {
FileFS.remove(path); return replyNotFound(FPSTR(FILE_NOT_FOUND));
HTTP.send(200, "text/plain", ""); }
path = String(); deleteRecursive(path);
replyOKWithMsg(lastExistingParent(path));
} }
void handleFileCreate() { /*
if (HTTP.args() == 0) Handle the creation/rename of a new file
return HTTP.send(500, "text/plain", "BAD ARGS"); Operation | req.responseText
String path = HTTP.arg(0); ---------------+--------------------------------------------------------------
Create file | parent of created file
Create folder | parent of created folder
Rename file | parent of source file
Move file | parent of source file, or remaining ancestor
Rename folder | parent of source folder
Move folder | parent of source folder, or remaining ancestor
*/
void handleFileCreate()
{
String path = HTTP.arg("path");
if (path.isEmpty())
{
return replyBadRequest(F("PATH ARG MISSING"));
}
#ifdef USE_SPIFFS
if (checkForUnsupportedPath(path).length() > 0)
{
return replyServerError(F("INVALID FILENAME"));
}
#endif
if (path == "/") if (path == "/")
return HTTP.send(500, "text/plain", "BAD PATH"); {
return replyBadRequest("BAD PATH");
}
if (FileFS.exists(path)) if (FileFS.exists(path))
return HTTP.send(500, "text/plain", "FILE EXISTS"); {
File file = FileFS.open(path, "w"); return replyBadRequest(F("PATH FILE EXISTS"));
if (file) }
file.close();
String src = HTTP.arg("src");
if (src.isEmpty())
{
// No source specified: creation
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
if (path.endsWith("/"))
{
// Create a folder
path.remove(path.length() - 1);
if (!FileFS.mkdir(path))
{
return replyServerError(F("MKDIR FAILED"));
}
}
else
{
// Create a file
File file = FileFS.open(path, "w");
if (file)
{
#ifdef ESP8266
file.write((const char *)0);
#endif
#ifdef ESP32
file.write(0);
#endif
file.close();
}
else
{
return replyServerError(F("CREATE FAILED"));
}
}
if (path.lastIndexOf('/') > -1)
{
path = path.substring(0, path.lastIndexOf('/'));
}
replyOKWithMsg(path);
}
else else
return HTTP.send(500, "text/plain", "CREATE FAILED"); {
HTTP.send(200, "text/plain", ""); // Source specified: rename
path = String(); if (src == "/")
{
return replyBadRequest("BAD SRC");
}
if (!FileFS.exists(src))
{
return replyBadRequest(F("SRC FILE NOT FOUND"));
}
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
if (path.endsWith("/"))
{
path.remove(path.length() - 1);
}
if (src.endsWith("/"))
{
src.remove(src.length() - 1);
}
if (!FileFS.rename(src, path))
{
return replyServerError(F("RENAME FAILED"));
}
replyOKWithMsg(lastExistingParent(src));
}
} }
void handleFileList() { /*
File dir = FileFS.open("/", "r"); Return the list of files in the directory specified by the "dir" query string parameter.
String output = "["; Also demonstrates the use of chunked responses.
File entry; */
while (entry = dir.openNextFile()) { #ifdef ESP8266
if (output != "[") output += ','; void handleFileList()
bool isDir = entry.isDirectory(); {
if (!HTTP.hasArg("dir"))
{
return replyBadRequest(F("DIR ARG MISSING"));
}
String path = HTTP.arg("dir");
if (path != "/" && !FileFS.exists(path))
{
return replyBadRequest("BAD PATH");
}
// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
Dir dir = FileFS.openDir(path);
path.clear();
// use HTTP/1.1 Chunked response to avoid building a huge temporary string
if (!HTTP.chunkedResponseModeStart(200, "text/json"))
{
HTTP.send(505, F("text/html"), F("HTTP1.1 required"));
return;
}
// use the same string for every line
String output;
output.reserve(64);
while (dir.next())
{
#ifdef USE_SPIFFS
String error = checkForUnsupportedPath(dir.fileName());
if (error.length() > 0)
{
// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName());
continue;
}
#endif
if (output.length())
{
// send string from previous iteration
// as an HTTP chunk
HTTP.sendContent(output);
output = ',';
}
else
{
output = '[';
}
output += "{\"type\":\""; output += "{\"type\":\"";
output += (isDir) ? "dir" : "file"; if (dir.isDirectory())
output += "\",\"name\":\""; {
output += String(entry.name()); output += "dir";
}
else
{
output += F("file\",\"size\":\"");
output += dir.fileSize();
}
output += F("\",\"name\":\"");
// Always return names without leading "/"
if (dir.fileName()[0] == '/')
{
output += &(dir.fileName()[1]);
}
else
{
output += dir.fileName();
}
output += "\"}"; output += "\"}";
entry.close();
} }
// send last string
output += "]"; output += "]";
//Serial.println(output); HTTP.sendContent(output);
HTTP.send(200, "text/json", output); HTTP.chunkedResponseFinalize();
}
#endif
#ifdef ESP32
void handleFileList() {
if (!HTTP.hasArg("dir")) {
HTTP.send(500, "text/plain", "BAD ARGS");
return;
}
String path = HTTP.arg("dir");
// DBG_OUTPUT_PORT.println("handleFileList: " + path);
File root = FileFS.open(path);
path = String();
String output = "[";
if(root.isDirectory()){
File file = root.openNextFile();
while(file){
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
// output += (file.isDirectory()) ? "dir" : "file";
if (file.isDirectory())
{
output += "dir";
}
else
{
output += F("file\",\"size\":\"");
output += file.size();
}
output += "\",\"name\":\"";
output += String(file.name());
output += "\"}";
file = root.openNextFile();
}
}
output += "]";
HTTP.send(200, "text/json", output);
}
#endif
/*
The "Not Found" handler catches all URI not explicitly declared in code
First try to find and return the requested file from the filesystem,
and if it fails, return a 404 page with debug information
*/
void handleNotFound()
{
#ifdef ESP8266
String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
#endif
#ifdef ESP32
String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
#endif
if (handleFileRead(uri))
{
return;
}
// Dump debug data
String message;
message.reserve(100);
message = F("Error: File not found\n\nURI: ");
message += uri;
message += F("\nMethod: ");
message += (HTTP.method() == HTTP_GET) ? "GET" : "POST";
message += F("\nArguments: ");
message += HTTP.args();
message += '\n';
for (uint8_t i = 0; i < HTTP.args(); i++)
{
message += F(" NAME:");
message += HTTP.argName(i);
message += F("\n VALUE:");
message += HTTP.arg(i);
message += '\n';
}
message += "path=";
message += HTTP.arg("path");
message += '\n';
// DBG_OUTPUT_PORT.print(message);
return replyNotFound(message);
} }
void printDirectory(File dir, String& out) { /*
while (true) { This specific handler returns the index.htm (or a gzipped version) from the /edit folder.
File entry = dir.openNextFile(); If the file is not present but the flag INCLUDE_FALLBACK_INDEX_HTM has been set, falls back to the version
if (!entry) { embedded in the program code.
break; Otherwise, fails with a 404 page with debug information
} */
if (entry.isDirectory()) { void handleGetEdit()
out += entry.name(); {
out += "/"; if (handleFileRead(F("/edit.htm")))
printDirectory(entry, out); {
} else { return;
out += entry.name();
out += "\r\n";
}
} }
#ifdef INCLUDE_FALLBACK_INDEX_HTM
server.sendHeader(F("Content-Encoding"), "gzip");
server.send(200, "text/html", index_htm_gz, index_htm_gz_len);
#else
replyNotFound(FPSTR(FILE_NOT_FOUND));
#endif
} }
#endif #endif

View File

@@ -14,6 +14,7 @@ void* getAPI_Bme280(String subtype, String params);
void* getAPI_Bmp280(String subtype, String params); void* getAPI_Bmp280(String subtype, String params);
void* getAPI_Dht1122(String subtype, String params); void* getAPI_Dht1122(String subtype, String params);
void* getAPI_Ds18b20(String subtype, String params); void* getAPI_Ds18b20(String subtype, String params);
void* getAPI_Impulse(String subtype, String params);
void* getAPI_Pzem004(String subtype, String params); void* getAPI_Pzem004(String subtype, String params);
void* getAPI_RTC(String subtype, String params); void* getAPI_RTC(String subtype, String params);
void* getAPI_Sht20(String subtype, String params); void* getAPI_Sht20(String subtype, String params);
@@ -47,6 +48,7 @@ if ((tmpAPI = getAPI_Bme280(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Bmp280(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Bmp280(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Dht1122(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Dht1122(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Ds18b20(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Ds18b20(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Impulse(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Pzem004(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Pzem004(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_RTC(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_RTC(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Sht20(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Sht20(subtype, params)) != nullptr) return tmpAPI;

View File

@@ -6,23 +6,22 @@ extern IoTGpio IoTgpio;
class Multitouch : public IoTItem class Multitouch : public IoTItem
{ {
private: private:
int _pin;
int _int; int _int;
int _inv;
String _pinMode;
int _lastButtonState = LOW;
unsigned long _lastDebounceTime = 0;
unsigned long timing; unsigned long timing;
long _debounceDelay = 50;
long _PWMDelay = 500; long _PWMDelay = 500;
int _buttonState;
int _reading;
int _count = 0; int _count = 0;
int duration = 0; int duration = 0;
int _pin;
bool _execLevel, _fixState, _inv, _buttonState, _reading;
bool _lastButtonState = LOW;
unsigned long _lastDebounceTime = 0;
int _debounceDelay = 50;
public: public:
Multitouch(String parameters) : IoTItem(parameters) Multitouch(String parameters) : IoTItem(parameters)
{ {
String _pinMode;
jsonRead(parameters, "pin", _pin); jsonRead(parameters, "pin", _pin);
jsonRead(parameters, "pinMode", _pinMode); jsonRead(parameters, "pinMode", _pinMode);
jsonRead(parameters, "debounceDelay", _debounceDelay); jsonRead(parameters, "debounceDelay", _debounceDelay);
@@ -31,15 +30,20 @@ public:
jsonRead(parameters, "inv", _inv); jsonRead(parameters, "inv", _inv);
_round = 0; _round = 0;
IoTgpio.pinMode(_pin, INPUT); if (_pinMode == F("INPUT"))
if (_pinMode == "INPUT_PULLUP") IoTgpio.pinMode(_pin, INPUT);
IoTgpio.digitalWrite(_pin, HIGH); else if (_pinMode == F("INPUT_PULLUP"))
else if (_pinMode == "INPUT_PULLDOWN") IoTgpio.pinMode(_pin, INPUT_PULLUP);
else if (_pinMode == F("INPUT_PULLDOWN"))
{
IoTgpio.pinMode(_pin, INPUT);
IoTgpio.digitalWrite(_pin, LOW); IoTgpio.digitalWrite(_pin, LOW);
}
value.valD = _buttonState = IoTgpio.digitalRead(_pin); value.valD = _buttonState = IoTgpio.digitalRead(_pin);
// сообщаем всем о стартовом статусе без генерации события // сообщаем всем о стартовом статусе без генерации события
regEvent(_buttonState, "", false, false); regEvent(_buttonState, "", false, false);
SerialPrint("I", F("Multitouch"), "_buttonState " + String(_buttonState));
} }
void loop() void loop()
@@ -49,35 +53,34 @@ public:
{ {
_lastDebounceTime = millis(); _lastDebounceTime = millis();
} }
if ((millis() - _lastDebounceTime) > _debounceDelay) if ((millis() - _lastDebounceTime) > _debounceDelay)
{ {
if (millis() - timing > _int && _reading == _inv && millis() - _lastDebounceTime > _PWMDelay)
{
timing = millis();
duration = millis() - _lastDebounceTime - _PWMDelay;
value.valD = duration / 50;
regEvent(value.valD, "Multitouch");
_count = -1;
}
if (_reading != _buttonState) if (_reading != _buttonState)
{ {
_buttonState = _reading;
_count++; _count++;
duration = 0; duration = 0;
_buttonState = _reading;
} }
if (1 < _count && millis() > _lastDebounceTime + _PWMDelay) if (1 < _count && millis() > _lastDebounceTime + _PWMDelay)
{ {
value.valD = _count / 2; value.valD = _count / 2;
regEvent(value.valD, "Multitouch"); regEvent(value.valD, F("Multitouch"));
_count = 0; _count = 0;
} }
if (millis() - timing > _int && _reading == _inv && millis() - _lastDebounceTime > _PWMDelay)
{
SerialPrint("I", F("Multitouch"), "Считаем задержку");
timing = millis();
duration = millis() - _lastDebounceTime - _PWMDelay;
value.valD = duration / _int + 1;
regEvent(value.valD, F("Multitouch"));
_count = -1;
}
} }
_lastButtonState = _reading; _lastButtonState = _reading;
} }
~Multitouch(){}; ~Multitouch(){};
}; };

View File

@@ -25,7 +25,7 @@
"authorGit": "https://github.com/avaksru", "authorGit": "https://github.com/avaksru",
"specialThanks": "", "specialThanks": "",
"moduleName": "Multitouch", "moduleName": "Multitouch",
"moduleVersion": "1.0", "moduleVersion": "2.0",
"usedRam": { "usedRam": {
"esp32_4mb": 15, "esp32_4mb": 15,
"esp8266_4mb": 15 "esp8266_4mb": 15
@@ -48,8 +48,6 @@
"esp8266_1mb": [], "esp8266_1mb": [],
"esp8266_1mb_ota": [], "esp8266_1mb_ota": [],
"esp8285_1mb": [], "esp8285_1mb": [],
"esp8285_1mb_ota": [], "esp8285_1mb_ota": []
"esp8266_2mb": [],
"esp8266_2mb_ota": []
} }
} }

View File

@@ -0,0 +1,99 @@
#include "Global.h"
#include "classes/IoTItem.h"
#include "modules/sensors/UART/Uart.h"
#define READ_TIMEOUT 100
class A02Distance : public IoTItem
{
private:
public:
A02Distance(String parameters) : IoTItem(parameters)
{
if (myUART)
{
}
}
//Периодическое выполнение программы, в int секунд, которые зададим в конфигурации
void doByInterval()
{
if (myUART)
{
static uint8_t data[4];
if (recieve(data, 4) == 4)
{
if (data[0] == 0xff)
{
int sum;
sum = (data[0] + data[1] + data[2]) & 0x00FF;
if (sum == data[3])
{
float distance = (data[1] << 8) + data[2];
if (distance > 30)
{
value.valD = distance / 10;
//SerialPrint("i", F("A02Distance"), "distance = " + String(value.valD) + "cm");
regEvent(value.valD, "A02Distance");
}
else
{
SerialPrint("E", "A02Distance", "Below the lower limit");
regEvent(NAN, "A02Distance");
}
}
else
{
regEvent(NAN, "A02Distance");
SerialPrint("E", "A02Distance", "Distance data error");
}
}
} else {
regEvent(NAN, "A02Distance");
SerialPrint("E", "A02Distance", "Recieve data error");
}
} else
{
regEvent(NAN, "A02Distance");
SerialPrint("E", "A02Distance", "Not find UART");
}
}
//Приём данных из COM порта
uint16_t recieve(uint8_t *resp, uint16_t len)
{
((SoftwareSerial *)myUART)->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 (myUART->available() > 0)
{
uint8_t c = (uint8_t)myUART->read();
resp[index++] = c;
}
}
return index;
}
~A02Distance(){};
};
//Функиця ядра, чтобы нашел наш модуль
void *getAPI_A02Distance(String subtype, String param)
{
if (subtype == F("A02Distance"))
{
return new A02Distance(param);
}
else
{
return nullptr;
}
}

View File

@@ -0,0 +1,40 @@
{
"menuSection": "Сенсоры",
"configItem": [
{
"name": "A02 Дальность",
"type": "Reading",
"subtype": "A02Distance",
"id": "dist",
"widget": "anydataCm",
"page": "Сенсоры",
"descr": "Дальность",
"int": 5,
"round": 1
}
],
"about": {
"authorName": "Bubnov Mikhail",
"authorContact": "https://t.me/Mitchel",
"authorGit": "https://github.com/Mit4el",
"exampleURL": "https://iotmanager.org/wiki",
"specialThanks": "",
"moduleName": "A02Distance",
"moduleVersion": "0.1",
"moduleDesc": "A0221AU, A02YYUW Ультразвуковой датчик. Позволяет получить дальность с ультрозвуковых датчиков A0221AU, A02YYUW",
"propInfo": {
"int": "Количество секунд между опросами датчика."
}
},
"defActive": true,
"usedLibs": {
"esp32_4mb": [],
"esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],
"esp8285_1mb": [],
"esp8285_1mb_ota": [],
"esp8266_2mb": [],
"esp8266_2mb_ota": []
}
}

View File

@@ -3,6 +3,14 @@
extern IoTGpio IoTgpio; extern IoTGpio IoTgpio;
#ifdef ESP32
#define SC_ADC 4095. //Scale ADC
#else
#define SC_ADC 1023. //Scale ADC
#endif
#define DEF_NAN 50 //количество отсчетов АПЦ которые мы принимаем за отсутствие датчика (в идеале 0, но все равно Acs712 выдает минимум 0,5В)
class Acs712 : public IoTItem class Acs712 : public IoTItem
{ {
private: private:
@@ -10,56 +18,97 @@ private:
const unsigned long _sampleTime = 100000UL; // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains 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 _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 const unsigned long _sampleInterval = _sampleTime / _numSamples; // the sampling interval, must be longer than then ADC conversion time
int _adc_zero1; //Переменная автоматической калибровки int _adc_zero1; // Переменная автоматической калибровки
int _fl_rms; // 1 - подсчет средне-квадратического тока (переменный), 0 - подсчет средне-арифмитического тока (постоянный)
int _sens = 100; //Чувствительность датчика тока: 5A = 185mВ/A, 20A = 100mВ/A, 30A = 66mВ/A
int _vref; //"Vref (мВ) - Опороное наряжение питания Acs712, по умолчанию = 5000мВ",
float k ; //Чувствительность(разрешение) Acs712 по току, сколько тока в одном отсчете АЦП, k=VACP/sens (30А->74mА, 20A->49mA, 5A->26mA для esp8266)
bool f_nan = false; //Флаг отсутствия входа на АЦП
float vacp; //Напряжение в мВ для смещения одного разряда АЦП esp8266 = 4.887, esp32 = 1.221; vacp = vcc*1000/1023
public: public:
Acs712(String parameters) : IoTItem(parameters) Acs712(String parameters) : IoTItem(parameters)
{ {
String tmp; String tmp;
jsonRead(parameters, "pin", tmp); jsonRead(parameters, "pin", tmp);
_pin = tmp.toInt(); _pin = tmp.toInt();
_adc_zero1 = determineVQ(_pin); jsonRead(parameters, "adczero", tmp);
_adc_zero1 = tmp.toInt(); // determineVQ(_pin);
jsonRead(parameters, "rms", tmp);
_fl_rms = tmp.toInt();
jsonRead(parameters, "sens", tmp);
_sens = tmp.toInt();
jsonRead(parameters, "vref", tmp);
_vref = tmp.toInt();
vacp = _vref/SC_ADC;
k = vacp / (float)_sens; //коэффециент для домножения измерений АЦП
} }
void doByInterval() void doByInterval()
{ {
f_nan = false;
unsigned long currentAcc = 0; unsigned long currentAcc = 0;
unsigned int count = 0; unsigned int count = 0;
unsigned long prevMicros = micros() - _sampleInterval; unsigned long prevMicros = micros() - _sampleInterval;
while (count < _numSamples) while (count < _numSamples)
{
if (micros() - prevMicros >= _sampleInterval)
{ {
if (micros() - prevMicros >= _sampleInterval) int adc_raw = IoTgpio.analogRead(_pin);
{ if (adc_raw > DEF_NAN) f_nan = true; //Если за цикл измерений не было АЦП больше 50, то считаем что нет датчика
int adc_raw = IoTgpio.analogRead(_pin) - _adc_zero1; adc_raw -= _adc_zero1;
if (_fl_rms == 0)
currentAcc += (unsigned long)abs(adc_raw);
else
currentAcc += (unsigned long)(adc_raw * adc_raw); currentAcc += (unsigned long)(adc_raw * adc_raw);
++count; ++count;
prevMicros += _sampleInterval; prevMicros += _sampleInterval;
}
} }
#ifdef ESP32 }
value.valD = int(sqrt((float)currentAcc / (float)_numSamples) * (75.7576 / 4095.0));
#else if (_fl_rms == 0)
value.valD = int(sqrt((float)currentAcc / (float)_numSamples) * (75.7576 / 1023.0)); {
#endif #ifdef ESP32
value.valD = ((float)currentAcc / (float)_numSamples) * k;
#else
value.valD = ((float)currentAcc / (float)_numSamples) * k;
#endif
}
else
{
#ifdef ESP32
value.valD = (sqrt((float)currentAcc / (float)_numSamples) * k);
#else
value.valD = (sqrt((float)currentAcc / (float)_numSamples) * k);
#endif
}
if (f_nan)
regEvent(value.valD, "Acs712"); regEvent(value.valD, "Acs712");
else
regEvent(NAN, "Acs712");
}
void onModuleOrder(String &key, String &value)
{
if (key == "setZero")
{
_adc_zero1 = determineVQ(_pin);
SerialPrint("i", F("Acs712"), "User run calibration ADC zero: " + String(_adc_zero1));
// TODO wtitejson to config.json?????
}
} }
int determineVQ(int PIN) int determineVQ(int PIN)
{ {
long VQ = 0; long VQ = 0;
// read 5000 samples to stabilise value for (int i = 0; i < 100; i++)
for (int i = 0; i < 5000; i++)
{ {
VQ += IoTgpio.analogRead(PIN); VQ += IoTgpio.analogRead(PIN);
//delay(1); // depends on sampling (on filter capacitor), can be 1/80000 (80kHz) max.
} }
VQ /= 5000; VQ /= 100;
return int(VQ); return int(VQ);
} }
~Acs712(){}; ~Acs712(){};
}; };

View File

@@ -11,26 +11,42 @@
"descr": "Ток", "descr": "Ток",
"round": 3, "round": 3,
"pin": 39, "pin": 39,
"int": 5 "int": 5,
"rms": 1,
"vref": 5000,
"sens": 100,
"adczero" : 512,
"btn-setZero": "nil"
} }
], ],
"about": { "about": {
"authorName": "Yuriy Kuneev", "authorName": "Bubnov Mikhail",
"authorContact": "https://t.me/Kuneev07", "authorContact": "https://t.me/Mitchel",
"authorGit": "", "authorGit": "https://github.com/Mit4el",
"exampleURL": "https://iotmanager.org/wiki", "exampleURL": "https://iotmanager.org/wiki",
"specialThanks": "", "specialThanks": "",
"moduleName": "Acs712", "moduleName": "Acs712",
"moduleVersion": "1.0", "moduleVersion": "2.0",
"moduleDesc": "Позволяет получить текущее значение тока на аналоговом пине с помощью модуля Acs712.", "moduleDesc": "Позволяет получить текущее значение тока на аналоговом пине с помощью модуля Acs712. Не забываем про делитель для входа на АЦП/",
"propInfo": { "propInfo": {
"pin": "Аналоговый GPIO номер, к которому подключен датчик.", "pin": "Аналоговый GPIO номер, к которому подключен датчик. Для esp8266 0",
"int": "Количество секунд между опросами датчика." "int": "Количество секунд между опросами датчика.",
"rms": "1 - подсчет средне-квадратического тока (переменный), 0 - подсчет средне-арифмитического тока (постоянный)",
"vref": "Vref (мВ) - Опороное наряжение питания Acs712, по умолчанию = 5000мВ",
"sens": "Чувствительность датчика тока: 5A = 185mВ/A , 20A = 100mВ/A , 30A = 66mВ/A ",
"adczero" : "Переменная калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Для ESP8266 - 512, Для ESP32 -2048, это 2.5В = 0А (1,65 с делителем) для Acs712 20A и 30A при стабильном токе 5В",
"btn-setZero": "Кнопка калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Нагрузка в момент калибровки должна быть отключена! После перезагрузки будет установлено в значение по умолчанию adczero. Для сохранение смотрим лог, и изменияем adczero"
} }
}, },
"defActive": true, "defActive": true,
"usedLibs": { "usedLibs": {
"esp32_4mb": [], "esp32_4mb": [],
"esp8266_4mb": [] "esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],
"esp8285_1mb": [],
"esp8285_1mb_ota": [],
"esp8266_2mb": [],
"esp8266_2mb_ota": []
} }
} }

View File

@@ -0,0 +1,87 @@
#include "Global.h"
#include "classes/IoTItem.h"
extern IoTGpio IoTgpio;
class Impulse : public IoTItem
{
private:
int _int;
int _pin;
bool _buttonState, _reading;
bool _lastButtonState = LOW;
unsigned long _lastDebounceTime = 0;
int _debounceDelay = 50;
int _count = 0;
unsigned long timing;
public:
Impulse(String parameters) : IoTItem(parameters)
{
String _pinMode;
jsonRead(parameters, F("pin"), _pin);
jsonRead(parameters, F("pinMode"), _pinMode);
jsonRead(parameters, F("debounceDelay"), _debounceDelay);
jsonRead(parameters, "int", _int);
_round = 0;
if (_pinMode == F("INPUT"))
IoTgpio.pinMode(_pin, INPUT);
else if (_pinMode == F("INPUT_PULLUP"))
IoTgpio.pinMode(_pin, INPUT_PULLUP);
else if (_pinMode == F("INPUT_PULLDOWN"))
{
IoTgpio.pinMode(_pin, INPUT);
IoTgpio.digitalWrite(_pin, LOW);
}
value.valD = _buttonState = IoTgpio.digitalRead(_pin);
regEvent(_buttonState, "", false, false);
}
void loop()
{
_reading = IoTgpio.digitalRead(_pin);
if (_reading != _lastButtonState)
{
_lastDebounceTime = millis();
}
if ((millis() - _lastDebounceTime) > _debounceDelay)
{
if (_reading != _buttonState)
{
_buttonState = _reading;
_count++;
}
if (_count == 1)
{
timing = millis();
}
if (millis() - timing > _int * 1000 && _count > 1)
{
timing = millis();
value.valD = _count;
regEvent(value.valD, F("Impulse"));
_count = 0;
}
}
_lastButtonState = _reading;
}
~Impulse(){};
};
void *getAPI_Impulse(String subtype, String param)
{
if (subtype == F("Impulse"))
{
return new Impulse(param);
}
else
{
return nullptr;
}
}

View File

@@ -0,0 +1,50 @@
{
"menuSection": "Сенсоры",
"configItem": [
{
"global": 0,
"name": "Аналоговый счетчик импульсов",
"type": "Writing",
"subtype": "Impulse",
"id": "impulse",
"widget": "anydataDef",
"page": "Счетчики",
"descr": "Импульсов",
"needSave": 0,
"int": 1,
"pin": 16,
"pinMode": "INPUT",
"debounceDelay": 3,
"multiply": 1
}
],
"about": {
"authorName": "AVAKS",
"authorContact": "https://t.me/@avaks_dev",
"authorGit": "https://github.com/avaksru",
"specialThanks": "",
"moduleName": "Impulse",
"moduleVersion": "2.0",
"usedRam": {
"esp32_4mb": 15,
"esp8266_4mb": 15
},
"title": "Аналоговый счетчик импульсов. Датчик Холла ",
"moduleDesc": "Считает количество импульсов за период времени",
"propInfo": {
"int": "Период сбора импульсов в секундах",
"pin": "Укажите GPIO номер пина для чтения импульсов",
"pinMode": "Может быть INPUT_PULLUP INPUT_PULLDOWN INPUT",
"debounceDelay": "Время обработки дребезга (миллисекунд)"
}
},
"defActive": true,
"usedLibs": {
"esp32_4mb": [],
"esp8266_4mb": [],
"esp8266_1mb": [],
"esp8266_1mb_ota": [],
"esp8285_1mb": [],
"esp8285_1mb_ota": []
}
}

View File

@@ -119,7 +119,7 @@
"moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры", "moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры",
"propInfo": { "propInfo": {
"addr": "Адрес modbus", "addr": "Адрес modbus",
"int": "Количество секунд между опросами датчика. Желателно устанавливать разные интервалы для параметров что бы опросы происходили в разное время.", "int": "Количество секунд между опросами датчика. Желателно устанавливать одинаковые интервалы для параметров (для одного адреса Pzem) что опрос происходил один раз, остальные из 500мс буфера.",
"changeaddr": "Поставьте этот параметр равным 1 и перезагрузите esp - будет установлен адрес указанный в setaddr. Смотрите в логе результат: [i] Pzem address set: 0x01", "changeaddr": "Поставьте этот параметр равным 1 и перезагрузите esp - будет установлен адрес указанный в setaddr. Смотрите в логе результат: [i] Pzem address set: 0x01",
"setaddr": "Новый адрес который нужно назначить", "setaddr": "Новый адрес который нужно назначить",
"reset": "Поставьте этот параметр равным 1 и pzem будет сброшен к нулю. Смотрите в логе результат: [i] Pzem reset done" "reset": "Поставьте этот параметр равным 1 и pzem будет сброшен к нулю. Смотрите в логе результат: [i] Pzem reset done"

View File

@@ -124,6 +124,8 @@ const String readFile(const String& filename, size_t max_size) {
size_t size = file.size(); size_t size = file.size();
if (size > max_size) { if (size > max_size) {
file.close(); file.close();
if (path == "/config.json")
return "[]";
return "large"; return "large";
} }
String temp = file.readString(); String temp = file.readString();