diff --git a/.gitignore b/.gitignore index d74a9041..b9f3806a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .pio .vscode -/myProfile_wm.json diff --git a/PrepareProject.py b/PrepareProject.py index d29c8da4..55b4e383 100644 --- a/PrepareProject.py +++ b/PrepareProject.py @@ -15,7 +15,11 @@ # # поддерживаемые контроллеры (профили): # esp8266_4mb +# esp8266_16mb # esp32_4mb +# esp32cam_4mb +# esp32_16mb +# esp32s2_4mb # esp8266_1mb # esp8266_1mb_ota # esp8285_1mb @@ -23,10 +27,12 @@ # esp8266_2mb # esp8266_2mb_ota + import configparser import os, json, sys, getopt from pathlib import Path import shutil +from distutils.dir_util import copy_tree config = configparser.ConfigParser() # создаём объекта парсера INI @@ -120,23 +126,19 @@ else: # определяем какое устройство используется в профиле deviceName = profJson['projectProp']['platformio']['default_envs'] -# назначаем папку с файлами прошивки в зависимости от устройства и запоминаем в профиле -dataDir = 'data_svelte' +# заполняем папку /data файлами прошивки в зависимости от устройства if deviceName == 'esp8266_1mb_ota' or deviceName == 'esp8285_1mb_ota' or deviceName == 'esp8266_2mb_ota': - dataDir = 'data_svelte_lite' -profJson['projectProp'] = { - 'platformio': { - 'data_dir': dataDir - } -} + copy_tree("data_lite", "data_svelte") +else: + copy_tree("data_full", "data_svelte") # генерируем файлы проекта на основе подготовленного профиля # заполняем конфигурационный файл прошивки параметрами из профиля -with open(dataDir + "/settings.json", "r", encoding='utf-8') as read_file: +with open("data_svelte/settings.json", "r", encoding='utf-8') as read_file: iotmJson = json.load(read_file) for key, value in profJson['iotmSettings'].items(): iotmJson[key] = value -with open(dataDir + "/settings.json", "w", encoding='utf-8') as write_file: +with open("data_svelte/settings.json", "w", encoding='utf-8') as write_file: json.dump(iotmJson, write_file, ensure_ascii=False, indent=4, sort_keys=False) @@ -165,7 +167,7 @@ for section, modules in profJson['modules'].items(): configItemsJson['name'] = str(itemsCount) + ". " + configItemsJson['name'] itemsCount = itemsCount + 1 itemsJson.append(configItemsJson) -with open(dataDir + "/items.json", "w", encoding='utf-8') as write_file: +with open("data_svelte/items.json", "w", encoding='utf-8') as write_file: json.dump(itemsJson, write_file, ensure_ascii=False, indent=4, sort_keys=False) @@ -200,14 +202,23 @@ config.read("platformio.ini") config["env:" + deviceName + "_fromitems"]["lib_deps"] = allLibs config["env:" + deviceName + "_fromitems"]["build_src_filter"] = includeDirs config["platformio"]["default_envs"] = deviceName -config["platformio"]["data_dir"] = profJson['projectProp']['platformio']['data_dir'] +# config["platformio"]["data_dir"] = profJson['projectProp']['platformio']['data_dir'] with open("platformio.ini", 'w') as configFile: config.write(configFile) -# сохраняем применяемый профиль в папку data_svelte для загрузки на контроллер и дальнейшего переиспользования -print(f"Сохраняем профиль {profile} в {dataDir}") -shutil.copy(profile, dataDir + "/" + profile) - + +# сохраняем часть применяемого профиля в папку data_svelte для загрузки на контроллер и дальнейшего переиспользования +print(f"Saving profile {profile} in /data_svelte/flashProfile.json") +shortProfJson = json.loads('{}') +shortProfJson['projectProp'] = { + 'platformio': { + 'default_envs': deviceName + } + } +shortProfJson['modules'] = profJson['modules'] +with open("data_svelte/flashProfile.json", "w", encoding='utf-8') as write_file: + json.dump(shortProfJson, write_file, ensure_ascii=False, indent=4, sort_keys=False) + # import ctypes # An included library with Python install. # if update: @@ -216,10 +227,11 @@ shutil.copy(profile, dataDir + "/" + profile) # ctypes.windll.user32.MessageBoxW(0, "Профиль " + profile + " применен, можно запускать компиляцию и прошивку.", "Операция завершена.", 0) if update: - print(f"\x1b[1;31;42m Модули профиля " + profile + " обновлены, а сам профиль применен, можно запускать компиляцию и прошивку.\x1b[0m") + shutil.copy(profile, "compilerProfile.json") + print(f"\x1b[1;31;42m Profile modules " + profile + " updated, profile applied, you can run compilation and firmware.\x1b[0m") else: - print(f"\x1b[1;31;42m Профиль ", profile, " применен, можно запускать компиляцию и прошивку.\x1b[0m") + print(f"\x1b[1;31;42m Profile ", profile, " applied, you can run compilation and firmware.\x1b[0m") # print(f"\x1b[1;32;41m Операция завершена. \x1b[0m") diff --git a/README.md b/README.md index 99208207..f2988612 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # IoTManager -Это модульная система беспроводной автоматизации на базе ESP32/ESP8266 микроконтроллеров и приложения IoT Manager. -Телеграм канал обсуждения приложения и системы автоматизации https://t.me/IoTmanager -# [Инструкция](https://github.com/IoTManagerProject/IoTManager/wiki) -![](https://github.com/IoTManagerProject/IoTManager/blob/beta/doc/pictures/007%20iot%20manager.jpg) +This is a smart home based on esp8266 and esp32 microcontrollers. These microcontrollers gained their popularity due to their low cost. Each such microcontroller is able to connect to your home wifi router. They can be purchased at any robotics store or on aliexpress, there are also ready-made devices based on them. This microcontroller has a certain number of pins on which digital signals are generated. Various peripheral devices can be connected to it: sensors, relays, stepper motors, servo drives, etc. + +Our firmware allows you to receive data from all these devices and manage them. The iot manager app available for ios and android is used to display the data. In order to connect devices and the application, a special mqtt server is needed, in other words, an mqtt broker. All devices are first connected to a wifi router, and then to this mqtt broker, the application is also connected to it. As a result, through the application you can manage devices from anywhere in the world, monitor sensor readings, build graphs and much more. Broker mqtt can be used in the cloud, such as wqtt.ru, or your own, raised, for example, on a single-board computer raspberry pi. There is also a second way to manage devices, it works even when you do not have the Internet - control through a web browser. All your devices will be available on one page. Both methods, through the application or through the web, work simultaneously with full mutual synchronization. + +To achieve your goal, you only need three things: + +1. Buy an esp microcontroller +2. Download the app +3. Get a cloud broker + +If remote control and the application are not needed, then the last step can be omitted. + +The logic of each device is configured using scripts. They are needed in order to teach the device to carry out your invented algorithms. You can assign any reaction to any action. The temperature has risen - the device will turn off the heater. Humidity has fallen and the level in the tank is more than 10% - the device will start watering, if not, it will send you a telegram notification that there is not enough water. These are just a few examples. Scenarios are created by you, and their flexibility will allow you to fulfill your every desire. diff --git a/bin/esp32bootloader/boot_app0.bin b/bin/esp32bootloader/boot_app0.bin new file mode 100644 index 00000000..13562cab Binary files /dev/null and b/bin/esp32bootloader/boot_app0.bin differ diff --git a/bin/esp32bootloader/bootloader_qio_80m.bin b/bin/esp32bootloader/bootloader_qio_80m.bin new file mode 100644 index 00000000..944f4947 Binary files /dev/null and b/bin/esp32bootloader/bootloader_qio_80m.bin differ diff --git a/data_svelte_lite/myProfile.json b/compilerProfile.json similarity index 55% rename from data_svelte_lite/myProfile.json rename to compilerProfile.json index 24d8a8db..97fd71cd 100644 --- a/data_svelte_lite/myProfile.json +++ b/compilerProfile.json @@ -3,7 +3,7 @@ "name": "IoTmanagerVer4", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", + "routerssid": "iot", "routerpass": "hostel3333", "timezone": 2, "ntp": "pool.ntp.org", @@ -24,16 +24,118 @@ }, "projectProp": { "platformio": { - "default_envs": "esp8266_1mb_ota", - "comments_default_envs": "choose from: esp8266_4mb or esp32_4mb or esp8266_1mb or esp8266_1mb_ota or esp8285_1mb or esp8285_1mb_ota" + "default_envs": "esp8266_4mb", + "comments_default_envs": "choose from: esp8266_4mb or esp32_4mb or esp32cam_4mb or esp32s2_4mb or esp32_4mb3f or esp32s3_16mb or esp32c3m_4mb or esp8266_1mb or esp8266_1mb_ota or esp8285_1mb or esp8285_1mb_ota", + "envs": [ + { + "name": "esp8266_4mb", + "firmware": "0x00000", + "littlefs": "0x300000" + }, + { + "name": "esp8266_16mb", + "firmware": "0x00000", + "littlefs": "0x200000" + }, + { + "name": "esp32_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32_4mb3f", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x310000" + }, + { + "name": "esp32cam_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32_16mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x910000" + }, + { + "name": "esp8266_1mb", + "firmware": "0x00000000", + "littlefs": "0x000bb000" + }, + { + "name": "esp8266_1mb_ota", + "firmware": "0x00000000", + "littlefs": "0x000eb000" + }, + { + "name": "esp8266_2mb", + "firmware": "0x00000000", + "littlefs": "0x00100000" + }, + { + "name": "esp8266_2mb_ota", + "firmware": "0x00000000", + "littlefs": "0x001c0000" + }, + { + "name": "esp8285_1mb", + "firmware": "0x00000000", + "littlefs": "0x000bb000" + }, + { + "name": "esp8285_1mb_ota", + "firmware": "0x00000000", + "littlefs": "0x000eb000" + }, + { + "name": "esp32s2_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32c3m_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x310000" + }, + { + "name": "esp32s3_16mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x910000" + } + ] } }, "modules": { - "Виртуальные элементы": [ + "virtual_elments": [ { "path": "src/modules/virtual/Cron", "active": true }, + { + "path": "src/modules/virtual/GoogleSheet", + "active": false + }, { "path": "src/modules/virtual/Loging", "active": true @@ -42,6 +144,14 @@ "path": "src/modules/virtual/LogingDaily", "active": true }, + { + "path": "src/modules/virtual/Math", + "active": false + }, + { + "path": "src/modules/virtual/owmWeather", + "active": true + }, { "path": "src/modules/virtual/Timer", "active": true @@ -50,10 +160,6 @@ "path": "src/modules/virtual/Variable", "active": true }, - { - "path": "src/modules/virtual/VariableColor", - "active": true - }, { "path": "src/modules/virtual/VButton", "active": true @@ -63,7 +169,15 @@ "active": false } ], - "Сенсоры": [ + "sensors": [ + { + "path": "src/modules/exec/Pcf8591", + "active": false + }, + { + "path": "src/modules/sensors/A02Distance", + "active": true + }, { "path": "src/modules/sensors/Acs712", "active": true @@ -88,6 +202,14 @@ "path": "src/modules/sensors/Ble", "active": false }, + { + "path": "src/modules/sensors/Ble_part1", + "active": false + }, + { + "path": "src/modules/sensors/Ble_part2", + "active": false + }, { "path": "src/modules/sensors/Bme280", "active": true @@ -112,6 +234,10 @@ "path": "src/modules/sensors/Emon", "active": false }, + { + "path": "src/modules/sensors/ExampleModule", + "active": false + }, { "path": "src/modules/sensors/ExternalMQTT", "active": false @@ -144,10 +270,18 @@ "path": "src/modules/sensors/Ina219", "active": false }, + { + "path": "src/modules/sensors/Ina226", + "active": false + }, { "path": "src/modules/sensors/IoTWiegand", "active": false }, + { + "path": "src/modules/sensors/ld2410", + "active": false + }, { "path": "src/modules/sensors/Max6675", "active": false @@ -156,6 +290,10 @@ "path": "src/modules/sensors/Mhz19", "active": false }, + { + "path": "src/modules/sensors/Ntc", + "active": false + }, { "path": "src/modules/sensors/Pzem004t", "active": true @@ -168,6 +306,14 @@ "path": "src/modules/sensors/RTC", "active": true }, + { + "path": "src/modules/sensors/S8", + "active": true + }, + { + "path": "src/modules/sensors/Scd40", + "active": false + }, { "path": "src/modules/sensors/Sds011", "active": false @@ -193,7 +339,7 @@ "active": true } ], - "Исполнительные устройства": [ + "executive_devices": [ { "path": "src/modules/exec/ButtonIn", "active": true @@ -202,10 +348,22 @@ "path": "src/modules/exec/ButtonOut", "active": true }, + { + "path": "src/modules/exec/Buzzer", + "active": true + }, + { + "path": "src/modules/exec/Enconder", + "active": true + }, { "path": "src/modules/exec/EspCam", "active": false }, + { + "path": "src/modules/exec/Ftp", + "active": false + }, { "path": "src/modules/exec/HttpGet", "active": false @@ -262,12 +420,20 @@ "path": "src/modules/exec/TelegramLT", "active": true }, + { + "path": "src/modules/exec/Telegram_v2", + "active": false + }, { "path": "src/modules/exec/Thermostat", "active": false + }, + { + "path": "src/modules/sensors/Ds2423", + "active": false } ], - "Экраны": [ + "screens": [ { "path": "src/modules/display/DwinI", "active": false @@ -280,6 +446,22 @@ "path": "src/modules/display/NextionUpload", "active": false }, + { + "path": "src/modules/display/Oled128", + "active": false + }, + { + "path": "src/modules/display/Oled64", + "active": true + }, + { + "path": "src/modules/display/Smi2_m", + "active": true + }, + { + "path": "src/modules/display/TM16XX", + "active": true + }, { "path": "src/modules/display/Ws2812b", "active": false diff --git a/data_full/build/bundle.css.gz b/data_full/build/bundle.css.gz new file mode 100644 index 00000000..29ab1fa8 Binary files /dev/null and b/data_full/build/bundle.css.gz differ diff --git a/data_full/build/bundle.js.gz b/data_full/build/bundle.js.gz new file mode 100644 index 00000000..24f6ab8b Binary files /dev/null and b/data_full/build/bundle.js.gz differ diff --git a/data_full/favicon.ico b/data_full/favicon.ico new file mode 100644 index 00000000..50d908fa Binary files /dev/null and b/data_full/favicon.ico differ diff --git a/data_full/index.html b/data_full/index.html new file mode 100644 index 00000000..bbcd166f --- /dev/null +++ b/data_full/index.html @@ -0,0 +1,16 @@ + + + + + + + IoT Manager 4.5.5 + + + + + + + + + diff --git a/data_svelte_lite/dev_conf.txt b/data_lite/build/bundle.css.gz similarity index 100% rename from data_svelte_lite/dev_conf.txt rename to data_lite/build/bundle.css.gz diff --git a/data_svelte_lite/scenario.txt b/data_lite/build/bundle.js.gz similarity index 100% rename from data_svelte_lite/scenario.txt rename to data_lite/build/bundle.js.gz diff --git a/data_lite/favicon.ico b/data_lite/favicon.ico new file mode 100644 index 00000000..e69de29b diff --git a/data_svelte_lite/index.html b/data_lite/index.html similarity index 94% rename from data_svelte_lite/index.html rename to data_lite/index.html index 538417e1..42845542 100644 --- a/data_svelte_lite/index.html +++ b/data_lite/index.html @@ -8,14 +8,14 @@ + diff --git a/data_svelte/items.json b/data_svelte/items.json index f8cc2674..8ee594e1 100644 --- a/data_svelte/items.json +++ b/data_svelte/items.json @@ -4,7 +4,7 @@ "num": 0 }, { - "header": "Виртуальные элементы" + "header": "virtual_elments" }, { "global": 0, @@ -60,7 +60,7 @@ "num": 4, "int": 1, "logid": "t", - "points": 365, + "points": 200, "telegram": 0, "test": 0, "btn-defvalue": 0, @@ -68,7 +68,29 @@ }, { "global": 0, - "name": "5. Таймер", + "name": "5. Погода OWM", + "type": "Reading", + "subtype": "owmWeather", + "id": "owm", + "needSave": 0, + "widget": "nil", + "page": "Погода", + "descr": "Температура на улице", + "int": 30, + "API_key": "", + "city": "Moscow", + "lon": "37.54", + "lat": "57.74", + "lang": "ru", + "param": "", + "round": 1, + "val": "...", + "debug": 0, + "num": 5 + }, + { + "global": 0, + "name": "6. Таймер", "type": "Writing", "subtype": "Timer", "id": "timer", @@ -80,11 +102,11 @@ "ticker": 1, "repeat": 1, "needSave": 0, - "num": 5 + "num": 6 }, { "global": 0, - "name": "6. Окно ввода числа (переменная)", + "name": "7. Окно ввода числа (переменная)", "type": "Reading", "subtype": "Variable", "id": "value", @@ -98,11 +120,11 @@ "plus": 0, "multiply": 1, "round": 0, - "num": 6 + "num": 7 }, { "global": 0, - "name": "7. Окно ввода времени", + "name": "8. Окно ввода времени", "type": "Reading", "subtype": "Variable", "id": "time", @@ -112,11 +134,11 @@ "descr": "Введите время", "int": "0", "val": "02:00", - "num": 7 + "num": 8 }, { "global": 0, - "name": "8. Окно ввода даты", + "name": "9. Окно ввода даты", "type": "Reading", "subtype": "Variable", "id": "time", @@ -126,11 +148,11 @@ "descr": "Введите дату", "int": "0", "val": "24.05.2022", - "num": 8 + "num": 9 }, { "global": 0, - "name": "9. Окно ввода текста", + "name": "10. Окно ввода текста", "type": "Reading", "subtype": "Variable", "id": "txt", @@ -140,11 +162,11 @@ "descr": "Введите текст", "int": "0", "val": "текст", - "num": 9 + "num": 10 }, { "global": 0, - "name": "10. Вывод значения", + "name": "11. Вывод значения", "type": "Reading", "subtype": "Variable", "id": "vout", @@ -158,20 +180,6 @@ "plus": 0, "multiply": 1, "round": 0, - "num": 10 - }, - { - "global": 0, - "name": "11. Цветной текст", - "type": "Reading", - "subtype": "VariableColor", - "id": "color", - "needSave": 0, - "widget": "anydataDef", - "page": "Вывод", - "descr": "Цветной текст", - "val": "...", - "round": 0, "num": 11 }, { @@ -189,7 +197,7 @@ "num": 12 }, { - "header": "Сенсоры" + "header": "sensors" }, { "name": "13. A02 Дальность", @@ -630,7 +638,7 @@ "num": 42 }, { - "header": "Исполнительные устройства" + "header": "executive_devices" }, { "global": 0, @@ -712,13 +720,14 @@ "type": "Writing", "subtype": "IoTServo", "id": "servo", - "widget": "range", + "widget": "rangeServo", "page": "servo", "descr": "угол", - "int": 1, "pin": 12, - "apin": -1, - "amap": "0, 4096, 0, 180", + "minPulseWidth": 544, + "maxPulseWidth": 2400, + "neutralPulseWidth": 1500, + "trackingID": "", "num": 47 }, { @@ -757,7 +766,7 @@ "id": "impulse", "widget": "anydataDef", "page": "Кнопки", - "descr": "Количество нажаний", + "descr": "Количество нажатий", "needSave": 0, "int": 300, "inv": 1, @@ -811,7 +820,7 @@ "num": 53 }, { - "header": "Экраны" + "header": "screens" }, { "global": 0, @@ -819,14 +828,15 @@ "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - "int": 15, + "widget": "inputTxt", + "page": "screens", + "descr": "LCD Экран", "addr": "0x27", "size": "20,4", "coord": "0,0", - "id2show": "id датчика", + "id2show": "", + "prefix": "", + "postfix": "", "num": 54 }, { @@ -834,14 +844,52 @@ "type": "Reading", "subtype": "Lcd2004", "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - "int": 15, + "widget": "inputTxt", + "page": "screens", + "descr": "LCD Экран", "addr": "0x27", "size": "16,2", "coord": "0,0", - "id2show": "id датчика", + "id2show": "", + "prefix": "", + "postfix": "", "num": 55 + }, + { + "global": 0, + "name": "56. OLED экран 64 8266", + "type": "Reading", + "subtype": "Oled64", + "id": "Oled", + "widget": "inputTxt", + "page": "screens", + "descr": "OLED Экран", + "addr": "0x3C", + "coord": "0,0", + "size": "1", + "id2show": "", + "prefix": "", + "postfix": "", + "num": 56 + }, + { + "global": 0, + "name": "57. 7 сегментный дисплей TM16XX", + "type": "Writing", + "subtype": "TM16XX", + "id": "tm", + "widget": "inputTxt", + "page": "screens", + "descr": "Экран", + "round": 0, + "chip": 1637, + "numDigits": 4, + "DIO": "13", + "CLK": "14", + "STB": "12", + "intensity": "5", + "on": "1", + "id2show": "", + "num": 57 } ] \ No newline at end of file diff --git a/data_svelte/ota.json b/data_svelte/ota.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/data_svelte/ota.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/data_svelte/settings.json b/data_svelte/settings.json index 793904a6..297019c9 100644 --- a/data_svelte/settings.json +++ b/data_svelte/settings.json @@ -2,7 +2,7 @@ "name": "IoTmanagerVer4", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", + "routerssid": "iot", "routerpass": "hostel3333", "timezone": 2, "ntp": "pool.ntp.org", @@ -21,5 +21,6 @@ "pinSDA": 0, "i2cFreq": 100000, "wg": "group1", + "udps": 1, "settings_": "" } \ No newline at end of file diff --git a/data_svelte/widgets.json b/data_svelte/widgets.json index adf8aae5..9da79005 100644 --- a/data_svelte/widgets.json +++ b/data_svelte/widgets.json @@ -245,6 +245,17 @@ "max": 100, "debounce": 500 }, + { + "name": "rangeServo", + "label": "Ползунок (Servo)", + "widget": "range", + "descrColor": "red", + "after": "°", + "k": 1, + "min": 0, + "max": 180, + "debounce": 500 + }, { "name": "select", "label": "Выпадающий", @@ -290,6 +301,27 @@ "after": "cm", "icon": "speedometer" }, + { + "name": "anydataLiter", + "label": "Литры", + "widget": "anydata", + "after": "ltr", + "icon": "speedometer" + }, + { + "name": "anydataSpeed", + "label": "мерты в секунду", + "widget": "anydata", + "after": "m/s", + "icon": "speedometer" + }, + { + "name": "anydataСorner", + "label": "угол градусов", + "widget": "anydata", + "after": "°", + "icon": "speedometer" + }, { "name": "nil", "label": "Без виджета" diff --git a/data_svelte_lite/config.json b/data_svelte_lite/config.json deleted file mode 100644 index 0637a088..00000000 --- a/data_svelte_lite/config.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/data_svelte_lite/edit.htm.gz b/data_svelte_lite/edit.htm.gz deleted file mode 100644 index 06931fac..00000000 Binary files a/data_svelte_lite/edit.htm.gz and /dev/null differ diff --git a/data_svelte_lite/items.json b/data_svelte_lite/items.json deleted file mode 100644 index b8a7410e..00000000 --- a/data_svelte_lite/items.json +++ /dev/null @@ -1,694 +0,0 @@ -[ - { - "name": "Выберите элемент", - "num": 0 - }, - { - "header": "Виртуальные элементы" - }, - { - "global": 0, - "name": "1. Будильник (Cron)", - "type": "Writing", - "subtype": "Cron", - "id": "cron", - "widget": "anydataDef", - "page": "Таймеры", - "descr": "Будильник", - "int": 1, - "val": "*/15 * * * * *", - "formatNextAlarm": "%H:%M:%S", - "needSave": 0, - "num": 1 - }, - { - "global": 0, - "name": "2. График", - "type": "Writing", - "subtype": "Loging", - "id": "log", - "widget": "chart2", - "page": "Графики", - "descr": "Температура", - "num": 2, - "int": 5, - "logid": "t", - "points": 300 - }, - { - "global": 0, - "name": "3. График по событию", - "type": "Writing", - "subtype": "Loging", - "id": "log", - "widget": "chart2", - "page": "Графики", - "descr": "Температура", - "int": 0, - "num": 3, - "points": 300 - }, - { - "global": 0, - "name": "4. График дневного расхода", - "type": "Writing", - "subtype": "LogingDaily", - "id": "log", - "widget": "chart3", - "page": "Графики", - "descr": "Температура", - "num": 4, - "int": 1, - "logid": "t", - "points": 365, - "telegram": 0, - "test": 0, - "btn-defvalue": 0, - "btn-reset": "nil" - }, - { - "global": 0, - "name": "5. Таймер", - "type": "Writing", - "subtype": "Timer", - "id": "timer", - "widget": "anydataDef", - "page": "Таймеры", - "descr": "Таймер", - "int": 1, - "countDown": 15, - "ticker": 1, - "repeat": 1, - "needSave": 0, - "num": 5 - }, - { - "global": 0, - "name": "6. Окно ввода числа (переменная)", - "type": "Reading", - "subtype": "Variable", - "id": "value", - "needSave": 0, - "widget": "inputDgt", - "page": "Ввод", - "descr": "Введите число", - "int": "0", - "val": "0.0", - "map": "1024,1024,1,100", - "plus": 0, - "multiply": 1, - "round": 0, - "num": 6 - }, - { - "global": 0, - "name": "7. Окно ввода времени", - "type": "Reading", - "subtype": "Variable", - "id": "time", - "needSave": 0, - "widget": "inputTm", - "page": "Ввод", - "descr": "Введите время", - "int": "0", - "val": "02:00", - "num": 7 - }, - { - "global": 0, - "name": "8. Окно ввода даты", - "type": "Reading", - "subtype": "Variable", - "id": "time", - "needSave": 0, - "widget": "inputDate", - "page": "Ввод", - "descr": "Введите дату", - "int": "0", - "val": "24.05.2022", - "num": 8 - }, - { - "global": 0, - "name": "9. Окно ввода текста", - "type": "Reading", - "subtype": "Variable", - "id": "txt", - "needSave": 0, - "widget": "inputTxt", - "page": "Ввод", - "descr": "Введите текст", - "int": "0", - "val": "текст", - "num": 9 - }, - { - "global": 0, - "name": "10. Вывод значения", - "type": "Reading", - "subtype": "Variable", - "id": "vout", - "needSave": 0, - "widget": "anydataDef", - "page": "Вывод", - "descr": "Значение", - "int": "0", - "val": "0.0", - "map": "1024,1024,1,100", - "plus": 0, - "multiply": 1, - "round": 0, - "num": 10 - }, - { - "global": 0, - "name": "11. Цветной текст", - "type": "Reading", - "subtype": "VariableColor", - "id": "color", - "needSave": 0, - "widget": "anydataDef", - "page": "Вывод", - "descr": "Цветной текст", - "val": "...", - "round": 0, - "num": 11 - }, - { - "global": 0, - "name": "12. Виртуальная кнопка", - "type": "Reading", - "subtype": "VButton", - "id": "vbtn", - "needSave": 0, - "widget": "toggle", - "page": "Кнопки", - "descr": "Кнопка", - "int": "0", - "val": "0", - "num": 12 - }, - { - "header": "Сенсоры" - }, - { - "name": "13. Acs712 Ток", - "type": "Reading", - "subtype": "Acs712", - "id": "amp", - "widget": "anydataAmp", - "page": "Сенсоры", - "descr": "Ток", - "round": 3, - "pin": 39, - "int": 5, - "rms": 1, - "vref": 5000, - "sens": 100, - "adczero": 512, - "btn-setZero": "nil", - "num": 13 - }, - { - "global": 0, - "name": "14. Аналоговый сенсор", - "type": "Reading", - "subtype": "AnalogAdc", - "id": "t", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Температура", - "map": "1,1024,1,100", - "plus": 0, - "multiply": 1, - "round": 1, - "pin": 0, - "int": 15, - "avgSteps": 1, - "num": 14 - }, - { - "global": 0, - "name": "15. BME280 Температура", - "type": "Reading", - "subtype": "Bme280t", - "id": "Tmp3", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Температура", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 15 - }, - { - "global": 0, - "name": "16. BME280 Давление", - "type": "Reading", - "subtype": "Bme280p", - "id": "Press3", - "widget": "anydataMm", - "page": "Сенсоры", - "descr": "Давление", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 16 - }, - { - "global": 0, - "name": "17. BME280 Влажность", - "type": "Reading", - "subtype": "Bme280h", - "id": "Hum3", - "widget": "anydataHum", - "page": "Сенсоры", - "descr": "Влажность", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 17 - }, - { - "global": 0, - "name": "18. BME280 Tочка росы", - "type": "Reading", - "subtype": "Bme280dp", - "id": "Dew3", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Точка росы", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 18 - }, - { - "global": 0, - "name": "19. BMP280 Температура", - "type": "Reading", - "subtype": "Bmp280t", - "id": "tmp3", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "280 Температура", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 19 - }, - { - "global": 0, - "name": "20. BMP280 Давление", - "type": "Reading", - "subtype": "Bmp280p", - "id": "Press3", - "widget": "anydataMm", - "page": "Сенсоры", - "descr": "280 Давление", - "int": 15, - "addr": "0x77", - "round": 1, - "num": 20 - }, - { - "global": 0, - "name": "21. DS18B20 Температура", - "type": "Reading", - "subtype": "Ds18b20", - "id": "dstmp", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "DS Температура", - "int": 15, - "pin": 2, - "index": 0, - "addr": "", - "round": 1, - "num": 21 - }, - { - "global": 0, - "name": "22. Аналоговый счетчик импульсов", - "type": "Writing", - "subtype": "Impulse", - "id": "impulse", - "widget": "anydataDef", - "page": "Счетчики", - "descr": "Импульсов", - "needSave": 0, - "int": 1, - "pin": 16, - "pinMode": "INPUT", - "debounceDelay": 3, - "multiply": 1, - "num": 22 - }, - { - "global": 0, - "name": "23. PZEM 004t Напряжение", - "type": "Reading", - "subtype": "Pzem004v", - "id": "v", - "widget": "anydataVlt", - "page": "PZEM", - "descr": "Напряжение", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 23 - }, - { - "global": 0, - "name": "24. PZEM 004t Сила тока", - "type": "Reading", - "subtype": "Pzem004a", - "id": "a", - "widget": "anydataAmp", - "page": "PZEM", - "descr": "Сила тока", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 24 - }, - { - "global": 0, - "name": "25. PZEM 004t Мощность", - "type": "Reading", - "subtype": "Pzem004w", - "id": "w", - "widget": "anydataWt", - "page": "PZEM", - "descr": "Мощность", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 25 - }, - { - "global": 0, - "name": "26. PZEM 004t Энергия", - "type": "Reading", - "subtype": "Pzem004wh", - "id": "wh", - "widget": "anydataWth", - "page": "PZEM", - "descr": "Энергия", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 26 - }, - { - "global": 0, - "name": "27. PZEM 004t Частота", - "type": "Reading", - "subtype": "Pzem004hz", - "id": "hz", - "widget": "anydataHtz", - "page": "PZEM", - "descr": "Частота", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 27 - }, - { - "global": 0, - "name": "28. PZEM 004t Косинус", - "type": "Reading", - "subtype": "Pzem004pf", - "id": "pf", - "widget": "anydata", - "page": "PZEM", - "descr": "Косинус F", - "int": 15, - "addr": "0xF8", - "round": 1, - "num": 28 - }, - { - "global": 0, - "name": "29. PZEM настройка", - "type": "Reading", - "subtype": "Pzem004cmd", - "id": "set", - "widget": "nil", - "page": "", - "descr": "", - "int": 15, - "addr": "0xF8", - "changeaddr": 0, - "setaddr": "0x01", - "reset": 0, - "num": 29 - }, - { - "global": 0, - "name": "30. Часы реального времени", - "type": "Reading", - "subtype": "RTC", - "id": "rtc", - "widget": "anydataDef", - "page": "Таймеры", - "descr": "Время RTC", - "chipCode": 1, - "timeFormat": "d-m-Y H:i:s", - "RST": -1, - "CLK": -1, - "DAT": -1, - "ticker": 0, - "int": 5, - "btn-setUTime": "0", - "btn-setSysTime": "nil", - "num": 30 - }, - { - "global": 0, - "name": "31. Sht20 Температура", - "type": "Reading", - "subtype": "Sht20t", - "id": "tmp2", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Температура", - "int": 15, - "round": 1, - "num": 31 - }, - { - "global": 0, - "name": "32. Sht20 Влажность", - "type": "Reading", - "subtype": "Sht20h", - "id": "Hum2", - "widget": "anydataHum", - "page": "Сенсоры", - "descr": "Влажность", - "int": 15, - "round": 1, - "num": 32 - }, - { - "global": 0, - "name": "33. Sht30 Температура", - "type": "Reading", - "subtype": "Sht30t", - "id": "tmp30", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "SHT30 Температура", - "int": 15, - "round": 1, - "num": 33 - }, - { - "global": 0, - "name": "34. Sht30 Влажность", - "type": "Reading", - "subtype": "Sht30h", - "id": "Hum30", - "widget": "anydataHum", - "page": "Сенсоры", - "descr": "SHT30 Влажность", - "int": 15, - "round": 1, - "num": 34 - }, - { - "global": 0, - "name": "35. HC-SR04 Ультразвуковой дальномер", - "num": 35, - "type": "Reading", - "subtype": "Sonar", - "id": "sonar", - "widget": "anydataTmp", - "page": "Сенсоры", - "descr": "Расстояние (см)", - "pinTrig": 5, - "pinEcho": 4, - "int": 5 - }, - { - "name": "36. UART", - "type": "Reading", - "subtype": "UART", - "page": "", - "descr": "", - "widget": "nil", - "id": "u", - "tx": 4, - "rx": 5, - "line": 2, - "speed": 9600, - "eventFormat": 0, - "num": 36 - }, - { - "header": "Исполнительные устройства" - }, - { - "global": 0, - "name": "37. Кнопка подключенная к пину", - "type": "Writing", - "subtype": "ButtonIn", - "id": "btn", - "widget": "toggle", - "page": "Кнопки", - "descr": "Освещение", - "needSave": 0, - "int": 0, - "pin": 16, - "execLevel": "1", - "pinMode": "INPUT", - "debounceDelay": 50, - "fixState": 0, - "inv": 0, - "num": 37 - }, - { - "global": 0, - "name": "38. Управление пином", - "type": "Writing", - "subtype": "ButtonOut", - "needSave": 0, - "id": "btn", - "widget": "toggle", - "page": "Кнопки", - "descr": "Освещение", - "int": 0, - "inv": 0, - "pin": 2, - "num": 38 - }, - { - "global": 0, - "name": "39. Расширитель портов Mcp23017", - "type": "Reading", - "subtype": "Mcp23017", - "id": "Mcp", - "widget": "", - "page": "", - "descr": "", - "int": "0", - "addr": "0x20", - "index": 1, - "num": 39 - }, - { - "global": 0, - "name": "40. Сенсорная кнопка", - "type": "Writing", - "subtype": "Multitouch", - "id": "impulse", - "widget": "anydataDef", - "page": "Кнопки", - "descr": "Количество нажаний", - "needSave": 0, - "int": 300, - "inv": 1, - "pin": 16, - "pinMode": "INPUT", - "debounceDelay": 50, - "PWMDelay": 500, - "num": 40 - }, - { - "global": 0, - "name": "41. Расширитель портов Pcf8574", - "type": "Reading", - "subtype": "Pcf8574", - "id": "Pcf", - "widget": "", - "page": "", - "descr": "", - "int": "0", - "addr": "0x20", - "index": 1, - "num": 41 - }, - { - "global": 0, - "name": "42. PWM ESP8266", - "type": "Writing", - "subtype": "Pwm8266", - "id": "pwm", - "widget": "range", - "page": "Кнопки", - "descr": "PWM", - "int": 0, - "pin": 15, - "freq": 5000, - "val": 0, - "apin": -1, - "num": 42 - }, - { - "global": 0, - "name": "43. Телеграм-Лайт", - "type": "Writing", - "subtype": "TelegramLT", - "id": "tg", - "widget": "", - "page": "", - "descr": "", - "token": "", - "chatID": "", - "num": 43 - }, - { - "header": "Экраны" - }, - { - "global": 0, - "name": "44. LCD экран 2004", - "type": "Reading", - "subtype": "Lcd2004", - "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - "int": 15, - "addr": "0x27", - "size": "20,4", - "coord": "0,0", - "id2show": "id датчика", - "num": 44 - }, - { - "name": "45. LCD экран 1602", - "type": "Reading", - "subtype": "Lcd2004", - "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - "int": 15, - "addr": "0x27", - "size": "16,2", - "coord": "0,0", - "id2show": "id датчика", - "num": 45 - } -] \ No newline at end of file diff --git a/data_svelte_lite/layout.json b/data_svelte_lite/layout.json deleted file mode 100644 index 0637a088..00000000 --- a/data_svelte_lite/layout.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/data_svelte_lite/settings.json b/data_svelte_lite/settings.json deleted file mode 100644 index f6913b49..00000000 --- a/data_svelte_lite/settings.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "IoTmanagerVer4", - "apssid": "IoTmanager", - "appass": "", - "routerssid": "rise", - "routerpass": "hostel3333", - "timezone": 2, - "ntp": "pool.ntp.org", - "weblogin": "admin", - "webpass": "admin", - "mqttServer": "", - "mqttPort": 8021, - "mqttPrefix": "/risenew", - "mqttUser": "rise", - "mqttPass": "3hostel3", - "serverip": "http://iotmanager.org", - "log": 0, - "mqttin": 0, - "pinSCL": 0, - "pinSDA": 0, - "i2cFreq": 100000, - "settings_": "", - "wg": "group1" -} \ No newline at end of file diff --git a/data_svelte_lite/values.json b/data_svelte_lite/values.json deleted file mode 100644 index 0e0dcd23..00000000 --- a/data_svelte_lite/values.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} \ No newline at end of file diff --git a/data_svelte_lite/widgets.json b/data_svelte_lite/widgets.json deleted file mode 100644 index cab0dbe2..00000000 --- a/data_svelte_lite/widgets.json +++ /dev/null @@ -1,216 +0,0 @@ -[ - { - "name": "anydataRed", - "label": "Сообщение1", - "widget": "anydata", - "icon": "body", - "color": "red", - "descrColor": "red" - }, - { - "name": "anydataDgr", - "label": "Сообщение2", - "widget": "anydata", - "after": "", - "color": "red", - "icon": "walk" - }, - { - "name": "anydataDef", - "label": "Текст", - "widget": "anydata", - "after": "", - "icon": "" - }, - { - "name": "anydataVlt", - "label": "Вольты", - "widget": "anydata", - "after": "V", - "icon": "speedometer" - }, - { - "name": "anydataAmp", - "label": "Амперы", - "widget": "anydata", - "after": "A", - "icon": "speedometer" - }, - { - "name": "anydataWt", - "label": "Ватты", - "widget": "anydata", - "after": "Wt", - "icon": "speedometer" - }, - { - "name": "anydataWth", - "label": "Энергия", - "widget": "anydata", - "after": "kWt/Hr", - "icon": "speedometer" - }, - { - "name": "anydataHtz", - "label": "Герцы", - "widget": "anydata", - "after": "Hz", - "icon": "speedometer" - }, - { - "name": "anydataTmp", - "label": "Температура", - "widget": "anydata", - "after": "°С", - "icon": "thermometer" - }, - { - "name": "anydataMm", - "label": "Давление", - "widget": "anydata", - "after": "mm", - "icon": "speedometer" - }, - { - "name": "anydataHum", - "label": "Влажность", - "widget": "anydata", - "after": "%", - "icon": "water", - "color": "#88AADF" - }, - { - "name": "anydataTm", - "label": "Время", - "widget": "anydata", - "after": "", - "icon": "speedometer" - }, - { - "name": "button", - "label": "Кнопка", - "widget": "btn", - "size": "large", - "color": "green", - "send": "test" - }, - { - "name": "toggle", - "label": "Переключатель", - "widget": "toggle", - "icon": "", - "iconOff": "" - }, - { - "name": "chart1", - "label": "График без точек", - "widget": "chart", - "dateFormat": "HH:mm", - "maxCount": 86400, - "pointRadius": 0 - }, - { - "name": "chart2", - "label": "График с точками", - "widget": "chart", - "maxCount": 86400, - "dateFormat": "HH:mm" - }, - { - "name": "chart3", - "label": "График Дневной", - "widget": "chart", - "dateFormat": "DD.MM.YYYY", - "maxCount": 86400, - "type": "bar" - }, - { - "name": "fillgauge", - "label": "Бочка", - "widget": "fillgauge", - "circleColor": "#00FFFF", - "textColor": "#FFFFFF", - "waveTextColor": "#000000", - "waveColor": "#00FFFF" - }, - { - "name": "inputDate", - "label": "Ввод даты", - "widget": "input", - "size": "small", - "color": "orange", - "type": "date" - }, - { - "name": "inputDgt", - "label": "Ввод числа", - "widget": "input", - "color": "blue", - "type": "number" - }, - { - "name": "inputTxt", - "label": "Ввод текста", - "widget": "input", - "size": "small", - "color": "orange", - "type": "text" - }, - { - "name": "inputTm", - "label": "Ввод времени", - "widget": "input", - "color": "blue", - "type": "time" - }, - { - "name": "progressLine", - "label": "Статус линия", - "widget": "progress-line", - "icon": "sunny", - "max": "100", - "stroke": "10" - }, - { - "name": "progressRound", - "label": "Статус круг", - "widget": "progress-round", - "max": "100", - "stroke": "20", - "color": "#45ccce", - "background": "#777", - "semicircle": "1" - }, - { - "name": "range", - "label": "Ползунок", - "widget": "range", - "descrColor": "red", - "after": "%", - "k": 0.0977, - "min": 0, - "max": 100, - "debounce": 500 - }, - { - "name": "select", - "label": "Выпадающий", - "widget": "select", - "options": [ - "Выключен", - "Включен" - ], - "status": 0 - }, - { - "name": "anydataPpm", - "label": "PPM", - "widget": "anydata", - "after": "ppm", - "icon": "speedometer" - }, - { - "name": "nil", - "label": "Без виджета" - } -] \ No newline at end of file diff --git a/include/BuildTime.h b/include/BuildTime.h new file mode 100644 index 00000000..9049b78f --- /dev/null +++ b/include/BuildTime.h @@ -0,0 +1,80 @@ +/* + Парсинг и получение даты и времени компиляции из __DATE__ и __TIME__ + Документация: + GitHub: https://github.com/GyverLibs/buildTime + Константы времени компиляции: + BUILD_YEAR - год + BUILD_MONTH - месяц + BUILD_DAY - день + BUILD_HOUR - час + BUILD_MIN - минута + BUILD_SEC - секунда + + Исходник http://qaru.site/questions/186859/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stringify + AlexGyver, alex@alexgyver.ru + https://alexgyver.ru/ + MIT License + + Версии: + v1.0 - релиз +*/ + +#ifndef buildTime_h +#define buildTime_h +// Example of __DATE__ string: "Jul 27 2012" +// 01234567890 + +#define BUILD_YEAR_CH0 (__DATE__[7] - '0') +#define BUILD_YEAR_CH1 (__DATE__[8] - '0') +#define BUILD_YEAR_CH2 (__DATE__[9] - '0') +#define BUILD_YEAR_CH3 (__DATE__[10] - '0') +#define BUILD_YEAR (BUILD_YEAR_CH0 * 1000 + BUILD_YEAR_CH1 * 100 + BUILD_YEAR_CH2 * 10 + BUILD_YEAR_CH3) + +#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') +#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F') +#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') +#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p') +#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') +#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') +#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') +#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u') +#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S') +#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O') +#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N') +#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D') + +#define BUILD_MONTH \ + ( \ + (BUILD_MONTH_IS_JAN) ? 1 : (BUILD_MONTH_IS_FEB) ? 2 \ + : (BUILD_MONTH_IS_MAR) ? 3 \ + : (BUILD_MONTH_IS_APR) ? 4 \ + : (BUILD_MONTH_IS_MAY) ? 5 \ + : (BUILD_MONTH_IS_JUN) ? 6 \ + : (BUILD_MONTH_IS_JUL) ? 7 \ + : (BUILD_MONTH_IS_AUG) ? 8 \ + : (BUILD_MONTH_IS_SEP) ? 9 \ + : (BUILD_MONTH_IS_OCT) ? 10 \ + : (BUILD_MONTH_IS_NOV) ? 11 \ + : (BUILD_MONTH_IS_DEC) ? 12 \ + : /* error default */ '?') + +#define BUILD_DAY_CH0 (((__DATE__[4] >= '0') ? (__DATE__[4]) : '0') - '0') +#define BUILD_DAY_CH1 (__DATE__[5] - '0') +#define BUILD_DAY (BUILD_DAY_CH0 * 10 + BUILD_DAY_CH1) + +// Example of __TIME__ string: "21:06:19" +// 01234567 + +#define BUILD_HOUR_CH0 (__TIME__[0] - '0') +#define BUILD_HOUR_CH1 (__TIME__[1] - '0') +#define BUILD_HOUR (BUILD_HOUR_CH0 * 10 + BUILD_HOUR_CH1) + +#define BUILD_MIN_CH0 (__TIME__[3] - '0') +#define BUILD_MIN_CH1 (__TIME__[4] - '0') +#define BUILD_MIN (BUILD_MIN_CH0 * 10 + BUILD_MIN_CH1) + +#define BUILD_SEC_CH0 (__TIME__[6] - '0') +#define BUILD_SEC_CH1 (__TIME__[7] - '0') +#define BUILD_SEC (BUILD_SEC_CH0 * 10 + BUILD_SEC_CH1) + +#endif \ No newline at end of file diff --git a/include/Const.h b/include/Const.h index 20cf9a1f..e7abc35d 100644 --- a/include/Const.h +++ b/include/Const.h @@ -1,7 +1,8 @@ #pragma once +#include "BuildTime.h" -//Версия прошивки -#define FIRMWARE_VERSION 435 +// Версия прошивки +#define FIRMWARE_VERSION 455 #ifdef esp8266_1mb_ota #define FIRMWARE_NAME "esp8266_1mb_ota" @@ -23,25 +24,57 @@ #define FIRMWARE_NAME "esp8266_4mb" #endif +#ifdef esp8266_16mb +#define FIRMWARE_NAME "esp8266_16mb" +#endif + #ifdef esp32_4mb #define FIRMWARE_NAME "esp32_4mb" #endif -//Размер буфера json -#define JSON_BUFFER_SIZE 2048 //держим 2 кб не меняем -#define WEB_SOCKETS_FRAME_SIZE 2048 +#ifdef esp32cam_4mb +#define FIRMWARE_NAME "esp32cam_4mb" +#endif -//#define LOOP_DEBUG +#ifdef esp32_16mb +#define FIRMWARE_NAME "esp32_16mb" +#endif -//выбор сервера -//#define ASYNC_WEB_SERVER -//#define ASYNC_WEB_SOCKETS +#ifdef esp32s2_4mb +#define FIRMWARE_NAME "esp32s2_4mb" +#endif + +#ifdef esp32c3m_4mb +#define FIRMWARE_NAME "esp32c3m_4mb" +#endif + +#ifdef esp32s3_16mb +#define FIRMWARE_NAME "esp32s3_16mb" +#endif + +// Размер буфера json +#define JSON_BUFFER_SIZE 4096 // держим 2 кб не меняем + +/* +WEB_SOCKETS_FRAME_SIZE создан для того что бы не загружать оперативку. +Эта технология передаёт в сокеты большие файлы по частям. +Чем меньше этот фрейм тем теоретически лучше. +Но и сильно малый он тоже быть не должен. +Я опытным путём установил что размер 1024 является оптимальным. Можно так же поставить 2048 +*/ +#define WEB_SOCKETS_FRAME_SIZE 1024 + +// #define LOOP_DEBUG + +// выбор сервера +// #define ASYNC_WEB_SERVER +// #define ASYNC_WEB_SOCKETS #define STANDARD_WEB_SERVER #define STANDARD_WEB_SOCKETS #define UDP_ENABLED -//#define REST_FILE_OPERATIONS +// #define REST_FILE_OPERATIONS #define MQTT_RECONNECT_INTERVAL 20000 @@ -54,35 +87,29 @@ #define MIN_DATETIME 1575158400 #define LEAP_YEAR(Y) (((1970 + Y) > 0) && !((1970 + Y) % 4) && (((1970 + Y) % 100) || !((1970 + Y) % 400))) -//задачи таскера -enum TimerTask_t { WIFI_SCAN, - WIFI_MQTT_CONNECTION_CHECK, - TIME, - TIME_SYNC, - UPTIME, - UDP, // UDPP - TIMES, // периодические секундные проверки - PTASK, - ST, - END }; +// задачи таскера +enum TimerTask_t { + WIFI_SCAN, + WIFI_MQTT_CONNECTION_CHECK, + TIME, + TIME_SYNC, + UPTIME, + UDP, // UDPP + TIMES, // периодические секундные проверки + PTASK, + ST, + END +}; -//задачи которые надо протащить через loop +// задачи которые надо протащить через loop enum NotAsyncActions { do_ZERO, do_MQTTPARAMSCHANGED, do_LAST, }; -//состояния обновления -enum UpdateStates { NOT_STARTED, - UPDATE_FS_IN_PROGRESS, - UPDATE_FS_COMPLETED, - UPDATE_FS_FAILED, - UPDATE_BUILD_IN_PROGRESS, - UPDATE_BUILD_COMPLETED, - UPDATE_BUILD_FAILED, - PATH_ERROR -}; +// состояния обновления +enum UpdateStates { UPDATE_COMPLETED, UPDATE_FAILED, PATH_ERROR }; enum distination { TO_MQTT, @@ -90,4 +117,4 @@ enum distination { TO_MQTT_WS, }; -#define WS_BROADCAST -1 \ No newline at end of file +#define WS_BROADCAST -1 diff --git a/include/DeviceList.h b/include/DeviceList.h index 5f376a29..773492f9 100644 --- a/include/DeviceList.h +++ b/include/DeviceList.h @@ -2,7 +2,7 @@ #include "Global.h" #ifdef ESP8266 -//эта библиотека встроена в ядро +// эта библиотека встроена в ядро #include "ESPAsyncUDP.h" #else #include "AsyncUDP.h" @@ -11,7 +11,8 @@ extern AsyncUDP asyncUdp; extern const String getThisDevice(); extern void addThisDeviceToList(); -extern void asyncUdpInit(); +extern void udpListningInit(); +extern void udpBroadcastInit(); extern String uint8tToString(uint8_t* data, size_t len); extern void udpPacketParse(String& data); extern void jsonMergeArrays(String& existJson, String& incJson); \ No newline at end of file diff --git a/include/EspFileSystem.h b/include/EspFileSystem.h index b77439ba..078f0f8d 100644 --- a/include/EspFileSystem.h +++ b/include/EspFileSystem.h @@ -1,6 +1,7 @@ #pragma once #include "Global.h" #ifdef ESP32 +#include #if USE_LITTLEFS #include #define FileFS LittleFS @@ -35,7 +36,7 @@ extern FS* filesystem; extern bool fileSystemInit(); extern void globalVarsSync(); -//extern String getParamsJson(); +// extern String getParamsJson(); extern void syncSettingsFlashJson(); extern void syncValuesFlashJson(); @@ -47,3 +48,4 @@ extern uint32_t ESP_getChipId(void); extern uint32_t ESP_getFlashChipId(void); extern const String getMacAddress(); extern const String getWebVersion(); +extern uint32_t getFlashChipIdNew(); diff --git a/include/Global.h b/include/Global.h index 91cd2047..4922d660 100644 --- a/include/Global.h +++ b/include/Global.h @@ -1,8 +1,8 @@ #pragma once -//константы +// константы #include "Const.h" -//внешние глобальные директории +// внешние глобальные директории #include #include #include @@ -43,7 +43,7 @@ #include -//внутренние глобальные директории проекта +// внутренние глобальные директории проекта #include "utils/FileUtils.h" #include "utils/JsonUtils.h" #include "utils/SerialPrint.h" @@ -56,6 +56,8 @@ **********************************************************************************************************************/ extern IoTGpio IoTgpio; extern IoTItem* rtcItem; +//extern IoTItem* camItem; +extern IoTItem* tlgrmItem; extern TickerScheduler ts; extern WiFiClient espClient; diff --git a/include/MqttClient.h b/include/MqttClient.h index 99ed843f..f7614f94 100644 --- a/include/MqttClient.h +++ b/include/MqttClient.h @@ -14,6 +14,7 @@ boolean mqttConnect(); void mqttReconnect(); void mqttLoop(); void mqttSubscribe(); +bool mqttIsConnect(); boolean publish(const String& topic, const String& data); boolean publishData(const String& topic, const String& data); @@ -21,6 +22,7 @@ boolean publishChartMqtt(const String& topic, const String& data); boolean publishJsonMqtt(const String& topic, const String& json); boolean publishStatusMqtt(const String& topic, const String& data); boolean publishEvent(const String& topic, const String& data); +void mqttSubscribeExternal(String topic, bool usePrefix = false); bool publishChartFileToMqtt(String path, String id, int maxCount); diff --git a/include/NTP.h b/include/NTP.h index 61a8c754..5911bef4 100644 --- a/include/NTP.h +++ b/include/NTP.h @@ -15,5 +15,6 @@ extern const String getDateTimeDotFormated(); extern const String getTodayDateDotFormated(); extern unsigned long strDateToUnix(String date); extern const String getDateTimeDotFormatedFromUnix(unsigned long unixTime); +extern const String getTimeDotFormatedFromUnix(unsigned long unixTime); extern unsigned long gmtTimeToLocal(unsigned long gmtTimestamp); extern const String getDateDotFormatedFromUnix(unsigned long unixTime); diff --git a/include/UpgradeFirm.h b/include/UpgradeFirm.h index 0e669984..c7b8d120 100644 --- a/include/UpgradeFirm.h +++ b/include/UpgradeFirm.h @@ -1,8 +1,8 @@ #pragma once #include "Global.h" -//#include "Upgrade.h" +// #include "Upgrade.h" #ifdef ESP8266 -//#include "ESP8266.h" +// #include "ESP8266.h" #else #include #endif @@ -17,12 +17,12 @@ struct updateFirm { extern void upgradeInit(); extern void getLastVersion(); -extern void upgrade_firmware(int type); -extern bool upgradeFS(); -extern bool upgradeBuild(); +extern void upgrade_firmware(int type, String path); +extern bool upgradeFS(String path); +extern bool upgradeBuild(String path); extern void restartEsp(); extern const String getBinPath(String file); extern void putUserDataToRam(); extern void saveUserDataToFlash(); -extern void handleUpdateStatus(bool send, int state); \ No newline at end of file +extern void saveUpdeteStatus(String key, int val); \ No newline at end of file diff --git a/include/WsServer.h b/include/WsServer.h index 0fc6b884..eed0ac78 100644 --- a/include/WsServer.h +++ b/include/WsServer.h @@ -18,4 +18,7 @@ void publishJsonWs(const String& topic, String& json); void periodicWsSend(); void sendFileToWsByFrames(const String& filename, const String& header, const String& json, int client_id, size_t frameSize); -void sendStringToWs(const String& header, String& payload, int client_id); \ No newline at end of file +void sendStringToWs(const String& header, String& payload, int client_id); + +void sendDeviceList(uint8_t num); +int getNumWSClients(); \ No newline at end of file diff --git a/include/classes/IoTItem.h b/include/classes/IoTItem.h index ff899a20..8eab3261 100644 --- a/include/classes/IoTItem.h +++ b/include/classes/IoTItem.h @@ -11,7 +11,7 @@ struct IoTValue { class IoTItem { public: IoTItem(const String& parameters); - virtual ~IoTItem() {} + virtual ~IoTItem() {}; virtual void loop(); virtual void doByInterval(); virtual IoTValue execute(String command, std::vector& param); @@ -29,15 +29,18 @@ class IoTItem { virtual String getValue(); long getInterval(); bool isGlobal(); - + void sendSubWidgetsValues(String& id, String& json); void setInterval(long interval); void setIntFromNet(int interval); - unsigned long currentMillis; - unsigned long prevMillis; - unsigned long difference; + // unsigned long currentMillis; + // unsigned long prevMillis; + // unsigned long difference; + unsigned long nextMillis=0; // достаточно 1 переменной, надо экономить память + // задержка следующего вызова, не изменяет текущий _interval + void suspendNextDoByInt(unsigned long _delay); // 0 - force IoTValue value; // хранение основного значения, которое обновляется из сценария, execute(), loop() или doByInterval() @@ -48,21 +51,31 @@ class IoTItem { virtual IoTGpio* getGpioDriver(); virtual IoTItem* getRtcDriver(); + //virtual IoTItem* getCAMDriver(); + virtual IoTItem* getTlgrmDriver(); virtual unsigned long getRtcUnixTime(); + // делаем доступным модулям отправку сообщений в телеграм + virtual void sendTelegramMsg(bool often, String msg); + virtual void sendFoto(uint8_t *buf, uint32_t length, const String &name); + virtual void editFoto(uint8_t *buf, uint32_t length, const String &name); + + virtual void setValue(const IoTValue& Value, bool genEvent = true); virtual void setValue(const String& valStr, bool genEvent = true); - String getRoundValue(); + String getRoundValue(); void getNetEvent(String& event); + virtual String getMqttExterSub(); // хуки для системных событий (должны начинаться с "on") virtual void onRegEvent(IoTItem* item); virtual void onMqttRecive(String& topic, String& msg); virtual void onMqttWsAppConnectEvent(); virtual void onModuleOrder(String& key, String& value); + virtual void onTrackingValue(IoTItem* item); // момент, когда ядро заметило изменение отслеживаемого значения - // делаем доступным модулям отправку сообщений в телеграм - virtual void sendTelegramMsg(bool often, String msg); + // для обновления экрана Nextion из телеграм + virtual void uploadNextionTlgrm(String &url); // методы для графиков (будет упрощено) virtual void publishValue(); @@ -71,6 +84,8 @@ class IoTItem { virtual void clearHistory(); virtual void setTodayDate(); + bool isTracking(IoTItem* item); // проверка на отслеживание + protected: bool _needSave = false; // признак необходимости сохранять и загружать значение элемента на flash String _subtype = ""; @@ -87,8 +102,11 @@ class IoTItem { int _map3 = 0; int _map4 = 0; int _round = 1; // 1, 10, 100, 1000, 10000 + int _numDigits = 1; // количество целых значений, не значимые позиции заменяются нулем в строковом формате bool _global = false; // характеристика айтема, что ему нужно слать и принимать события из внешнего мира + + IoTValue* _trackingValue = nullptr; // указатель на значение родительского элемента изменение которого отслеживается }; IoTItem* findIoTItem(const String& name); // поиск экземпляра элемента модуля по имени diff --git a/include/utils/JsonUtils.h b/include/utils/JsonUtils.h index a9810dc2..2d03ce8c 100644 --- a/include/utils/JsonUtils.h +++ b/include/utils/JsonUtils.h @@ -15,9 +15,11 @@ extern bool jsonRead(const String& json, String key, float& value, bool e = true extern bool jsonRead(const String& json, String key, String& value, bool e = true); extern bool jsonRead(const String& json, String key, bool& value, bool e = true); extern bool jsonRead(const String& json, String key, int& value, bool e = true); +extern bool jsonReadArray(const String& json, String key, std::vector& jArray, bool e = true); extern String jsonReadStr(const String& json, String name, bool e = true); extern int jsonReadInt(const String& json, String name, bool e = true); +long int jsonReadLInt(const String& json, String name, bool e=true); extern boolean jsonReadBool(const String& json, String name, bool e = true); extern bool jsonWriteStr_(String& json, const String& name, const String& value, bool e = true); diff --git a/include/utils/StringUtils.h b/include/utils/StringUtils.h index 816d8353..ea403cfb 100644 --- a/include/utils/StringUtils.h +++ b/include/utils/StringUtils.h @@ -45,3 +45,7 @@ String uint64ToString(uint64_t input, uint8_t base = 10); void cleanString(String& str); unsigned char ChartoHex(char ch); + +std::vector splitStr(const String& str, const String& delimiter); + +bool strInVector(const String& str, const std::vector& vec); diff --git a/include/utils/WiFiUtils.h b/include/utils/WiFiUtils.h index 404cee06..28524211 100644 --- a/include/utils/WiFiUtils.h +++ b/include/utils/WiFiUtils.h @@ -2,10 +2,10 @@ #include "Global.h" #include "MqttClient.h" - boolean isNetworkActive(); +uint8_t getNumAPClients(); void routerConnect(); bool startAPMode(); -boolean RouterFind(String ssid); +boolean RouterFind(std::vector jArray); uint8_t RSSIquality(); extern void wifiSignalInit(); diff --git a/iotm/esp32_4mb/400/firmware.bin b/iotm/esp32_4mb/400/firmware.bin new file mode 100644 index 00000000..77631f73 Binary files /dev/null and b/iotm/esp32_4mb/400/firmware.bin differ diff --git a/iotm/esp32_4mb/400/littlefs.bin b/iotm/esp32_4mb/400/littlefs.bin new file mode 100644 index 00000000..ed4deb51 Binary files /dev/null and b/iotm/esp32_4mb/400/littlefs.bin differ diff --git a/iotm/esp32_4mb/400/partitions.bin b/iotm/esp32_4mb/400/partitions.bin new file mode 100644 index 00000000..0e52b4c4 Binary files /dev/null and b/iotm/esp32_4mb/400/partitions.bin differ diff --git a/iotm/esp8266_4mb/400/firmware.bin b/iotm/esp8266_4mb/400/firmware.bin new file mode 100644 index 00000000..3b5bff0b Binary files /dev/null and b/iotm/esp8266_4mb/400/firmware.bin differ diff --git a/iotm/esp8266_4mb/400/littlefs.bin b/iotm/esp8266_4mb/400/littlefs.bin new file mode 100644 index 00000000..c7baf85d Binary files /dev/null and b/iotm/esp8266_4mb/400/littlefs.bin differ diff --git a/iotm/ver.json b/iotm/ver.json new file mode 100644 index 00000000..06766b23 --- /dev/null +++ b/iotm/ver.json @@ -0,0 +1,5 @@ +{ + "esp8266_4mb": { + "0": "400" + } +} \ No newline at end of file diff --git a/lib/ESPNexUpload/src/ESPNexUpload.cpp b/lib/ESPNexUpload/src/ESPNexUpload.cpp new file mode 100644 index 00000000..0856aa5f --- /dev/null +++ b/lib/ESPNexUpload/src/ESPNexUpload.cpp @@ -0,0 +1,667 @@ +/** + * @file NexUpload.cpp + * + * The implementation of uploading tft file for nextion displays. + * + * Original version (a part of https://github.com/itead/ITEADLIB_Arduino_Nextion) + * @author Chen Zengpeng (email:) + * @date 2016/3/29 + * @copyright + * Copyright (C) 2014-2015 ITEAD Intelligent Systems Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#define DEBUG_SERIAL_ENABLE +#include "ESPNexUpload.h" + +#if defined ESP8266 + +#include + +#ifndef NEXT_RX +#define NEXT_RX 14 // Nextion RX pin | Default 14 / D5 +#define NEXT_TX 12 // Nextion TX pin | Default 12 / D6 +#endif +#ifndef nexSerial +//SoftwareSerial softSerial(NEXT_RX, NEXT_TX); +#define nexSerial softSerial +#define nexSerialBegin(a, b, c) nexSerial.begin(a) +#endif + +#elif defined ESP32 + +#ifndef NEXT_RX +#define NEXT_RX 17 // Nextion RX pin | Default 16 +#define NEXT_TX 16 // Nextion TX pin | Default 17 +#endif +#ifndef nexSerial +#define nexSerial Serial2 +#define nexSerialBegin(a, rx, tx) nexSerial.begin(a, SERIAL_8N1, rx, tx) +#endif + +#endif + +#ifdef DEBUG_SERIAL_ENABLE +#define dbSerialPrint(a) Serial.print(a) +#define dbSerialPrintHex(a) Serial.print(a, HEX) +#define dbSerialPrintln(a) Serial.println(a) +#define dbSerialBegin(a) Serial.begin(a) +#else +#define dbSerialPrint(a) \ + do \ + { \ + } while (0) +#define dbSerialPrintHex(a) \ + do \ + { \ + } while (0) +#define dbSerialPrintln(a) \ + do \ + { \ + } while (0) +#define dbSerialBegin(a) \ + do \ + { \ + } while (0) +#endif + +ESPNexUpload::ESPNexUpload(uint32_t upload_baudrate, uint8_t rx, uint8_t tx) +{ + _upload_baudrate = upload_baudrate; + if (rx == 0 || tx == 0) + { + _rx = NEXT_RX; + _tx = NEXT_TX; + }else{ + _rx = rx; + _tx = tx; + } +#if defined ESP8266 + SoftwareSerial softSerial(_rx, _tx); +#endif + +} + +bool ESPNexUpload::connect() +{ +#if defined ESP8266 + yield(); +#endif + + dbSerialBegin(115200); + _printInfoLine(F("serial tests & connect")); + + if (_getBaudrate() == 0) + { + statusMessage = F("get baudrate error"); + _printInfoLine(statusMessage); + return false; + } + + _setRunningMode(); + + if (!_echoTest("mystop_yesABC")) + { + statusMessage = F("echo test failed"); + _printInfoLine(statusMessage); + return false; + } + + if (!_handlingSleepAndDim()) + { + statusMessage = F("handling sleep and dim settings failed"); + _printInfoLine(statusMessage); + return false; + } + + if (!_setPrepareForFirmwareUpdate(_upload_baudrate)) + { + statusMessage = F("modifybaudrate error"); + _printInfoLine(statusMessage); + return false; + } + + return true; +} + +bool ESPNexUpload::prepareUpload(uint32_t file_size) +{ + _undownloadByte = file_size; + return this->connect(); +} + +uint16_t ESPNexUpload::_getBaudrate(void) +{ + + _baudrate = 0; + uint32_t baudrate_array[7] = {115200, 19200, 9600, 57600, 38400, 4800, 2400}; + for (uint8_t i = 0; i < 7; i++) + { + if (_searchBaudrate(baudrate_array[i])) + { + _baudrate = baudrate_array[i]; + _printInfoLine(F("baudrate determined")); + break; + } + delay(1500); // wait for 1500 ms + } + return _baudrate; +} + +bool ESPNexUpload::_searchBaudrate(uint32_t baudrate) +{ + +#if defined ESP8266 + yield(); +#endif + + String response = String(""); + _printInfoLine(); + dbSerialPrint(F("init nextion serial interface on baudrate: ")); + dbSerialPrintln(baudrate); + + nexSerialBegin(baudrate, _rx, _tx); + _printInfoLine(F("ESP baudrate established, try to connect to display")); + const char _nextion_FF_FF[3] = {0xFF, 0xFF, 0x00}; + + this->sendCommand("DRAKJHSUYDGBNCJHGJKSHBDN"); + this->sendCommand("", true, true); // 0x00 0xFF 0xFF 0xFF + + this->recvRetString(response); + if (response[0] != 0x1A) + { + _printInfoLine(F("first indication that baudrate is wrong")); + } + else + { + _printInfoLine(F("first respone from display, first indication that baudrate is correct")); + } + + this->sendCommand("connect"); // first connect attempt + + this->recvRetString(response); + if (response.indexOf(F("comok")) == -1) + { + _printInfoLine(F("display doesn't accept the first connect request")); + } + else + { + _printInfoLine(F("display accept the first connect request")); + } + + response = String(""); + delay(110); // based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R + this->sendCommand(_nextion_FF_FF, false); + + this->sendCommand("connect"); // second attempt + this->recvRetString(response); + if (response.indexOf(F("comok")) == -1 && response[0] != 0x1A) + { + _printInfoLine(F("display doesn't accept the second connect request")); + _printInfoLine(F("conclusion, wrong baudrate")); + return 0; + } + else + { + _printInfoLine(F("display accept the second connect request")); + _printInfoLine(F("conclusion, correct baudrate")); + } + + return 1; +} + +void ESPNexUpload::sendCommand(const char *cmd, bool tail, bool null_head) +{ + +#if defined ESP8266 + yield(); +#endif + + if (null_head) + { + nexSerial.write(0x00); + } + + while (nexSerial.available()) + { + nexSerial.read(); + } + + nexSerial.print(cmd); + if (tail) + { + nexSerial.write(0xFF); + nexSerial.write(0xFF); + nexSerial.write(0xFF); + } + _printSerialData(true, cmd); +} + +uint16_t ESPNexUpload::recvRetString(String &response, uint32_t timeout, bool recv_flag) +{ + +#if defined ESP8266 + yield(); +#endif + + uint16_t ret = 0; + uint8_t c = 0; + uint8_t nr_of_FF_bytes = 0; + long start; + bool exit_flag = false; + bool ff_flag = false; + if (timeout != 500) + _printInfoLine("timeout setting serial read: " + String(timeout)); + + start = millis(); + + while (millis() - start <= timeout) + { + + while (nexSerial.available()) + { + + c = nexSerial.read(); + if (c == 0) + { + continue; + } + + if (c == 0xFF) + nr_of_FF_bytes++; + else + { + nr_of_FF_bytes = 0; + ff_flag = false; + } + + if (nr_of_FF_bytes >= 3) + ff_flag = true; + + response += (char)c; + + if (recv_flag) + { + if (response.indexOf(0x05) != -1) + { + exit_flag = true; + } + } + } + if (exit_flag || ff_flag) + { + break; + } + } + _printSerialData(false, response); + + // if the exit flag and the ff flag are both not found, than there is a timeout + // if(!exit_flag && !ff_flag) + // _printInfoLine(F("recvRetString: timeout")); + + if (ff_flag) + response = response.substring(0, response.length() - 3); // Remove last 3 0xFF + + ret = response.length(); + return ret; +} + +bool ESPNexUpload::_setPrepareForFirmwareUpdate(uint32_t upload_baudrate) +{ + +#if defined ESP8266 + yield(); +#endif + + String response = String(""); + String cmd = String(""); + + cmd = F("00"); + this->sendCommand(cmd.c_str()); + delay(0.1); + + this->recvRetString(response, 800, true); // normal response time is 400ms + + String filesize_str = String(_undownloadByte, 10); + String baudrate_str = String(upload_baudrate); + cmd = "whmi-wri " + filesize_str + "," + baudrate_str + ",0"; + + this->sendCommand(cmd.c_str()); + + // Without flush, the whmi command will NOT transmitted by the ESP in the current baudrate + // because switching to another baudrate (nexSerialBegin command) has an higher prio. + // The ESP will first jump to the new 'upload_baudrate' and than process the serial 'transmit buffer' + // The flush command forced the ESP to wait until the 'transmit buffer' is empty + nexSerial.flush(); + + nexSerialBegin(upload_baudrate, _rx, _tx); + _printInfoLine(F("changing upload baudrate...")); + _printInfoLine(String(upload_baudrate)); + + this->recvRetString(response, 800, true); // normal response time is 400ms + + // The Nextion display will, if it's ready to accept data, send a 0x05 byte. + if (response.indexOf(0x05) != -1) + { + _printInfoLine(F("preparation for firmware update done")); + return 1; + } + else + { + _printInfoLine(F("preparation for firmware update failed")); + return 0; + } +} + +void ESPNexUpload::setUpdateProgressCallback(THandlerFunction value) +{ + _updateProgressCallback = value; +} + +bool ESPNexUpload::upload(const uint8_t *file_buf, size_t buf_size) +{ + +#if defined ESP8266 + yield(); +#endif + + uint8_t c; + uint8_t timeout = 0; + String string = String(""); + + for (uint16_t i = 0; i < buf_size; i++) + { + + // Users must split the .tft file contents into 4096 byte sized packets with the final partial packet size equal to the last remaining bytes (<4096 bytes). + if (_sent_packets == 4096) + { + + // wait for the Nextion to return its 0x05 byte confirming reception and readiness to receive the next packets + this->recvRetString(string, 500, true); + if (string.indexOf(0x05) != -1) + { + + // reset sent packets counter + _sent_packets = 0; + + // reset receive String + string = ""; + } + else + { + if (timeout >= 8) + { + statusMessage = F("serial connection lost"); + _printInfoLine(statusMessage); + return false; + } + + timeout++; + } + + // delay current byte + i--; + } + else + { + + // read buffer + c = file_buf[i]; + + // write byte to nextion over serial + nexSerial.write(c); + + // update sent packets counter + _sent_packets++; + } + } + + return true; +} + +bool ESPNexUpload::upload(Stream &myFile) +{ +#if defined ESP8266 + yield(); +#endif + + // create buffer for read + uint8_t buff[2048] = {0}; + + // read all data from server + while (_undownloadByte > 0 || _undownloadByte == -1) + { + + // get available data size + size_t size = myFile.available(); + + if (size) + { + // read up to 2048 byte into the buffer + int c = myFile.readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + + // Write the buffered bytes to the nextion. If this fails, return false. + if (!this->upload(buff, c)) + { + return false; + } + else + { + if (_updateProgressCallback) + { + _updateProgressCallback(); + } + } + + if (_undownloadByte > 0) + { + _undownloadByte -= c; + } + } + delay(1); + } + + return true; +} + +void ESPNexUpload::softReset(void) +{ + // soft reset nextion device + this->sendCommand("rest"); +} + +void ESPNexUpload::end() +{ + + // wait for the nextion to finish internal processes + delay(1600); + + // soft reset the nextion + this->softReset(); + + // end Serial connection + nexSerial.end(); + + // reset sent packets counter + _sent_packets = 0; + + statusMessage = F("upload ok"); + _printInfoLine(statusMessage + F("\r\n")); +} + +void ESPNexUpload::_setRunningMode(void) +{ + String cmd = String(""); + delay(100); + cmd = F("runmod=2"); + this->sendCommand(cmd.c_str()); + delay(60); +} + +bool ESPNexUpload::_echoTest(String input) +{ + String cmd = String(""); + String response = String(""); + + cmd = "print \"" + input + "\""; + this->sendCommand(cmd.c_str()); + + uint32_t duration_ms = calculateTransmissionTimeMs(cmd) * 2 + 10; // times 2 (send + receive) and 10 ms extra + this->recvRetString(response, duration_ms); + + return (response.indexOf(input) != -1); +} + +bool ESPNexUpload::_handlingSleepAndDim(void) +{ + String cmd = String(""); + String response = String(""); + bool set_sleep = false; + bool set_dim = false; + + cmd = F("get sleep"); + this->sendCommand(cmd.c_str()); + + this->recvRetString(response); + + if (response[0] != 0x71) + { + statusMessage = F("unknown response from 'get sleep' request"); + _printInfoLine(statusMessage); + return false; + } + + if (response[1] != 0x00) + { + _printInfoLine(F("sleep enabled")); + set_sleep = true; + } + else + { + _printInfoLine(F("sleep disabled")); + } + + response = String(""); + cmd = F("get dim"); + this->sendCommand(cmd.c_str()); + + this->recvRetString(response); + + if (response[0] != 0x71) + { + statusMessage = F("unknown response from 'get dim' request"); + _printInfoLine(statusMessage); + return false; + } + + if (response[1] == 0x00) + { + _printInfoLine(F("dim is 0%, backlight from display is turned off")); + set_dim = true; + } + else + { + _printInfoLine(); + dbSerialPrint(F("dim ")); + dbSerialPrint((uint8_t)response[1]); + dbSerialPrintln(F("%")); + } + + if (!_echoTest("ABC")) + { + statusMessage = F("echo test in 'handling sleep and dim' failed"); + _printInfoLine(statusMessage); + return false; + } + + if (set_sleep) + { + cmd = F("sleep=0"); + this->sendCommand(cmd.c_str()); + // Unfortunately the display doesn't send any respone on the wake up request (sleep=0) + // Let the ESP wait for one second, this is based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R + // This gives the Nextion display some time to wake up + delay(1000); + } + + if (set_dim) + { + cmd = F("dim=100"); + this->sendCommand(cmd.c_str()); + delay(15); + } + + return true; +} + +void ESPNexUpload::_printSerialData(bool esp_request, String input) +{ + + char c; + if (esp_request) + dbSerialPrint(F("ESP request: ")); + else + dbSerialPrint(F("Nextion respone: ")); + + if (input.length() == 0) + { + dbSerialPrintln(F("none")); + return; + } + + for (int i = 0; i < input.length(); i++) + { + + c = input[i]; + if ((uint8_t)c >= 0x20 && (uint8_t)c <= 0x7E) + dbSerialPrint(c); + else + { + dbSerialPrint(F("0x")); + dbSerialPrintHex(c); + dbSerialPrint(F(" ")); + } + } + dbSerialPrintln(); +} + +uint32_t ESPNexUpload::calculateTransmissionTimeMs(String message) +{ + // In general, 1 second (s) = 1000 (10^-3) millisecond (ms) or + // 1 second (s) = 1000 000 (10^-6) microsecond (us). + // To calculate how much microsecond one BIT of data takes with a certain baudrate you have to divide + // the baudrate by one second. + // For example 9600 baud = 1000 000 us / 9600 ≈ 104 us + // The time to transmit one DATA byte (if we use default UART modulation) takes 10 bits. + // 8 DATA bits and one START and one STOP bit makes 10 bits. + // In this example (9600 baud) a byte will take 1041 us to send or receive. + // Multiply this value by the length of the message (number of bytes) and the total transmit/ receive time + // is calculated. + + uint32_t duration_one_byte_us = 10000000 / _baudrate; // 1000 000 * 10 bits / baudrate + uint16_t nr_of_bytes = message.length() + 3; // 3 times 0xFF byte + uint32_t duration_message_us = nr_of_bytes * duration_one_byte_us; + uint32_t return_value_ms = duration_message_us / 1000; + + _printInfoLine("calculated transmission time: " + String(return_value_ms) + " ms"); + return return_value_ms; +} + +void ESPNexUpload::_printInfoLine(String line) +{ + dbSerialPrint(F("Status info: ")); + if (line.length() != 0) + dbSerialPrintln(line); +} \ No newline at end of file diff --git a/lib/ESPNexUpload/src/ESPNexUpload.h b/lib/ESPNexUpload/src/ESPNexUpload.h new file mode 100644 index 00000000..b9cfe746 --- /dev/null +++ b/lib/ESPNexUpload/src/ESPNexUpload.h @@ -0,0 +1,273 @@ +/** + * @file NexUpload.h + * The definition of class NexUpload. + * + * + * 1 - BugFix when display baudrate is diffrent from initial ESP baudrate + * 2 - Improved debug information + * 3 - Make delay commands dependent on the baudrate + * @author Machiel Mastenbroek (machiel.mastenbroek@gmail.com) + * @date 2019/11/04 + * @version 0.5.5 + * + * Stability improvement, Nextion display doesn’t freeze after the seconds 4096 trance of firmware bytes. + * Now the firmware upload process is stabled without the need of a hard Display power off-on intervention. + * Undocumented features (not mentioned in nextion-hmi-upload-protocol-v1-1 specification) are added. + * This implementation is based in on a reverse engineering with a UART logic analyser between + * the Nextion editor v0.58 and a NX4024T032_011R Display. + * + * @author Machiel Mastenbroek (machiel.mastenbroek@gmail.com) + * @date 2019/10/24 + * @version 0.5.0 + * + * Modified to work with ESP32, HardwareSerial and removed SPIFFS dependency + * @author Onno Dirkzwager (onno.dirkzwager@gmail.com) + * @date 2018/12/26 + * @version 0.3.0 + * + * Modified to work with ESP8266 and SoftwareSerial + * @author Ville Vilpas (psoden@gmail.com) + * @date 2018/2/3 + * @version 0.2.0 + * + * Original version (a part of https://github.com/itead/ITEADLIB_Arduino_Nextion) + * @author Chen Zengpeng (email:) + * @date 2016/3/29 + * @copyright + * Copyright (C) 2014-2015 ITEAD Intelligent Systems Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __ESPNEXUPLOAD_H__ +#define __ESPNEXUPLOAD_H__ +#include +#include +#include + +/** + * @addtogroup CoreAPI + * @{ + */ + +// callback template definition +typedef std::function THandlerFunction; + +/** + * + * Provides the API for nextion to upload the ftf file. + */ +class ESPNexUpload +{ +public: /* methods */ + // callback template definition + typedef std::function THandlerFunction; + + /** + * Constructor. + * + * @param uint32_t upload_baudrate - set upload baudrate. + */ + ESPNexUpload(uint32_t upload_baudrate, uint8_t rx=0, uint8_t tx=0); + + /** + * destructor. + * + */ + ~ESPNexUpload() {} + + /** + * Connect to Nextion over serial + * + * @return true or false. + */ + bool connect(); + + /** + * prepare upload. Set file size & Connect to Nextion over serial + * + * @return true if success, false for failure. + */ + bool prepareUpload(uint32_t file_size); + + /** + * set Update Progress Callback. (What to do during update progress) + * + * @return none + */ + void setUpdateProgressCallback(THandlerFunction value); + + /** + * start update tft file to nextion. + * + * @param const uint8_t *file_buf + * @param size_t buf_size + * @return true if success, false for failure. + */ + bool upload(const uint8_t *file_buf, size_t buf_size); + + /** + * start update tft file to nextion. + * + * @param Stream &myFile + * @return true if success, false for failure. + */ + bool upload(Stream &myFile); + + /** + * Send reset command to Nextion over serial + * + * @return none. + */ + void softReset(void); + + /** + * Send reset, end serial, reset _sent_packets & update status message + * + * @return none. + */ + void end(void); + +public: /* data */ + String statusMessage = ""; + +private: /* methods */ + /* + * get communicate baudrate. + * + * @return communicate baudrate. + * + */ + uint16_t _getBaudrate(void); + + /* + * search communicate baudrate. + * + * @param baudrate - communicate baudrate. + * + * @return true if success, false for failure. + */ + bool _searchBaudrate(uint32_t baudrate); + + /* + * set download baudrate. + * + * @param baudrate - set download baudrate. + * + * @return true if success, false for failure. + */ + bool _setPrepareForFirmwareUpdate(uint32_t upload_baudrate); + + /* + * set Nextion running mode. + * + * Undocumented feature of the Nextion protocol. + * It's used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 + * + * The nextion display doesn't send any response + * + */ + void _setRunningMode(void); + + /* + * Test UART nextion connection availability + * + * @param input - echo string, + * + * @return true when the 'echo string' that is send is equal to the received string + * + * This test is used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 + * + */ + bool _echoTest(String input); + + /* + * This function get the sleep and dim value from the Nextion display. + * + * If sleep = 1 meaning: sleep is enabled + * action : sleep will be disabled + * If dim = 0, meaning: the display backlight is turned off + * action : dim will be set to 100 (percent) + * + */ + bool _handlingSleepAndDim(void); + + /* + * This function (debug) print the Nextion response to a human readable string + * + * @param esp_request - true: request message from esp to nextion + * false: response message from nextion to esp + * + * @param input - string to print + * + */ + void _printSerialData(bool esp_request, String input); + + /* + * This function print a prefix debug line + * + * @param line: optional debug/ info line + */ + void _printInfoLine(String line = ""); + + /* + * Send command to Nextion. + * + * @param cmd - the string of command. + * @param tail - end the string with tripple 0xFF byte + * @param null_head - start the string with a single 0x00 byte + * + * @return none. + */ + void sendCommand(const char *cmd, bool tail = true, bool null_head = false); + + /* + * Receive string data. + * + * @param buffer - save string data. + * @param timeout - set timeout time. + * @param recv_flag - if recv_flag is true,will braak when receive 0x05. + * + * @return the length of string buffer. + * + */ + uint16_t recvRetString(String &string, uint32_t timeout = 500, bool recv_flag = false); + + /* + * + * This function calculates the transmission time, the transmission time + * is based on the length of the message and the baudrate. + * + * @param message - only used to determine the length of the message + * + * @return time in us length of string buffer. + * + */ + uint32_t calculateTransmissionTimeMs(String message); + +private: /* data */ + uint32_t _baudrate; /* nextion serail baudrate */ + uint32_t _undownloadByte; /* undownload byte of tft file */ + uint32_t _upload_baudrate; /* upload baudrate */ + uint16_t _sent_packets = 0; /* upload baudrate */ + uint8_t _rx; + uint8_t _tx; + + THandlerFunction _updateProgressCallback; +}; +/** + * @} + */ + +#endif /* #ifndef __ESPNEXUPLOAD_H__ */ \ No newline at end of file diff --git a/lib/SimpleFTPServer/.project b/lib/SimpleFTPServer/.project new file mode 100644 index 00000000..aa361b16 --- /dev/null +++ b/lib/SimpleFTPServer/.project @@ -0,0 +1,11 @@ + + + SimpleFTPServer + + + + + + + + diff --git a/lib/SimpleFTPServer/FtpServer.cpp b/lib/SimpleFTPServer/FtpServer.cpp new file mode 100644 index 00000000..8ec4a184 --- /dev/null +++ b/lib/SimpleFTPServer/FtpServer.cpp @@ -0,0 +1,2354 @@ +/* + * FtpServer Arduino, esp8266 and esp32 library for Ftp Server + * Derived form Jean-Michel Gallego version + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + * + * Use Ethernet library + * + * Commands implemented: + * USER, PASS, AUTH (AUTH only return 'not implemented' code) + * CDUP, CWD, PWD, QUIT, NOOP + * MODE, PASV, PORT, STRU, TYPE + * ABOR, DELE, LIST, NLST, MLST, MLSD + * APPE, RETR, STOR + * MKD, RMD + * RNTO, RNFR + * MDTM, MFMT + * FEAT, SIZE + * SITE FREE + * HELP + * + * Tested with those clients: + * under Windows: + * FTP Rush + * Filezilla + * WinSCP + * NcFTP, ncftpget, ncftpput + * Firefox + * command line ftp.exe + * under Ubuntu: + * gFTP + * Filezilla + * NcFTP, ncftpget, ncftpput + * lftp + * ftp + * Firefox + * under Android: + * AndFTP + * FTP Express + * Firefox + * with a second Arduino and sketch of SurferTim at + * http://playground.arduino.cc/Code/FTP + * + */ + +#include + +FtpServer::FtpServer( uint16_t _cmdPort, uint16_t _pasvPort ) + : ftpServer( _cmdPort ), dataServer( _pasvPort ) +{ + cmdPort = _cmdPort; + pasvPort = _pasvPort; + + millisDelay = 0; + nbMatch = 0; + iCL = 0; + + iniVariables(); +} + +void FtpServer::begin( const char * _user, const char * _pass, const char * _welcomeMessage ) +{ + if ( strcmp( _user, "anonymous" ) != 0) { + DEBUG_PRINTLN(F("NOT ANONYMOUS")); + DEBUG_PRINTLN(_user); + this->anonymousConnection = false; // needed to reset after end of anonymnous and begin of not anonymous + } + // Tells the ftp server to begin listening for incoming connection + ftpServer.begin(); + #if (defined(ESP8266) && (FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)) || defined(ARDUINO_ARCH_RP2040) || FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN + ftpServer.setNoDelay( true ); + #endif +// localIp = _localIP == FTP_NULLIP() || (uint32_t) _localIP == 0 ? NET_CLASS.localIP() : _localIP ; + localIp = NET_CLASS.localIP(); //_localIP == FTP_NULLIP() || (uint32_t) _localIP == 0 ? NET_CLASS.localIP() : _localIP ; +// strcpy( user, FTP_USER ); +// strcpy( pass, FTP_PASS ); + if( strlen( _user ) > 0 && strlen( _user ) < FTP_CRED_SIZE ) { + //strcpy( user, _user ); + this->user = _user; + } + if( strlen( _pass ) > 0 && strlen( _pass ) < FTP_CRED_SIZE ) { +// strcpy( pass, _pass ); + this->pass = _pass; + } +// strcpy(_welcomeMessage, welcomeMessage); + + this->welcomeMessage = _welcomeMessage; + + dataServer.begin(); +#if (defined(ESP8266) && (FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)) || defined(ARDUINO_ARCH_RP2040) || FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN + dataServer.setNoDelay( true ); +#endif + + millisDelay = 0; + cmdStage = FTP_Stop; + iniVariables(); +} + +void FtpServer::begin( const char * _welcomeMessage ) { + this->anonymousConnection = true; + this->begin( "anonymous", "anonymous", _welcomeMessage); +} + +void FtpServer::end() +{ + if(client.connected()) { + disconnectClient(); + } + +#if FTP_SERVER_NETWORK_TYPE == NETWORK_ESP32 && !defined(ARDUINO_ARCH_RP2040) + ftpServer.end(); + dataServer.end(); +#endif + + DEBUG_PRINTLN(F("Stop server!")); + + if (FtpServer::_callback) { + FtpServer::_callback(FTP_DISCONNECT, free(), capacity()); + } + + cmdStage = FTP_Init; + transferStage = FTP_Close; + dataConn = FTP_NoConn; +} +void FtpServer::setLocalIp(IPAddress localIp) +{ + this->localIp = localIp; +} +void FtpServer::credentials( const char * _user, const char * _pass ) +{ + if( strlen( _user ) > 0 && strlen( _user ) < FTP_CRED_SIZE ) +// strcpy( user, _user ); + this->user = user; + if( strlen( _pass ) > 0 && strlen( _pass ) < FTP_CRED_SIZE ) +// strcpy( pass, _pass ); + this->pass = _pass; +} + +void FtpServer::iniVariables() +{ + // Default for data port + dataPort = FTP_DATA_PORT_DFLT; + + // Default Data connection is Active + dataConn = FTP_NoConn; + + // Set the root directory + strcpy( cwdName, "/" ); + + rnfrCmd = false; + transferStage = FTP_Close; +} + +uint8_t FtpServer::handleFTP() { +#ifdef FTP_ADDITIONAL_DEBUG +// int8_t data0 = data.status(); + ftpTransfer transferStage0 = transferStage; + ftpCmd cmdStage0 = cmdStage; + ftpDataConn dataConn0 = dataConn; +#endif + + if ((int32_t) (millisDelay - millis()) <= 0) { + if (cmdStage == FTP_Stop) { + if (client.connected()) { + DEBUG_PRINTLN(F("Disconnect client!")); + disconnectClient(); + } + cmdStage = FTP_Init; + } else if (cmdStage == FTP_Init) { // Ftp server waiting for connection + abortTransfer(); + iniVariables(); + DEBUG_PRINT(F(" Ftp server waiting for connection on port ")); + DEBUG_PRINTLN(cmdPort); + + cmdStage = FTP_Client; + } else if (cmdStage == FTP_Client) { // Ftp server idle +#if (FTP_SERVER_NETWORK_TYPE == NETWORK_WiFiNINA) +// if (client && !client.connected()) { +// client.stop(); +// DEBUG_PRINTLN(F("CLIENT STOP!!")); +// } + byte status; + client = ftpServer.available(&status); + /* + * CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 + * + */ +// DEBUG_PRINTLN(status); +#elif (defined(ESP8266) && (FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)) + + if( ftpServer.hasClient()) + { + client.stop(); + client = ftpServer.available(); + } +#else + if (client && !client.connected()) { + client.stop(); + DEBUG_PRINTLN(F("CLIENT STOP!!")); + } + client = ftpServer.accept(); +#endif + if (client.connected()) // A client connected + { + clientConnected(); + millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait client id for 10 s. + cmdStage = FTP_User; + } + } else if (readChar() > 0) // got response + { + processCommand(); + if (cmdStage == FTP_Stop) + millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait authentication for 10 s. + else if (cmdStage < FTP_Cmd) + millisDelay = millis() + 200; // delay of 100 ms + else + millisEndConnection = millis() + 1000L * FTP_TIME_OUT; + } else if (!client.connected()) { + if (FtpServer::_callback) { + FtpServer::_callback(FTP_DISCONNECT, free(), capacity()); + } + + cmdStage = FTP_Init; + } + if (transferStage == FTP_Retrieve) // Retrieve data + { + if (!doRetrieve()) { + transferStage = FTP_Close; + } + } else if (transferStage == FTP_Store) // Store data + { + if (!doStore()) { + if (FtpServer::_callback) { + FtpServer::_callback(FTP_FREE_SPACE_CHANGE, free(), capacity()); + } + + transferStage = FTP_Close; + } + } else if (transferStage == FTP_List || transferStage == FTP_Nlst) // LIST or NLST + { + if (!doList()) { + transferStage = FTP_Close; + } + } else if (transferStage == FTP_Mlsd) // MLSD listing + { + if (!doMlsd()) { + + transferStage = FTP_Close; + } + } else if (cmdStage > FTP_Client + && !((int32_t) (millisEndConnection - millis()) > 0)) { + client.println(F("530 Timeout")); + millisDelay = millis() + 200; // delay of 200 ms + cmdStage = FTP_Stop; + } + +#ifdef FTP_ADDITIONAL_DEBUG + if (cmdStage != cmdStage0 || transferStage != transferStage0 + || dataConn != dataConn0) { + DEBUG_PRINT(F(" Command Old: ")); + DEBUG_PRINT(cmdStage0); + DEBUG_PRINT(F(" Transfer Old: ")); + DEBUG_PRINT(transferStage0); + DEBUG_PRINT(F(" Data Old: ")); + DEBUG_PRINTLN(dataConn0); + + DEBUG_PRINT(F(" Command : ")); + DEBUG_PRINT(cmdStage); + DEBUG_PRINT(F(" Transfer : ")); + DEBUG_PRINT(transferStage); + DEBUG_PRINT(F(" Data : ")); + DEBUG_PRINTLN(dataConn); + } +#endif + } + return cmdStage | (transferStage << 3) | (dataConn << 6); +} + +void FtpServer::clientConnected() +{ + DEBUG_PRINTLN( F(" Client connected!") ); + client.print(F("220---")); client.print(welcomeMessage); client.println(F(" ---")); + client.println(F("220--- By Renzo Mischianti ---")); + client.print(F("220 -- Version ")); client.print(FTP_SERVER_VERSION); client.println(F(" --")); + iCL = 0; + if (FtpServer::_callback) { + FtpServer::_callback(FTP_CONNECT, free(), capacity()); + } + +} + +void FtpServer::disconnectClient() +{ + DEBUG_PRINTLN( F(" Disconnecting client") ); + + abortTransfer(); + client.println(F("221 Goodbye") ); + + if (FtpServer::_callback) { + FtpServer::_callback(FTP_DISCONNECT, free(), capacity()); + } + + if( client ) { + } + if( data ) { + data.stop(); + } +} + +bool FtpServer::processCommand() +{ + /////////////////////////////////////// + // // + // AUTHENTICATION COMMANDS // + // // + /////////////////////////////////////// + + // RoSchmi added the next two lines + DEBUG_PRINT("Command is: "); + DEBUG_PRINTLN(command); + + // + // USER - User Identity + // + if( CommandIs( "USER" )) + { + DEBUG_PRINT(F("USER: ")); + DEBUG_PRINT(parameter); + DEBUG_PRINT(F(" ")); + DEBUG_PRINTLN(user) + + if (this->anonymousConnection && ! strcmp( parameter, user )) { + DEBUG_PRINTLN( F(" Anonymous authentication Ok. Waiting for commands.") ); + + client.println(F("230 Ok") ); + cmdStage = FTP_Cmd; + } else if( ! strcmp( parameter, user )) + { + client.println(F("331 Ok. Password required") ); + strcpy( cwdName, "/" ); + cmdStage = FTP_Pass; + } + else + { + client.println(F("530 ") ); + cmdStage = FTP_Stop; + } + } + // + // PASS - Password + // + else if( CommandIs( "PASS" )) + { + if( cmdStage != FTP_Pass ) + { + client.println(F("503 ") ); + cmdStage = FTP_Stop; + } + if( ! strcmp( parameter, pass )) + { + DEBUG_PRINTLN( F(" Authentication Ok. Waiting for commands.") ); + + client.println(F("230 Ok") ); + cmdStage = FTP_Cmd; + } + else + { + client.println( F("530 ") ); + cmdStage = FTP_Stop; + } + } + // + // FEAT - New Features + // + else if( CommandIs( "FEAT" )) + { + client.println(F("211-Extensions suported:")); + client.println(F(" MLST type*;modify*;size*;") ); + client.println(F(" MLSD") ); + client.println(F(" MDTM") ); + client.println(F(" MFMT") ); +#ifdef UTF8_SUPPORT + client.println(F(" UTF8") ); +#endif + client.println(F(" SIZE") ); + client.println(F(" SITE FREE") ); + client.println(F("211 End.") ); + } + // + // AUTH - Not implemented + // + else if( CommandIs( "AUTH" )) + client.println(F("502 ") ); + // + // Unrecognized commands at stage of authentication + // + else if( cmdStage < FTP_Cmd ) + { + client.println(F("530 ") ); + cmdStage = FTP_Stop; + } + + /////////////////////////////////////// + // // + // ACCESS CONTROL COMMANDS // + // // + /////////////////////////////////////// + + // + // PWD - Print Directory + // + else if( CommandIs( "PWD" ) || + ( CommandIs( "CWD" ) && ParameterIs( "." ))) { + client.print( F("257 \"")); client.print( cwdName ); client.print( F("\"") ); client.println( F(" is your current directory") ); + // + // CDUP - Change to Parent Directory + // + } else if( CommandIs( "CDUP" ) || + ( CommandIs( "CWD" ) && ParameterIs( ".." ))) + { + bool ok = false; + + if( strlen( cwdName ) > 1 ) // do nothing if cwdName is root + { + // if cwdName ends with '/', remove it (must not append) + if( cwdName[ strlen( cwdName ) - 1 ] == '/' ) { + cwdName[ strlen( cwdName ) - 1 ] = 0; + } + // search last '/' + char * pSep = strrchr( cwdName, '/' ); + ok = pSep > cwdName; + // if found, ends the string on its position + if( ok ) + { + * pSep = 0; + ok = exists( cwdName ); + } + } + // if an error appends, move to root + if( ! ok ) { + strcpy( cwdName, "/" ); + } + client.print( F("250 Ok. Current directory is ") ); client.println( cwdName ); + } + // + // CWD - Change Working Directory + // + else if( CommandIs( "CWD" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makeExistsPath( path )) + { + strcpy( cwdName, path ); + client.print( F("250 Directory changed to ") ); client.print(cwdName); client.println(); + } + } + // + // QUIT + // + else if( CommandIs( "QUIT" )) + { + client.println(F("221 Goodbye") ); + disconnectClient(); + cmdStage = FTP_Stop; + } + + /////////////////////////////////////// + // // + // TRANSFER PARAMETER COMMANDS // + // // + /////////////////////////////////////// + + // + // MODE - Transfer Mode + // + else if( CommandIs( "MODE" )) + { + if( ParameterIs( "S" )) { + client.println(F("200 S Ok") ); + } else { + client.println(F("504 Only S(tream) is suported") ); + } + } + // + // PASV - Passive Connection management + // + else if( CommandIs( "PASV" )) + { + data.stop(); + dataServer.begin(); + if (((((uint32_t) NET_CLASS.localIP()) & ((uint32_t) NET_CLASS.subnetMask())) == + (((uint32_t) client.remoteIP()) & ((uint32_t) NET_CLASS.subnetMask()))) && (uint32_t)localIp <= 0) { + dataIp = NET_CLASS.localIP(); + } else { + dataIp = localIp; + } + DEBUG_PRINT( F(" IP: ") ); + DEBUG_PRINT( int( dataIp[0]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[1]) ); DEBUG_PRINT( F(".") ); + DEBUG_PRINT( int( dataIp[2]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINTLN( int( dataIp[3]) ); + +#if !defined(ARDUINO_ARCH_RP2040) && ((FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266_ASYNC) || (FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266) || (FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266) || (FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP32)) // || (FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_WiFiNINA) || (FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN)) + if (dataIp.toString() == F("0.0.0.0")) { + dataIp = NET_CLASS.softAPIP(); + } +#endif + DEBUG_PRINT( F(" Soft IP: ") ); + DEBUG_PRINT( int( dataIp[0]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[1]) ); DEBUG_PRINT( F(".") ); + DEBUG_PRINT( int( dataIp[2]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINTLN( int( dataIp[3]) ); + + dataPort = pasvPort; + DEBUG_PRINTLN( F(" Connection management set to passive") ); + DEBUG_PRINT( F(" Listening at ") ); + DEBUG_PRINT( int( dataIp[0]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[1]) ); DEBUG_PRINT( F(".") ); + DEBUG_PRINT( int( dataIp[2]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[3]) ); + DEBUG_PRINT( F(":") ); DEBUG_PRINTLN( dataPort ); + + client.print( F("227 Entering Passive Mode") ); client.print( F(" (") ); + client.print( int( dataIp[0]) ); client.print( F(",") ); client.print( int( dataIp[1]) ); client.print( F(",") ); + client.print( int( dataIp[2]) ); client.print( F(",") ); client.print( int( dataIp[3]) ); client.print( F(",") ); + client.print( ( dataPort >> 8 ) ); client.print( F(",") ); client.print( ( dataPort & 255 ) ); client.println( F(")") ); + dataConn = FTP_Pasive; + } + // + // PORT - Data Port + // + else if( CommandIs( "PORT" )) + { + data.stop(); + // get IP of data client + dataIp[ 0 ] = atoi( parameter ); + char * p = strchr( parameter, ',' ); + for( uint8_t i = 1; i < 4; i ++ ) + { + dataIp[ i ] = atoi( ++ p ); + p = strchr( p, ',' ); + } + // get port of data client + dataPort = 256 * atoi( ++ p ); + p = strchr( p, ',' ); + dataPort += atoi( ++ p ); + if( p == NULL ) { + client.println(F("501 Can't interpret parameters") ); + } else + { + DEBUG_PRINT( F(" Data IP set to ") ); DEBUG_PRINT( int( dataIp[0]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[1]) ); + DEBUG_PRINT( F(".") ); DEBUG_PRINT( int( dataIp[2]) ); DEBUG_PRINT( F(".") ); DEBUG_PRINTLN( int( dataIp[3]) ); + DEBUG_PRINT( F(" Data port set to ") ); DEBUG_PRINTLN( dataPort ); + + client.println(F("200 PORT command successful") ); + dataConn = FTP_Active; + } + } + // + // STRU - File Structure + // + else if( CommandIs( "STRU" )) + { + if( ParameterIs( "F" )) { + client.println(F("200 F Ok") ); + // else if( ParameterIs( "R" )) + // client.println(F("200 B Ok") ); + }else{ + client.println(F("504 Only F(ile) is suported") ); + } + } + // + // TYPE - Data Type + // + else if( CommandIs( "TYPE" )) + { + if( ParameterIs( "A" )) { + client.println(F("200 TYPE is now ASCII")); + } else if( ParameterIs( "I" )) { + client.println(F("200 TYPE is now 8-bit binary") ); + } else { + client.println(F("504 Unknow TYPE") ); + } + } + + /////////////////////////////////////// + // // + // FTP SERVICE COMMANDS // + // // + /////////////////////////////////////// + + // + // ABOR - Abort + // + else if( CommandIs( "ABOR" )) + { + abortTransfer(); + client.println(F("226 Data connection closed")); + } + // + // DELE - Delete a File + // + else if( CommandIs( "DELE" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makeExistsPath( path )) { + if( remove( path )) { + if (FtpServer::_callback) { + FtpServer::_callback(FTP_FREE_SPACE_CHANGE, free(), capacity()); + } + + client.print( F("250 Deleted ") ); client.println( parameter ); + } else { + client.print( F("450 Can't delete ") ); client.println( parameter ); + } + } + } + // + // LIST - List + // NLST - Name List + // MLSD - Listing for Machine Processing (see RFC 3659) + // + else if( CommandIs( "LIST" ) || CommandIs( "NLST" ) || CommandIs( "MLSD" )) + { + DEBUG_PRINT("List of file!!"); + + if( dataConnect()){ + if( openDir( & dir )) + { + DEBUG_PRINT("Dir opened!!"); + + nbMatch = 0; + if( CommandIs( "LIST" )) + transferStage = FTP_List; + else if( CommandIs( "NLST" )) + transferStage = FTP_Nlst; + else + transferStage = FTP_Mlsd; + } + else { + DEBUG_PRINT("List Data stop!!"); + data.stop(); + } + } + } + // + // MLST - Listing for Machine Processing (see RFC 3659) + // + else if( CommandIs( "MLST" )) + { + char path[ FTP_CWD_SIZE ]; + uint16_t dat=0, tim=0; + char dtStr[ 15 ]; + bool isdir; + if( haveParameter() && makeExistsPath( path )){ + if( ! getFileModTime( path, &dat, &tim )) { + client.print( F("550 Unable to retrieve time for ") ); client.println( parameter ); + } else + { + isdir = isDir( path ); + client.println( F("250-Begin") ); + client.print( F(" Type=") ); client.print( ( isdir ? F("dir") : F("file")) ); + client.print( F(";Modify=") ); client.print( makeDateTimeStr( dtStr, dat, tim ) ); + if( ! isdir ) + { + if( openFile( path, FTP_FILE_READ )) + { + client.print( F(";Size=") ); client.print( long( fileSize( file )) ); + file.close(); + } + } + client.print( F("; ") ); client.println( path ); + client.println( F("250 End.") ); + } + } + } + // + // NOOP + // + else if( CommandIs( "NOOP" )) { + client.println(F("200 Zzz...") ); + } + // +#ifdef UTF8_SUPPORT + // OPTS + // + else if( CommandIs( "OPTS" )) { + if( ParameterIs( "UTF8 ON" ) || ParameterIs( "utf8 on" )) { + client.println(F("200 OK, UTF8 ON") ); + DEBUG_PRINTLN(F("200 OK, UTF8 ON") ); + } else { + client.println(F("504 Unknow OPTS") ); + DEBUG_PRINTLN(F("504 Unknow OPTS") ); + } + } + // +#endif + // HELP + // + else if( CommandIs( "HELP" )) { + client.println(F("200 Commands implemented:") ); + client.println(F(" USER, PASS, AUTH (AUTH only return 'not implemented' code)") ); + client.println(F(" CDUP, CWD, PWD, QUIT, NOOP") ); + client.println(F(" MODE, PASV, PORT, STRU, TYPE") ); + client.println(F(" ABOR, DELE, LIST, NLST, MLST, MLSD") ); + client.println(F(" APPE, RETR, STOR") ); + client.println(F(" MKD, RMD") ); + client.println(F(" RNTO, RNFR") ); + client.println(F(" MDTM, MFMT") ); + client.println(F(" FEAT, SIZE") ); + client.println(F(" SITE FREE") ); + client.println(F(" HELP") ); + } + // + // RETR - Retrieve + // + else if( CommandIs( "RETR" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makeExistsPath( path )) { + if( ! openFile( path, FTP_FILE_READ )) { + client.print( F("450 Can't open ") ); client.print( parameter ); + } else if( dataConnect( false )) + { + DEBUG_PRINT( F(" Sending ") ); DEBUG_PRINT( parameter ); DEBUG_PRINT( F(" size ") ); DEBUG_PRINTLN( long( fileSize( file )) ); + + if (FtpServer::_transferCallback) { + FtpServer::_transferCallback(FTP_DOWNLOAD_START, parameter, long( fileSize( file ))); + } + + + client.print( F("150-Connected to port ") ); client.println( dataPort ); + client.print( F("150 ") ); client.print( long( fileSize( file )) ); client.println( F(" bytes to download") ); + millisBeginTrans = millis(); + bytesTransfered = 0; + transferStage = FTP_Retrieve; + } + } + } + // + // STOR - Store + // APPE - Append + // + else if( CommandIs( "STOR" ) || CommandIs( "APPE" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makePath( path )) + { + bool open; + if( exists( path )) { + DEBUG_PRINTLN(F("APPEND FILE!!")); + open = openFile( path, ( CommandIs( "APPE" ) ? FTP_FILE_WRITE_APPEND : FTP_FILE_WRITE_CREATE )); + } else { + DEBUG_PRINTLN(F("CREATE FILE!!")); + open = openFile( path, FTP_FILE_WRITE_CREATE ); + } + + data.stop(); + data.flush(); + + DEBUG_PRINT(F("open/create ")); + DEBUG_PRINTLN(open); + if( ! open ){ + client.print( F("451 Can't open/create ") ); client.println( parameter ); + }else if( ! dataConnect()) // && !data.available()) + file.close(); + else + { + DEBUG_PRINT( F(" Receiving ") ); DEBUG_PRINTLN( parameter ); + + millisBeginTrans = millis(); + bytesTransfered = 0; + transferStage = FTP_Store; + + if (FtpServer::_transferCallback) { + + FtpServer::_transferCallback(FTP_UPLOAD_START, parameter, bytesTransfered); + } + + } + } + } + // + // MKD - Make Directory + // + else if( CommandIs( "MKD" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makePath( path )) + { + if( exists( path )) { + client.print( F("521 \"") ); client.print( parameter ); client.println( F("\" directory already exists") ); + } else + { + DEBUG_PRINT( F(" Creating directory ")); DEBUG_PRINTLN( parameter ); + +#if STORAGE_TYPE != STORAGE_SPIFFS + if( makeDir( path )) { + client.print( F("257 \"") ); client.print( parameter ); client.print( F("\"") ); client.println( F(" created") ); + } else { +#endif + client.print( F("550 Can't create \"") ); client.print( parameter ); client.println( F("\"") ); +#if STORAGE_TYPE != STORAGE_SPIFFS + } +#endif + } + } + } + // + // RMD - Remove a Directory + // + else if( CommandIs( "RMD" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makeExistsPath( path )) { + if( removeDir( path )) + { + DEBUG_PRINT( F(" Deleting ") ); DEBUG_PRINTLN( path ); + + client.print( F("250 \"") ); client.print( parameter ); client.println( F("\" deleted") ); + } + else { + client.print( F("550 Can't remove \"") ); client.print( parameter ); client.println( F("\". Directory not empty?") ); + } + } + } + // + // RNFR - Rename From + // + else if( CommandIs( "RNFR" )) + { + rnfrName[ 0 ] = 0; + if( haveParameter() && makeExistsPath( rnfrName )) + { + DEBUG_PRINT( F(" Ready for renaming ") ); DEBUG_PRINTLN( rnfrName ); + + client.println(F("350 RNFR accepted - file exists, ready for destination") ); + rnfrCmd = true; + } + } + // + // RNTO - Rename To + // + else if( CommandIs( "RNTO" )) + { + char path[ FTP_CWD_SIZE ]; + char dirp[ FTP_FIL_SIZE ]; + if( strlen( rnfrName ) == 0 || ! rnfrCmd ) { + client.println(F("503 Need RNFR before RNTO") ); + } else if( haveParameter() && makePath( path )) + { + if( exists( path )) { + client.print( F("553 ") ); client.print( parameter ); client.println( F(" already exists") ); + } else + { + strcpy( dirp, path ); + char * psep = strrchr( dirp, '/' ); + bool fail = psep == NULL; + if( ! fail ) + { + if( psep == dirp ) + psep ++; + * psep = 0; +// fail = ! isDir( dirp ); +// if( fail ) { +// client.print( F("550 \"") ); client.print( dirp ); client.println( F("\" is not directory") ); +// } else +// { + DEBUG_PRINT( F(" Renaming ") ); DEBUG_PRINT( rnfrName ); DEBUG_PRINT( F(" to ") ); DEBUG_PRINTLN( path ); + + if( rename( rnfrName, path )) + client.println(F("250 File successfully renamed or moved") ); + else + fail = true; +// } + } + if( fail ) + client.println(F("451 Rename/move failure") ); + } + } + rnfrCmd = false; + } + /* + // + // SYST - System + // + else if( CommandIs( "SYST" )) + FtpOutCli << F("215 MSDOS") << endl; + */ + + /////////////////////////////////////// + // // + // EXTENSIONS COMMANDS (RFC 3659) // + // // + /////////////////////////////////////// + + // + // MDTM && MFMT - File Modification Time (see RFC 3659) + // + else if( CommandIs( "MDTM" ) || CommandIs( "MFMT" )) + { + if( haveParameter()) + { + char path[ FTP_CWD_SIZE ]; + char * fname = parameter; + uint16_t year; + uint8_t month, day, hour, minute, second, setTime; + char dt[ 15 ]; + bool mdtm = CommandIs( "MDTM" ); + + setTime = getDateTime( dt, & year, & month, & day, & hour, & minute, & second ); + // fname point to file name + fname += setTime; + if( strlen( fname ) <= 0 ) { + client.println(F("501 No file name") ); + } else if( makeExistsPath( path, fname )) { + if( setTime ) // set file modification time + { + if( timeStamp( path, year, month, day, hour, minute, second )) { + client.print( F("213 ") ); client.println( dt ); + } else { + client.println(F("550 Unable to modify time" )); + } + } + else if( mdtm ) // get file modification time + { + uint16_t dat=0, tim=0; + char dtStr[ 15 ]; + if( getFileModTime( path, &dat, &tim )) { + client.print( F("213 ") ); client.println( makeDateTimeStr( dtStr, dat, tim ) ); + } else { + client.println("550 Unable to retrieve time" ); + } + } + } + } + } + // + // SIZE - Size of the file + // + else if( CommandIs( "SIZE" )) + { + char path[ FTP_CWD_SIZE ]; + if( haveParameter() && makeExistsPath( path )) { + if( ! openFile( path, FTP_FILE_READ )) { + client.print( F("450 Can't open ") ); client.println( parameter ); + } else + { + client.print( F("213 ") ); client.println( long( fileSize( file )) ); + file.close(); + } + } + } + // + // SITE - System command + // + else if( CommandIs( "SITE" )) + { + if( ParameterIs( "FREE" )) + { + uint32_t capa = capacity(); + if(( capa >> 10 ) < 1000 ) { // less than 1 Giga + client.print( F("200 ") ); client.print( free() ); client.print( F(" kB free of ") ); + client.print( capa ); client.println( F(" kB capacity") ); + }else { + client.print( F("200 ") ); client.print( ( free() >> 10 ) ); client.print( F(" MB free of ") ); + client.print( ( capa >> 10 ) ); client.println( F(" MB capacity") ); + } + } + else { + client.print( F("500 Unknow SITE command ") ); client.println( parameter ); + } + } + // + // Unrecognized commands ... + // + else + client.println(F("500 Unknow command") ); + return true; +} + +int FtpServer::dataConnect( bool out150 ) +{ + if( ! data.connected()) { + if( dataConn == FTP_Pasive ) + { + uint16_t count = 1000; // wait up to a second + while( ! data.connected() && count -- > 0 ) + { + #if (FTP_SERVER_NETWORK_TYPE == NETWORK_WiFiNINA) + data = dataServer.available(); + #elif (defined(ESP8266) && (FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242)) // || defined(ARDUINO_ARCH_RP2040) + if( dataServer.hasClient()) + { + data.stop(); + data = dataServer.available(); + } + #else + data = dataServer.accept(); + #endif + delay( 1 ); + } + } + else if( dataConn == FTP_Active ) + data.connect( dataIp, dataPort ); + } + +//#ifdef ESP8266 + if( ! ( data.connected() || data.available())) { +//#else +// if( ! ( data.connected() )) { +//#endif + client.println(F("425 No data connection")); + } else if( out150 ) { + client.print( F("150 Accepted data connection to port ") ); client.println( dataPort ); + } +//#ifdef ESP8266 + return data.connected() || data.available(); +//#else +// return data.connected(); +//#endif + +} + +bool FtpServer::dataConnected() +{ + if( data.connected()) + return true; + data.stop(); + client.println(F("426 Data connection closed. Transfer aborted") ); + transferStage = FTP_Close; + return false; +} + +bool FtpServer::openDir( FTP_DIR * pdir ) +{ + bool openD; +#if (STORAGE_TYPE == STORAGE_LITTLEFS && (defined(ESP8266) || defined(ARDUINO_ARCH_RP2040))) + if( cwdName == 0 ) { + dir = STORAGE_MANAGER.openDir( "/" ); + } else { + dir = STORAGE_MANAGER.openDir( cwdName ); + } + openD = dir.rewind(); + + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#elif STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + if( cwdName == 0 ) { + dir = STORAGE_MANAGER.open( "/" ); + } else { + dir = STORAGE_MANAGER.open( cwdName ); + } + openD = true; + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#elif STORAGE_TYPE == STORAGE_FFAT || (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32)) + if( cwdName == 0 ) { + dir = STORAGE_MANAGER.open( "/" ); + } else { + dir = STORAGE_MANAGER.open( cwdName ); + } + openD = true; + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#elif STORAGE_TYPE == STORAGE_SEEED_SD + if( cwdName == 0 ) { + DEBUG_PRINT("cwdName forced -> "); + DEBUG_PRINTLN(cwdName ); + + FTP_DIR d = STORAGE_MANAGER.open( "/" ); + dir=d; + } else { + DEBUG_PRINT("cwdName -> "); + DEBUG_PRINTLN(cwdName ); + + FTP_DIR d = STORAGE_MANAGER.open( cwdName ); + dir=d; + } + + openD = dir.isDirectory(); + + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#elif STORAGE_TYPE == STORAGE_SPIFFS + if( cwdName == 0 || strcmp(cwdName, "/") == 0 ) { + DEBUG_PRINT("DIRECTORY / EXIST "); +#if ESP8266 + dir = STORAGE_MANAGER.openDir( "/" ); +#else + dir = STORAGE_MANAGER.open( "/" ); +#endif + openD = true; + + } else { + openD = false; + } + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#else + if( cwdName == 0 ) { + openD = pdir->open( "/" ); + } else { + openD = pdir->open( cwdName ); + } + if( ! openD ) { + client.print( F("550 Can't open directory ") ); client.println( cwdName ); + } +#endif + return openD; +} + +bool FtpServer::doRetrieve() +{ + if( ! dataConnected()) + { + file.close(); + return false; + } + int16_t nb = file.read( buf, FTP_BUF_SIZE ); + if( nb > 0 ) + { + data.write( buf, nb ); + DEBUG_PRINT(F("NB --> ")); + DEBUG_PRINTLN(nb); + bytesTransfered += nb; + + if (FtpServer::_transferCallback) { + FtpServer::_transferCallback(FTP_DOWNLOAD, getFileName(&file), bytesTransfered); + } + +// RoSchmi +#if STORAGE_TYPE != STORAGE_SEEED_SD + return true; +#endif + } + closeTransfer(); + return false; +} + +bool FtpServer::doStore() +{ + int16_t na = data.available(); + if( na == 0 ) { + DEBUG_PRINTLN("NO DATA AVAILABLE!"); +#if FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_SEEED_RTL8720DN + data.stop(); +#endif + if( data.connected()) { + return true; + } else + { + closeTransfer(); + return false; + } + } + + if( na > FTP_BUF_SIZE ) { + na = FTP_BUF_SIZE; + } + int16_t nb = data.read((uint8_t *) buf, na ); + int16_t rc = 0; + if( nb > 0 ) + { + DEBUG_PRINT("NB -> "); + DEBUG_PRINTLN(nb); + + rc = file.write( buf, nb ); + DEBUG_PRINT("RC -> "); + DEBUG_PRINTLN(rc); + bytesTransfered += nb; + + if (FtpServer::_transferCallback) { + + FtpServer::_transferCallback(FTP_UPLOAD, getFileName(&file), bytesTransfered); + } + } + if( nb < 0 || rc == nb ) { + return true; + } + client.println(F("552 Probably insufficient storage space") ); + file.close(); + data.stop(); + return false; +} + +void generateFileLine(FTP_CLIENT_NETWORK_CLASS* data, bool isDirectory, const char* fn, long fz, const char* time, const char* user, bool writeFilename = true) { + if( isDirectory ) { + // data.print( F("+/,\t") ); + // DEBUG_PRINT(F("+/,\t")); + + data->print( F("drwxrwsr-x\t2\t")); + data->print( user ); + data->print( F("\t") ); + data->print( long( 4096 ) ); + data->print( F("\t") ); + + DEBUG_PRINT( F("drwxrwsr-x\t2\t") ); + DEBUG_PRINT( user ); + DEBUG_PRINT( F("\t") ); + + DEBUG_PRINT( long( 4096 ) ); + DEBUG_PRINT( F("\t") ); + + data->print(time); + DEBUG_PRINT(time); + + data->print( F("\t") ); + if (writeFilename) data->println( fn ); + + DEBUG_PRINT( F("\t") ); + if (writeFilename) DEBUG_PRINTLN( fn ); + + } else { +// data.print( F("+r,s") ); +// DEBUG_PRINT(F("+r,s")); + + data->print( F("-rw-rw-r--\t1\t") ); + data->print( user ); + data->print( F("\t") ); + data->print( fz ); + data->print( F("\t") ); + + DEBUG_PRINT( F("-rw-rw-r--\t1\t") ); + DEBUG_PRINT( user ); + DEBUG_PRINT( F("\t") ); + DEBUG_PRINT( fz ); + DEBUG_PRINT( F("\t") ); + + data->print(time); + DEBUG_PRINT(time); + + data->print( F("\t") ); + if (writeFilename) data->println( fn ); + + DEBUG_PRINT( F("\t") ); + if (writeFilename) DEBUG_PRINTLN( fn ); + } + +} + +#if defined(ESP32) || defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) +// +// Formats printable String from a time_t timestamp +// +String makeDateTimeStrList(time_t ft, bool dateContracted = false) +{ + String tmp; + // a buffer with enough space for the formats + char buf[25]; + char *b = buf; + + // break down the provided file time + struct tm _tm; + gmtime_r(&ft, &_tm); + + if (dateContracted) + { + // "%Y%m%d%H%M%S", e.g. "20200517123400" + strftime(b, sizeof(buf), "%Y%m%d%H%M%S", &_tm); + } + else + { + // "%h %d %H:%M", e.g. "May 17 12:34" for file dates of the current year + // "%h %d %Y" , e.g. "May 17 2019" for file dates of any other years + + // just convert both ways, select later what's to be shown + // buf becomes "May 17 2019May 17 12:34" + strftime(b, sizeof(buf), "%h %d %H:%M%h %d %Y", &_tm); + + // check for a year != year from now + int fileYear = _tm.tm_year; + time_t nowTime = time(NULL); + gmtime_r(&nowTime, &_tm); + if (fileYear == _tm.tm_year) + { + // cut off 2nd half - year variant + b[12] = '\0'; + } + else + { + // skip 1st half - time variant + b += 12; + } + } + tmp = b; + return tmp; +} + +// https://files.stairways.com/other/ftp-list-specs-info.txt +void generateFileLine(FTP_CLIENT_NETWORK_CLASS* data, bool isDirectory, const char* fn, long fz, time_t time, const char* user, bool writeFilename = true) { + generateFileLine(data, isDirectory, fn, fz, makeDateTimeStrList(time).c_str(), user, writeFilename); +} +#endif + +bool FtpServer::doList() +{ + if( ! dataConnected()) + { +#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD + dir.close(); +#endif + return false; + } +#if STORAGE_TYPE == STORAGE_SPIFFS + #if ESP8266 + if( dir.next()) + #else + FTP_FILE fileDir = dir.openNextFile(); + if( fileDir ) + #endif + { + +// data.print( F("+r,s") ); +// #if ESP8266 +// data.print( long( dir.fileSize()) ); +// data.print( F(",\t") ); +// data.println( dir.fileName() ); +// #else +// data.print( long( fileDir.size()) ); +// data.print( F(",\t") ); +// data.println( fileDir.name() ); +// #endif + + + +#ifdef ESP8266 + String fn = dir.fileName(); + long fz = long( dir.fileSize()); + if (fn[0]=='/') { fn.remove(0, fn.lastIndexOf("/")+1); } + time_t time = dir.fileTime(); + generateFileLine(&data, false, fn.c_str(), fz, time, this->user); +#else + long fz = long( fileDir.size()); + const char* fnC = fileDir.name(); + const char* fn; + if ( fnC[0] == '/' ) { + fn = &fnC[1]; + }else{ + fn = fnC; + } + + time_t time = fileDir.getLastWrite(); + generateFileLine(&data, false, fn, fz, time, this->user); + +#endif + + nbMatch ++; + return true; + } +#elif STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + if( dir.next()) + #else +#if STORAGE_TYPE == STORAGE_SEEED_SD + FTP_FILE fileDir = STORAGE_MANAGER.open(dir.name()); + fileDir = dir.openNextFile(); +#else + FTP_FILE fileDir = dir.openNextFile(); +#endif + if( fileDir ) +#endif + { + + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + long fz = long( dir.fileSize()); +// const char* fn = dir.fileName().c_str(); + String aza = dir.fileName(); + const char* fn = aza.c_str(); //Serial.printf("test %s ", fn); + +// data.print( long( dir.fileSize()) ); +// data.print( F(",\t") ); +// data.println( dir.fileName() ); + #elif STORAGE_TYPE == STORAGE_SEEED_SD + const char* fnC = fileDir.name(); + const char* fn; + if ( fnC[0] == '/' ) { + fn = &fnC[1]; + }else{ + fn = fnC; + } + long fz = fileDir.size(); + #else + long fz = long( fileDir.size()); + const char* fn = fileDir.name(); + +// data.print( long( fileDir.size()) ); +// data.print( F("\t") ); +// data.println( fileDir.name() ); + +// DEBUG_PRINT( long( fileDir.size())); +// DEBUG_PRINT( F("\t") ); +// DEBUG_PRINTLN( fileDir.name() ); + #endif + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + time_t time = dir.fileTime(); + generateFileLine(&data, dir.isDirectory(), fn, fz, time, this->user); + #elif ESP32 + time_t time = fileDir.getLastWrite(); + generateFileLine(&data, fileDir.isDirectory(), fn, fz, time, this->user); + #else + generateFileLine(&data, fileDir.isDirectory(), fn, fz, "Jan 01 00:00", this->user); + #endif + nbMatch ++; + return true; + } +#elif STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + FTP_FILE fileDir = dir.openNextFile(); + if( fileDir ) + { + +// data.print( F("+r,s") ); +// data.print( long( fileDir.size()) ); +// data.print( F(",\t") ); +// data.println( fileDir.name() ); + + String fn = fileDir.name(); + if (fn[0]=='/') { fn.remove(0, fn.lastIndexOf("/")+1); } + + generateFileLine(&data, fileDir.isDirectory(), fn.c_str(), long( fileDir.size()), "Jan 01 00:00", this->user); + + nbMatch ++; + return true; + } + +#elif STORAGE_TYPE == STORAGE_FATFS + if( dir.nextFile()) + { +// if( dir.isDir()) { +// data.print( F("+/,\t") ); +// } else { +// data.print( F("+r,s") ); data.print( long( dir.fileSize()) ); data.print( F(",\t") ); +// } +// data.println( dir.fileName() ); + + String fn = dir.fileName(); + if (fn[0]=='/') { fn.remove(0, fn.lastIndexOf("/")+1); } + + generateFileLine(&data, dir.isDir(), fn.c_str(), long( dir.fileSize()), "Jan 01 00:00", this->user); + + nbMatch ++; + return true; + } +#else + if( file.openNext( &dir, FTP_FILE_READ_ONLY )) + { +// if( file.isDir()) { +// data.print( F("+/,\t") ); +// } else { +// data.print( F("+r,s") ); data.print( long( fileSize( file )) ); data.print( F(",\t") ); +// } + + generateFileLine(&data, file.isDir(), "", long( fileSize( file )), "Jan 01 00:00", this->user, false); + + file.printName( & data ); + data.println(); + file.close(); + nbMatch ++; + return true; + } +#endif + client.print( F("226 ") ); client.print( nbMatch ); client.println( F(" matches total") ); +#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD + dir.close(); +#endif + data.stop(); + return false; +} + +bool FtpServer::doMlsd() +{ + if( ! dataConnected()) + { +#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD + dir.close(); +#endif + DEBUG_PRINTLN(F("Not connected!!")); + return false; + } + DEBUG_PRINTLN(F("Connected!!")); + +#if STORAGE_TYPE == STORAGE_SPIFFS + DEBUG_PRINTLN("DIR MLSD "); + #if ESP8266 + if( dir.next()) + #else + File fileDir = dir.openNextFile(); + if( fileDir ) + #endif + { + DEBUG_PRINTLN("DIR NEXT "); + char dtStr[ 15 ]; + + struct tm * timeinfo; + + #if ESP8266 + time_t time = dir.fileTime(); + #else + time_t time = fileDir.getLastWrite(); + #endif + + timeinfo = localtime ( &time ); + + // 2000 01 01 16 06 56 + + strftime (dtStr,15,"%Y%m%d%H%M%S",timeinfo); + + + #if ESP8266 + String fn = dir.fileName(); + fn.remove(0, fn.lastIndexOf("/")+1); + long fz = dir.fileSize(); + #else + String fn = fileDir.name(); + fn.remove(0, fn.lastIndexOf("/")+1); + long fz = fileDir.size(); + #endif + + data.print( F("Type=") ); + + data.print( F("file") ); + data.print( F(";Modify=") ); data.print(dtStr);// data.print( makeDateTimeStr( dtStr, time, time) ); + data.print( F(";Size=") ); data.print( fz ); + data.print( F("; ") ); data.println( fn ); + + DEBUG_PRINT( F("Type=") ); + DEBUG_PRINT( F("file") ); + + DEBUG_PRINT( F(";Modify=") ); DEBUG_PRINT(dtStr); //DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) ); + DEBUG_PRINT( F(";Size=") ); DEBUG_PRINT( fz ); + DEBUG_PRINT( F("; ") ); DEBUG_PRINTLN( fn ); + + nbMatch ++; + return true; + } +#elif STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT + DEBUG_PRINTLN("DIR MLSD "); + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + if( dir.next()) + #else +#if STORAGE_TYPE == STORAGE_SEEED_SD + File fileDir = STORAGE_MANAGER.open(dir.name()); + fileDir = dir.openNextFile(); +#else + File fileDir = dir.openNextFile(); +#endif + DEBUG_PRINTLN(dir); + DEBUG_PRINTLN(fileDir); + if( fileDir ) + #endif + { + DEBUG_PRINTLN("DIR NEXT "); + char dtStr[ 15 ]; + + + #if STORAGE_TYPE == STORAGE_SEEED_SD + struct tm * timeinfo; + + strcpy(dtStr, "19700101000000"); + #else + struct tm * timeinfo; + + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + time_t time = dir.fileTime(); + #else + time_t time = fileDir.getLastWrite(); + #endif + + timeinfo = localtime ( &time ); + + // 2000 01 01 16 06 56 + + strftime (dtStr,15,"%Y%m%d%H%M%S",timeinfo); + #endif + + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + String fn = dir.fileName(); + long fz = dir.fileSize(); + FTP_DIR fileDir = dir; + #elif STORAGE_TYPE == STORAGE_SEEED_SD + String fn = fileDir.name(); + fn.remove(0, strlen(dir.name())); + if (fn[0]=='/') { fn.remove(0, fn.lastIndexOf("/")+1); } + long fz = fileDir.size(); + #else + String fn = fileDir.name(); + fn.remove(0, fn.lastIndexOf("/")+1); + long fz = fileDir.size(); + #endif + + data.print( F("Type=") ); + + data.print( ( fileDir.isDirectory() ? F("dir") : F("file")) ); + data.print( F(";Modify=") ); data.print(dtStr);// data.print( makeDateTimeStr( dtStr, time, time) ); + data.print( F(";Size=") ); data.print( fz ); + data.print( F("; ") ); data.println( fn ); + + DEBUG_PRINT( F("Type=") ); + DEBUG_PRINT( ( fileDir.isDirectory() ? F("dir") : F("file")) ); + + DEBUG_PRINT( F(";Modify=") ); DEBUG_PRINT(dtStr); //DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) ); + DEBUG_PRINT( F(";Size=") ); DEBUG_PRINT( fz ); + DEBUG_PRINT( F("; ") ); DEBUG_PRINTLN( fn ); + + nbMatch ++; +// RoSchmi: next line was commented +#if STORAGE_TYPE == STORAGE_SEEED_SD + fileDir.close(); +#endif + return true; + } + +#elif STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + DEBUG_PRINTLN("DIR MLSD "); + File fileDir = dir.openNextFile(); + if( fileDir ) + { + DEBUG_PRINTLN("DIR NEXT "); + char dtStr[ 15 ]; + + struct tm * timeinfo; + + strcpy(dtStr, "19700101000000"); + + +// long fz = dir.fileSize(); + String fn = fileDir.name(); + +//#ifdef ESP32 + fn.remove(0, fn.lastIndexOf("/")+1); +//#else if !defined(ESP8266) +// fn.remove(0, 1); +//#endif + + + long fz = fileDir.size(); + + data.print( F("Type=") ); + + data.print( ( fileDir.isDirectory() ? F("dir") : F("file")) ); + data.print( F(";Modify=") ); data.print(dtStr);// data.print( makeDateTimeStr( dtStr, time, time) ); + data.print( F(";Size=") ); data.print( fz ); + data.print( F("; ") ); data.println( fn ); + + DEBUG_PRINT( F("Type=") ); + DEBUG_PRINT( ( fileDir.isDirectory() ? F("dir") : F("file")) ); + + DEBUG_PRINT( F(";Modify=") ); DEBUG_PRINT(dtStr); //DEBUG_PRINT( makeDateTimeStr( dtStr, time, time) ); + DEBUG_PRINT( F(";Size=") ); DEBUG_PRINT( fz ); + DEBUG_PRINT( F("; ") ); DEBUG_PRINTLN( fn ); + + nbMatch ++; + return true; + } + +#elif STORAGE_TYPE == STORAGE_FATFS + if( dir.nextFile()) + { + char dtStr[ 15 ]; + data.print( F("Type=") ); data.print( ( dir.isDir() ? F("dir") : F("file")) ); + data.print( F(";Modify=") ); data.print( makeDateTimeStr( dtStr, dir.fileModDate(), dir.fileModTime()) ); + data.print( F(";Size=") ); data.print( long( dir.fileSize()) ); + data.print( F("; ") ); data.println( dir.fileName() ); + nbMatch ++; + return true; + } +#else + if( file.openNext( &dir, FTP_FILE_READ_ONLY )) + { + char dtStr[ 15 ]; + uint16_t filelwd, filelwt; + bool gfmt = getFileModTime( & filelwd, & filelwt ); + DEBUG_PRINT("gfmt --> "); + DEBUG_PRINTLN(gfmt); + if( gfmt ) + { + data.print( F("Type=") ); data.print( ( file.isDir() ? F("dir") : F("file")) ); + data.print( F(";Modify=") ); data.print( makeDateTimeStr( dtStr, filelwd, filelwt ) ); + data.print( F(";Size=") ); data.print( long( fileSize( file )) ); data.print( F("; ") ); + file.printName( & data ); + data.println(); + + DEBUG_PRINT( F("Type=") ); DEBUG_PRINT( ( file.isDir() ? F("dir") : F("file")) ); + DEBUG_PRINT( F(";Modify=") ); DEBUG_PRINT( makeDateTimeStr( dtStr, filelwd, filelwt ) ); + DEBUG_PRINT( F(";Size=") ); DEBUG_PRINT( long( fileSize( file )) ); DEBUG_PRINT( F("; ") ); +// DEBUG_PRINT(file.name()); + DEBUG_PRINTLN(); + nbMatch ++; + } + file.close(); + return gfmt; + } +#endif + client.println(F("226-options: -a -l") ); + client.print( F("226 ") ); client.print( nbMatch ); client.println( F(" matches total") ); +#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD && STORAGE_TYPE != STORAGE_SEEED_SD + dir.close(); +#endif + data.stop(); + DEBUG_PRINTLN(F("All file readed!!")); + return false; +} + +void FtpServer::closeTransfer() +{ + uint32_t deltaT = (int32_t) ( millis() - millisBeginTrans ); + if( deltaT > 0 && bytesTransfered > 0 ) + { + DEBUG_PRINT( F(" Transfer completed in ") ); DEBUG_PRINT( deltaT ); DEBUG_PRINTLN( F(" ms, ") ); + DEBUG_PRINT( bytesTransfered / deltaT ); DEBUG_PRINTLN( F(" kbytes/s") ); + + if (FtpServer::_transferCallback) { + FtpServer::_transferCallback(FTP_TRANSFER_STOP, getFileName(&file), bytesTransfered); + } + + + client.println(F("226-File successfully transferred") ); + client.print( F("226 ") ); client.print( deltaT ); client.print( F(" ms, ") ); + client.print( bytesTransfered / deltaT ); client.println( F(" kbytes/s") ); + } + else + client.println(F("226 File successfully transferred") ); + + file.close(); + data.stop(); +} + +void FtpServer::abortTransfer() +{ + if( transferStage != FTP_Close ) + { + if (FtpServer::_transferCallback) { + FtpServer::_transferCallback(FTP_TRANSFER_ERROR, getFileName(&file), bytesTransfered); + } + + file.close(); +#if STORAGE_TYPE != STORAGE_SPIFFS && STORAGE_TYPE != STORAGE_LITTLEFS && STORAGE_TYPE != STORAGE_SEEED_SD + dir.close(); +#endif + client.println(F("426 Transfer aborted") ); + DEBUG_PRINTLN( F(" Transfer aborted!") ); + + transferStage = FTP_Close; + } +// if( data.connected()) + data.stop(); +} + +// Read a char from client connected to ftp server +// +// update cmdLine and command buffers, iCL and parameter pointers +// +// return: +// -2 if buffer cmdLine is full +// -1 if line not completed +// 0 if empty line received +// length of cmdLine (positive) if no empty line received + +int8_t FtpServer::readChar() +{ + int8_t rc = -1; + + if( client.available()) + { + char c = client.read(); + DEBUG_PRINT("-"); + DEBUG_PRINT( c ); + + if( c == '\\' ) + c = '/'; + if( c != '\r' ){ + if( c != '\n' ) + { + if( iCL < FTP_CMD_SIZE ) + cmdLine[ iCL ++ ] = c; + else + rc = -2; // Line too long + } + else + { + cmdLine[ iCL ] = 0; + command[ 0 ] = 0; + parameter = NULL; + // empty line? + if( iCL == 0 ) + rc = 0; + else + { + rc = iCL; + // search for space between command and parameter + parameter = strchr( cmdLine, ' ' ); + if( parameter != NULL ) + { + if( parameter - cmdLine > 4 ) + rc = -2; // Syntax error + else + { + strncpy( command, cmdLine, parameter - cmdLine ); + command[ parameter - cmdLine ] = 0; + while( * ( ++ parameter ) == ' ' ) + ; + } + } + else if( strlen( cmdLine ) > 4 ) + rc = -2; // Syntax error. + else + strcpy( command, cmdLine ); + iCL = 0; + } + } + } + if( rc > 0 ) + for( uint8_t i = 0 ; i < strlen( command ); i ++ ) + command[ i ] = toupper( command[ i ] ); + if( rc == -2 ) + { + iCL = 0; + client.println(F("500 Syntax error")); + } + } + return rc; +} + +bool FtpServer::haveParameter() +{ + if( parameter != NULL && strlen( parameter ) > 0 ) + return true; + client.println("501 No file name" ); + return false; +} + +int utf8_strlen(const String& str) +{ + int c,i,ix,q; + for (q=0, i=0, ix=str.length(); i < ix; i++, q++) + { + c = (unsigned char) str[i]; + if (c>=0 && c<=127) i+=0; + else if ((c & 0xE0) == 0xC0) i+=1; + else if ((c & 0xF0) == 0xE0) i+=2; + else if ((c & 0xF8) == 0xF0) i+=3; + //else if (($c & 0xFC) == 0xF8) i+=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8 + //else if (($c & 0xFE) == 0xFC) i+=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8 + else return 0;//invalid utf8 + } + return q; +} + +//// ****** UTF8-Decoder: convert UTF8-string to extended ASCII ******* +//static byte c1; // Last character buffer +// +//// Convert a single Character from UTF8 to Extended ASCII +//// Return "0" if a byte has to be ignored +//byte utf8ascii(byte ascii) { +// if ( ascii<128 ) // Standard ASCII-set 0..0x7F handling +// { c1=0; +// return( ascii ); +// } +// +// // get previous input +// byte last = c1; // get last char +// c1=ascii; // remember actual character +// +// switch (last) // conversion depending on first UTF8-character +// { case 0xC2: return (ascii); break; +// case 0xC3: return (ascii | 0xC0); break; +// case 0x82: if(ascii==0xAC) return(0x80); // special case Euro-symbol +// } +// +// return (0); // otherwise: return zero, if character has to be ignored +//} +// +//// convert String object from UTF8 String to Extended ASCII +//String utf8ascii(String s) +//{ +// String r=""; +// char c; +// for (int i=0; i 1 ) + fullName[ strl ] = 0; + if( strlen( fullName ) >= FTP_CWD_SIZE ) + { + client.println(F("500 Command line too long")); + return false; + } +#ifdef UTF8_SUPPORT +// for( uint8_t i = 0; i < utf8_strlen( fullName ); i ++ ) { +// +// } + + DEBUG_PRINT(F("utf8_strlen")); + DEBUG_PRINTLN(utf8_strlen(fullName)); +// DEBUG_PRINT(F("utf8_strlen2")); +// DEBUG_PRINTLN(utf8_strlen2(fullName)); + + if (utf8_strlen(fullName)>=FILENAME_LENGTH) { + client.println(F("553 File name not allowed. Too long.") ); + return false; + } +#else + for( uint8_t i = 0; i < strlen( fullName ); i ++ ) { + if( ! legalChar( fullName[i])) + { + client.println(F("553 File name not allowed") ); + return false; + } + } + if (strlen(fullName)>=FILENAME_LENGTH) { + client.println(F("553 File name not allowed. Too long.") ); + return false; + } +#endif + return true; +} + +bool FtpServer::makeExistsPath( char * path, char * param ) +{ + if( ! makePath( path, param )) + return false; + // RoSchmi + //#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_SD +#if (STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC || STORAGE_TYPE == STORAGE_SEEED_SD) + if (strcmp(path, "/") == 0) return true; +#endif + DEBUG_PRINT("PATH --> ") + DEBUG_PRINT(path) + if( exists( path )) { + DEBUG_PRINTLN(" ...EXIST!") + return true; + } + DEBUG_PRINTLN(" ...NOT EXIST!") + client.print(F("550 ")); client.print( path ); client.println( F(" not found.") ); + return false; +} + +// Calculate year, month, day, hour, minute and second +// from first parameter sent by MDTM command (YYYYMMDDHHMMSS) +// Accept longer parameter YYYYMMDDHHMMSSmmm where mmm are milliseconds +// but don't take in account additional digits +// +// parameters: +// dt: 15 length string for 14 digits and terminator +// pyear, pmonth, pday, phour, pminute and psecond: pointer of +// variables where to store data +// +// return: +// 0 if parameter is not YYYYMMDDHHMMSS +// length of parameter + space +// +// Date/time are expressed as a 14 digits long string +// terminated by a space and followed by name of file + +uint8_t FtpServer::getDateTime( char * dt, uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, + uint8_t * phour, uint8_t * pminute, uint8_t * psecond ) +{ + uint8_t i; + dt[ 0 ] = 0; + if( strlen( parameter ) < 15 ) //|| parameter[ 14 ] != ' ' ) + return 0; + for( i = 0; i < 14; i ++ ) + if( ! isdigit( parameter[ i ])) + return 0; + for( i = 14; i < 18; i ++ ) + if( parameter[ i ] == ' ' ) + break; + else if( ! isdigit( parameter[ i ])) + return 0; + if( i == 18 ) + return 0; + i ++ ; + + strncpy( dt, parameter, 14 ); + dt[ 14 ] = 0; + * psecond = atoi( dt + 12 ); + dt[ 12 ] = 0; + * pminute = atoi( dt + 10 ); + dt[ 10 ] = 0; + * phour = atoi( dt + 8 ); + dt[ 8 ] = 0; + * pday = atoi( dt + 6 ); + dt[ 6 ] = 0 ; + * pmonth = atoi( dt + 4 ); + dt[ 4 ] = 0 ; + * pyear = atoi( dt ); + strncpy( dt, parameter, 14 ); + DEBUG_PRINT( F(" Modification time: ") ); DEBUG_PRINT( * pyear ); DEBUG_PRINT( F("/") ); DEBUG_PRINT( int(* pmonth) ); DEBUG_PRINT( F("/") ); DEBUG_PRINT( int(* pday) ); + DEBUG_PRINT( F(" ") ); DEBUG_PRINT( int(* phour) ); DEBUG_PRINT( F(":") ); DEBUG_PRINT( int(* pminute) ); DEBUG_PRINT( F(":") ); DEBUG_PRINT( int(* psecond) ); + DEBUG_PRINT( F(" of file: ") ); DEBUG_PRINTLN( (char *) ( parameter + i ) ); + + return i; +} + +// Create string YYYYMMDDHHMMSS from date and time +// +// parameters: +// date, time +// tstr: where to store the string. Must be at least 15 characters long +// +// return: +// pointer to tstr + +char * FtpServer::makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ) +{ + sprintf( tstr, "%04u%02u%02u%02u%02u%02u", + (( date & 0xFE00 ) >> 9 ) + 1980, ( date & 0x01E0 ) >> 5, date & 0x001F, + ( time & 0xF800 ) >> 11, ( time & 0x07E0 ) >> 5, ( time & 0x001F ) << 1 ); + return tstr; +} + + +uint32_t FtpServer::fileSize( FTP_FILE file ) { +#if (STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT || STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC || STORAGE_TYPE == STORAGE_SEEED_SD) + return file.size(); +#else + return file.fileSize(); +#endif +} + +#if (STORAGE_TYPE == STORAGE_SEEED_SD) + bool FtpServer::openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ){ + DEBUG_PRINT(F("File to open ") ); + DEBUG_PRINT( path ); + DEBUG_PRINT(F(" readType ") ); + DEBUG_PRINTLN(readTypeInt); + + if (readTypeInt == 0X01) { + readTypeInt = FILE_READ; + }else { + readTypeInt = FILE_WRITE; + } + + file = STORAGE_MANAGER.open( path, readTypeInt ); + if (!file) { // && readTypeInt[0]==FILE_READ) { + return false; + }else{ + DEBUG_PRINTLN("TRUE"); + + return true; + } +} +#elif ((STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC) && defined(ESP8266))// FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266_242) + bool FtpServer::openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ){ + DEBUG_PRINT(F("File to open ") ); + DEBUG_PRINT( path ); + DEBUG_PRINT(F(" readType ") ); + DEBUG_PRINTLN(readTypeInt); + + if (readTypeInt == 0X01) { + readTypeInt = FILE_READ; + }else { + readTypeInt = FILE_WRITE; + } + + file = STORAGE_MANAGER.open( path, readTypeInt ); + if (!file) { // && readTypeInt[0]==FILE_READ) { + return false; + }else{ + DEBUG_PRINTLN("TRUE"); + + return true; + } +} +#elif (STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT ) + bool FtpServer::openFile( const char * path, const char * readType ) { + DEBUG_PRINT(F("File to open ") ); + DEBUG_PRINT( path ); + DEBUG_PRINT(F(" readType ") ); + DEBUG_PRINTLN(readType); + file = STORAGE_MANAGER.open( path, readType ); + if (!file && readType[0]=='r') { + return false; + }else{ + DEBUG_PRINTLN("TRUE"); + + return true; + } + } +#elif STORAGE_TYPE <= STORAGE_SDFAT2 || STORAGE_TYPE == STORAGE_SPIFM || ((STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC) && ARDUINO_ARCH_SAMD) + bool FtpServer::openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ){ + DEBUG_PRINT(F("File to open ") ); + DEBUG_PRINT( path ); + DEBUG_PRINT(F(" readType ") ); + DEBUG_PRINTLN(readTypeInt); + + file = STORAGE_MANAGER.open( path, readTypeInt ); + if (!file) { + return false; + }else{ + DEBUG_PRINTLN("TRUE"); + + return true; + } +} + +#else + bool FtpServer::openFile( char path[ FTP_CWD_SIZE ], const char * readType ) { + return openFile( (const char*) path, readType ); + } + bool FtpServer::openFile( const char * path, const char * readType ) { + DEBUG_PRINT(F("File to open ") ); + DEBUG_PRINT( path ); + DEBUG_PRINT(F(" readType ") ); + DEBUG_PRINTLN(readType); + #if ((STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC) && !defined(ESP32)) + if (readType == 0X01) { + readType = FILE_READ; + }else { + readType = FILE_WRITE; + } + #endif + file = STORAGE_MANAGER.open( path, readType ); + if (!file && readType[0]=='r') { + return false; + }else{ + DEBUG_PRINTLN("TRUE"); + + return true; + } + } +#endif + +// Return true if path points to a directory +bool FtpServer::isDir( char * path ) +{ +#if (STORAGE_TYPE == STORAGE_LITTLEFS && (defined(ESP8266) || defined(ARDUINO_ARCH_RP2040))) + FTP_DIR dir; + bool res; + dir = STORAGE_MANAGER.openDir( path ); + + res = true; + return res; + + #elif STORAGE_TYPE == STORAGE_SPIFFS + if (strcmp(path, "/") == 0) { return true; } + return false; // no directory support +#elif STORAGE_TYPE == STORAGE_SEEED_SD || STORAGE_TYPE == STORAGE_FFAT || (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32)) + FTP_DIR dir; + bool res; + dir = STORAGE_MANAGER.open( path ); + +// return true; + res = dir.isDirectory(); + return res; +#elif STORAGE_TYPE == STORAGE_FATFS + return STORAGE_MANAGER.isDir( path ); +#elif STORAGE_TYPE == STORAGE_SDFAT1 || STORAGE_TYPE == STORAGE_SDFAT2 +// bool res = (!dir.open(path, FTP_FILE_READ) || !dir.isDir()); +// dir.close(); +// return res; + if (strcmp(path, "/") == 0) { return true; } + if( ! openFile( path, FTP_FILE_READ )) { + return false; + } + return true; +#else + FTP_FILE file; + bool res; + + if( ! openFile( path, FTP_FILE_READ )) { + return false; + } +#if STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC +// if (strcmp(path, "/") == 0) return true; +// res = file.isDirectory(); +// DEBUG_PRINT(path); +// DEBUG_PRINT(" IS DIRECOTORY --> "); +// DEBUG_PRINTLN(res); + return true; +#else +// res = file.isDir(); +// DEBUG_PRINT("IS DIRECTORY --> " ); +// DEBUG_PRINTLN(res); +#endif + file.close(); + return res; +#endif +} + +bool FtpServer::timeStamp( char * path, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second ) +{ +#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT || STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC || STORAGE_TYPE == STORAGE_SEEED_SD +// struct tm tmDate = { second, minute, hour, day, month, year }; +// time_t rawtime = mktime(&tmDate); + + return true; + // setTime(rawtime); + // SPIFFS USE time() call +// return STORAGE_MANAGER.timeStamp( path, year, month, day, hour, minute, second ); +#elif STORAGE_TYPE == STORAGE_FATFS + return STORAGE_MANAGER.timeStamp( path, year, month, day, hour, minute, second ); +#else + FTP_FILE file; + bool res; + + if( ! openFile( path, FTP_FILE_READ_WRITE )) + return false; + res = file.timestamp( T_WRITE, year, month, day, hour, minute, second ); + file.close(); + return res; +#endif +} + +bool FtpServer::getFileModTime( char * path, uint16_t * pdate, uint16_t * ptime ) +{ +#if STORAGE_TYPE == STORAGE_FATFS + return STORAGE_MANAGER.getFileModTime( path, pdate, ptime ); +#else +// FTP_FILE file; + bool res; + + if( ! openFile( path, FTP_FILE_READ )) { + return false; + } + res = getFileModTime( pdate, ptime ); + file.close(); + return res; +#endif +} + +// Assume SD library is SdFat (or family) and file is open + +#if STORAGE_TYPE != STORAGE_FATFS +bool FtpServer::getFileModTime( uint16_t * pdate, uint16_t * ptime ) +{ +#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT + #if defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) + return dir.fileTime(); + #else + return dir.getLastWrite(); + #endif +#elif STORAGE_TYPE == STORAGE_SDFAT1 + dir_t d; + + if( ! file.dirEntry( & d )) + return false; + * pdate = d.lastWriteDate; + * ptime = d.lastWriteTime; + return true; +#elif STORAGE_TYPE == STORAGE_SDFAT2 || STORAGE_TYPE == STORAGE_SPIFM + return file.getModifyDateTime( pdate, ptime ); +#endif + return false; +} +#endif + +#if STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + bool FtpServer::rename( const char * path, const char * newpath ){ + + FTP_FILE myFileIn = STORAGE_MANAGER.open(path, FILE_READ); + FTP_FILE myFileOut = STORAGE_MANAGER.open(newpath, FILE_WRITE); + + if(myFileOut) { + while (myFileIn.available() > 0) + { + int i = myFileIn.readBytes((char*)buf, FTP_BUF_SIZE); + myFileOut.write(buf, i); + } + // done, close the destination file + myFileOut.close(); + myFileOut = STORAGE_MANAGER.open(newpath, FILE_READ); + + } + bool operation = false; + + DEBUG_PRINT(F("RENAME --> ")); + DEBUG_PRINT(myFileIn.size()); + DEBUG_PRINT(" size "); + DEBUG_PRINTLN(myFileOut.size()); + + if (myFileIn.size() == myFileOut.size()) { + operation = true; + } + + + if (!operation) return operation; + + myFileIn.close(); + myFileOut.close(); + + return remove( path ); + }; +#endif diff --git a/lib/SimpleFTPServer/FtpServer.h b/lib/SimpleFTPServer/FtpServer.h new file mode 100644 index 00000000..9bc5aa59 --- /dev/null +++ b/lib/SimpleFTPServer/FtpServer.h @@ -0,0 +1,758 @@ +/* + * FtpServer Arduino, esp8266 and esp32 library for Ftp Server + * Derived form Jean-Michel Gallego version + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + + +/******************************************************************************* + ** ** + ** DEFINITIONS FOR FTP SERVER ** + ** ** + *******************************************************************************/ + +#include + +#ifndef FTP_SERVER_H +#define FTP_SERVER_H + +#define FTP_SERVER_VERSION "2.1.6 (2023-02-02)" + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +// +//#if(NETWORK_ESP8266_SD == DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266) +// #define ESP8266_GT_2_4_2_SD_STORAGE_SELECTED +// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 NETWORK_ESP8266 +//#endif + +#if !defined(FTP_SERVER_NETWORK_TYPE) +// select Network type based + #if defined(ESP8266) || defined(ESP31B) + #if(NETWORK_ESP8266_242 == DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266) + #define ARDUINO_ESP8266_RELEASE_2_4_2 + + #define FTP_SERVER_NETWORK_TYPE_SELECTED NETWORK_ESP8266_242 + + #define FTP_SERVER_NETWORK_TYPE NETWORK_ESP8266 + #else + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 + #endif + + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ESP8266 + #elif defined(ESP32) + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ESP32 + #elif defined(ARDUINO_ARCH_STM32) + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_STM32 + #elif defined(ARDUINO_ARCH_RP2040) + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_RP2040 + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_RP2040 + #elif defined(ARDUINO_ARCH_SAMD) + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_SAMD + #else + #define FTP_SERVER_NETWORK_TYPE DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO + #define STORAGE_TYPE DEFAULT_STORAGE_TYPE_ARDUINO + // #define STORAGE_SD_ENABLED + #endif +#endif + +#ifndef FTP_SERVER_NETWORK_TYPE_SELECTED + #define FTP_SERVER_NETWORK_TYPE_SELECTED FTP_SERVER_NETWORK_TYPE +#endif + + +#if defined(ESP8266) || defined(ESP31B) + #ifndef STORAGE_SD_FORCE_DISABLE + #define STORAGE_SD_ENABLED + #endif + #ifndef STORAGE_SPIFFS_FORCE_DISABLE + #define STORAGE_SPIFFS_ENABLED + #endif +#elif defined(ESP32) + #ifndef STORAGE_SD_FORCE_DISABLE + #define STORAGE_SD_ENABLED + #endif + #ifndef STORAGE_SPIFFS_FORCE_DISABLE + #define STORAGE_SPIFFS_ENABLED + #endif +#else + #ifndef STORAGE_SD_FORCE_DISABLE + #define STORAGE_SD_ENABLED + #endif +#endif + + +// Includes and defined based on Network Type +#if(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + + // Note: + // No SSL/WSS support for client in Async mode + // TLS lib need a sync interface! + + #if defined(ESP8266) + #include + //#include + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + + #elif defined(ESP32) + #include + //#include + + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #elif defined(ESP31B) + #include + + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #else + #error "network type ESP8266 ASYNC only possible on the ESP mcu!" + #endif + +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266 || FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242) + + #if !defined(ESP8266) && !defined(ESP31B) + #error "network type ESP8266 only possible on the ESP mcu!" + #endif + + #ifdef ESP8266 + #include + #else + #include + #endif + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #define NET_CLASS WiFi +// #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) +// #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ETHERNET_GENERIC) + + #include + #include + #define FTP_CLIENT_NETWORK_CLASS EthernetClient + #define FTP_SERVER_NETWORK_SERVER_CLASS EthernetServer + #define NET_CLASS Ethernet + +// #if defined(ESP8266) || defined(ESP32) +// #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) +// #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +// #else +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +// #endif +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_W5100 || FTP_SERVER_NETWORK_TYPE == NETWORK_ETHERNET_ENC) + + #include + #include + #define FTP_CLIENT_NETWORK_CLASS EthernetClient + #define FTP_SERVER_NETWORK_SERVER_CLASS EthernetServer + #define NET_CLASS Ethernet + +// #if defined(ESP8266) || defined(ESP32) +// #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) +// #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +// #else +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +// #endif +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ENC28J60 || FTP_SERVER_NETWORK_TYPE == NETWORK_UIPETHERNET) + + #include + + #define FTP_CLIENT_NETWORK_CLASS UIPClient + #define FTP_SERVER_NETWORK_SERVER_CLASS UIPServer + #define NET_CLASS Ethernet +// #if define(ESP8266) || define(ESP32) +// #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) +// #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +// #else +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +// #endif +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ETHERNET_LARGE) + + #include + #include + #define FTP_CLIENT_NETWORK_CLASS EthernetClient + #define FTP_SERVER_NETWORK_SERVER_CLASS EthernetServer + #define NET_CLASS Ethernet + +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ETHERNET_STM) + + #include + #include + #define FTP_CLIENT_NETWORK_CLASS EthernetClient + #define FTP_SERVER_NETWORK_SERVER_CLASS EthernetServer + #define NET_CLASS Ethernet + +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP32) + + #include + //#include + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiClientSecure + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #define NET_CLASS WiFi +// #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) +// #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_ESP32_ETH) + + #include + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #define NET_CLASS Ethernet +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_WiFiNINA) + + #include + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiSSLClient + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #define NET_CLASS WiFi +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +#elif(FTP_SERVER_NETWORK_TYPE == NETWORK_SEEED_RTL8720DN) + + #include + #define FTP_CLIENT_NETWORK_CLASS WiFiClient + //#define FTP_CLIENT_NETWORK_SSL_CLASS WiFiSSLClient + #define FTP_SERVER_NETWORK_SERVER_CLASS WiFiServer + #define NET_CLASS WiFi +// #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) +// #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +#else + #error "no network type selected!" +#endif + +#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040) + #define CommandIs( a ) (command != NULL && ! strcmp_P( command, PSTR( a ))) + #define ParameterIs( a ) ( parameter != NULL && ! strcmp_P( parameter, PSTR( a ))) +#else + #define CommandIs( a ) ( ! strcmp_PF( command, PSTR( a ))) + #define ParameterIs( a ) ( ! strcmp_PF( parameter, PSTR( a ))) +#endif + +#if(STORAGE_TYPE == STORAGE_SPIFFS) + #if defined(ESP32) +// #define FS_NO_GLOBALS + #include + + #define FTP_FILE File + #define FTP_DIR File + #else + #ifdef ARDUINO_ESP8266_RELEASE_2_4_2 + #define FS_NO_GLOBALS + #include "FS.h" + #define FTP_FILE fs::File + #define FTP_DIR fs::Dir + #else + #include "FS.h" + #define FTP_FILE File + #define FTP_DIR Dir + #endif + + #endif + +#if ESP8266 + #define FTP_FILE_READ "r" + #define FTP_FILE_READ_ONLY "r" + #define FTP_FILE_READ_WRITE "w+" + #define FTP_FILE_WRITE_APPEND "a+" + #define FTP_FILE_WRITE_CREATE "w+" +#else + #define FTP_FILE_READ "r" + #define FTP_FILE_READ_ONLY "r" + #define FTP_FILE_READ_WRITE "w" + #define FTP_FILE_WRITE_APPEND "a" + #define FTP_FILE_WRITE_CREATE "w" +#endif + + #define STORAGE_MANAGER SPIFFS + + #define FILENAME_LENGTH 32 +#elif(STORAGE_TYPE == STORAGE_FFAT) + #include "FS.h" + #include "FFat.h" + + #define STORAGE_MANAGER FFat + + #define FTP_FILE File + #define FTP_DIR File + + #define FTP_FILE_READ "r" + #define FTP_FILE_READ_ONLY "r" + #define FTP_FILE_READ_WRITE "w" + #define FTP_FILE_WRITE_APPEND "a" + #define FTP_FILE_WRITE_CREATE "w" + + #define FILENAME_LENGTH 255 +#elif(STORAGE_TYPE == STORAGE_LITTLEFS) + #if ESP8266 || ARDUINO_ARCH_RP2040 + #include "LittleFS.h" + #define STORAGE_MANAGER LittleFS + #define FTP_FILE File + #define FTP_DIR Dir + + #define FTP_FILE_READ "r" + #define FTP_FILE_READ_ONLY "r" + #define FTP_FILE_READ_WRITE "w+" + #define FTP_FILE_WRITE_APPEND "a+" + #define FTP_FILE_WRITE_CREATE "w+" + #else +#ifdef ESP32 + #if ESP_ARDUINO_VERSION_MAJOR >= 2 + #include "FS.h" + #include "LittleFS.h" + #define STORAGE_MANAGER LittleFS + #else + #include "LITTLEFS.h" + #define STORAGE_MANAGER LITTLEFS + #endif +#else + #include "LittleFS.h" + #define STORAGE_MANAGER LittleFS +#endif + #define FTP_FILE File + #define FTP_DIR File + + #define FTP_FILE_READ "r" + #define FTP_FILE_READ_ONLY "r" + #define FTP_FILE_READ_WRITE "w" + #define FTP_FILE_WRITE_APPEND "a" + #define FTP_FILE_WRITE_CREATE "w" + #endif + #define FILENAME_LENGTH 32 +#elif(STORAGE_TYPE == STORAGE_SD) + #include + #include + + #define STORAGE_MANAGER SD + #define FTP_FILE File + #define FTP_DIR File + + #define FTP_FILE_READ FILE_READ + #define FTP_FILE_READ_ONLY FILE_READ +#ifdef ESP32 + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_APPEND +#else + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_WRITE +#endif + #define FTP_FILE_WRITE_CREATE FILE_WRITE + + #define FILENAME_LENGTH 255 +#elif(STORAGE_TYPE == STORAGE_SD_MMC) + #include + #include + + #define STORAGE_MANAGER SD_MMC + #define FTP_FILE File + #define FTP_DIR File + + #define FTP_FILE_READ FILE_READ + #define FTP_FILE_READ_ONLY FILE_READ + #define FTP_FILE_READ_WRITE FILE_WRITE +#ifdef ESP32 + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_APPEND +#else + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_WRITE +#endif + #define FTP_FILE_WRITE_CREATE FILE_WRITE + + #define FILENAME_LENGTH 255 +#elif(STORAGE_TYPE == STORAGE_SEEED_SD) + #include + #define STORAGE_MANAGER SD + + #include "SD/Seeed_SD.h" + + + +// #define STORAGE_MANAGER SPIFLASH +// #include "SFUD/Seeed_SFUD.h" + + #define FTP_FILE File + #define FTP_DIR File + + #define FTP_FILE_READ FILE_READ + #define FTP_FILE_READ_ONLY FILE_READ + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_APPEND + #define FTP_FILE_WRITE_CREATE FILE_WRITE + + #define FILENAME_LENGTH 255 + +#elif (STORAGE_TYPE == STORAGE_SDFAT1) + #include + #include + + #define STORAGE_MANAGER sd + #define FTP_FILE SdFile + #define FTP_DIR SdFile + extern SdFat STORAGE_MANAGER; + + #define FTP_FILE_READ O_READ + #define FTP_FILE_READ_ONLY O_RDONLY + #define FTP_FILE_READ_WRITE O_RDWR + #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND + #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT + #define FILENAME_LENGTH 255 + +#elif (STORAGE_TYPE == STORAGE_SDFAT2) + #include + #include + + #define STORAGE_MANAGER sd + #define FTP_FILE FsFile + #define FTP_DIR FsFile + extern SdFat STORAGE_MANAGER; + + #define FTP_FILE_READ O_READ + #define FTP_FILE_READ_ONLY O_RDONLY + #define FTP_FILE_READ_WRITE O_RDWR + #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND + #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT + #define FILENAME_LENGTH 255 +#elif (STORAGE_TYPE == STORAGE_SPIFM) + #include + #include + #include + + #define STORAGE_MANAGER fatfs + #define FTP_FILE File + #define FTP_DIR File + extern FatFileSystem STORAGE_MANAGER; + extern Adafruit_SPIFlash flash; + #define FTP_FILE_READ FILE_READ + #define FTP_FILE_READ_ONLY FILE_READ + #define FTP_FILE_READ_WRITE FILE_WRITE + #define FTP_FILE_WRITE_APPEND FILE_WRITE + #define FTP_FILE_WRITE_CREATE FILE_WRITE + #define FILENAME_LENGTH 255 +#elif (STORAGE_TYPE == STORAGE_FATFS) + #include + #include + + #define STORAGE_MANAGER sdff + #define FTP_FILE FileFs + #define FTP_DIR DirFs + extern FatFsClass STORAGE_MANAGER; + #define O_READ FA_READ + #define O_WRITE FA_WRITE + #define O_RDWR FA_READ | FA_WRITE + #define O_CREAT FA_CREATE_ALWAYS + #define O_APPEND FA_OPEN_APPEND + + #define FTP_FILE_READ O_READ + #define FTP_FILE_READ_ONLY O_RDONLY + #define FTP_FILE_READ_WRITE O_RDWR + #define FTP_FILE_WRITE_APPEND O_WRITE | O_APPEND + #define FTP_FILE_WRITE_CREATE O_WRITE | O_CREAT + #define FILENAME_LENGTH 255 +#endif + +//#ifdef FTP_CLIENT_NETWORK_SSL_CLASS +//#define FTP_CLIENT_NETWORK_CLASS FTP_CLIENT_NETWORK_SSL_CLASS +//#endif + +#define OPEN_CLOSE_SPIFFS +#define OPEN_CLOSE_SD + +// Setup debug printing macros. +#ifdef FTP_SERVER_DEBUG + #define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } + #define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } +#else + #define DEBUG_PRINT(...) {} + #define DEBUG_PRINTLN(...) {} +#endif + +#define FTP_CMD_PORT 21 // Command port on wich server is listening +#define FTP_DATA_PORT_DFLT 20 // Default data port in active mode +#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode + +#define FF_MAX_LFN 255 // max size of a long file name +#define FTP_CMD_SIZE FF_MAX_LFN+8 // max size of a command +#define FTP_CWD_SIZE FF_MAX_LFN+8 // max size of a directory name +#define FTP_FIL_SIZE FF_MAX_LFN // max size of a file name +#define FTP_CRED_SIZE 16 // max size of username and password +#define FTP_NULLIP() IPAddress(0,0,0,0) + +enum ftpCmd { FTP_Stop = 0, // In this stage, stop any connection + FTP_Init, // initialize some variables + FTP_Client, // wait for client connection + FTP_User, // wait for user name + FTP_Pass, // wait for user password + FTP_Cmd }; // answers to commands + +enum ftpTransfer { FTP_Close = 0, // In this stage, close data channel + FTP_Retrieve, // retrieve file + FTP_Store, // store file + FTP_List, // list of files + FTP_Nlst, // list of name of files + FTP_Mlsd }; // listing for machine processing + +enum ftpDataConn { FTP_NoConn = 0,// No data connexion + FTP_Pasive, // Pasive type + FTP_Active }; // Active type + +enum FtpOperation { + FTP_CONNECT, + FTP_DISCONNECT, + FTP_FREE_SPACE_CHANGE +}; + +enum FtpTransferOperation { + FTP_UPLOAD_START = 0, + FTP_UPLOAD = 1, + + FTP_DOWNLOAD_START = 2, + FTP_DOWNLOAD = 3, + + + FTP_TRANSFER_STOP = 4, + FTP_DOWNLOAD_STOP = 4, + FTP_UPLOAD_STOP = 4, + + FTP_TRANSFER_ERROR = 5, + FTP_DOWNLOAD_ERROR = 5, + FTP_UPLOAD_ERROR = 5 +}; + +class FtpServer +{ +public: + FtpServer( uint16_t _cmdPort = FTP_CMD_PORT, uint16_t _pasvPort = FTP_DATA_PORT_PASV ); + + void begin( const char * _user, const char * _pass, const char * welcomeMessage = "Welcome to Simply FTP server" ); + void begin( const char * welcomeMessage = "Welcome to Simply FTP server" ); + + void end(); + void setLocalIp(IPAddress localIp); + void credentials( const char * _user, const char * _pass ); + uint8_t handleFTP(); + + void setCallback(void (*_callbackParam)(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace) ) + { + _callback = _callbackParam; + } + + void setTransferCallback(void (*_transferCallbackParam)(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize) ) + { + _transferCallback = _transferCallbackParam; + } + +private: + void (*_callback)(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){}; + void (*_transferCallback)(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){}; + + void iniVariables(); + void clientConnected(); + void disconnectClient(); + bool processCommand(); + bool haveParameter(); + int dataConnect( bool out150 = true ); + bool dataConnected(); + bool doRetrieve(); + bool doStore(); + bool doList(); + bool doMlsd(); + void closeTransfer(); + void abortTransfer(); + bool makePath( char * fullName, char * param = NULL ); + bool makeExistsPath( char * path, char * param = NULL ); + bool openDir( FTP_DIR * pdir ); + bool isDir( char * path ); + uint8_t getDateTime( char * dt, uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, + uint8_t * phour, uint8_t * pminute, uint8_t * second ); + char * makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ); + bool timeStamp( char * path, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second ); + bool getFileModTime( char * path, uint16_t * pdate, uint16_t * ptime ); +#if STORAGE_TYPE != STORAGE_FATFS + bool getFileModTime( uint16_t * pdate, uint16_t * ptime ); +#endif + int8_t readChar(); + + const char* getFileName(FTP_FILE *file){ + #if STORAGE_TYPE <= STORAGE_SDFAT2 + int max_characters = 100; + char f_name[max_characters]; + file->getName(f_name, max_characters); + String filename = String(f_name); + return filename.c_str(); + #elif STORAGE_TYPE == STORAGE_FATFS + return file->fileName(); + #else + return file->name(); + #endif + } + bool exists( const char * path ) { +#if STORAGE_TYPE == STORAGE_SPIFFS || (STORAGE_TYPE == STORAGE_SD && FTP_SERVER_NETWORK_TYPE == NETWORK_ESP8266_242) + if (strcmp(path, "/") == 0) return true; +#endif +#if STORAGE_TYPE == STORAGE_FFAT || (STORAGE_TYPE == STORAGE_LITTLEFS && defined(ESP32)) + FTP_DIR f = STORAGE_MANAGER.open(path, "r"); + return (f == true); +#else + return STORAGE_MANAGER.exists( path ); +#endif + }; + bool remove( const char * path ) { return STORAGE_MANAGER.remove( path ); }; +#if STORAGE_TYPE == STORAGE_SPIFFS + bool makeDir( const char * path ) { return false; }; + bool removeDir( const char * path ) { return false; }; +#else + bool makeDir( const char * path ) { return STORAGE_MANAGER.mkdir( path ); }; + bool removeDir( const char * path ) { return STORAGE_MANAGER.rmdir( path ); }; +#endif + +#if STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + bool rename( const char * path, const char * newpath ); +#else + bool rename( const char * path, const char * newpath ) { return STORAGE_MANAGER.rename( path, newpath ); }; +#endif +#if (STORAGE_TYPE == STORAGE_SEEED_SD) + bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ); +#elif (STORAGE_TYPE == STORAGE_SD && defined(ESP8266))// FTP_SERVER_NETWORK_TYPE_SELECTED == NETWORK_ESP8266_242) + bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ); +#elif (STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS || STORAGE_TYPE == STORAGE_FFAT ) + bool openFile( const char * path, const char * readType ); +// bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ); +#elif STORAGE_TYPE <= STORAGE_SDFAT2 || STORAGE_TYPE == STORAGE_SPIFM || (STORAGE_TYPE == STORAGE_SD && ARDUINO_ARCH_SAMD) + bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ); +#else + bool openFile( char path[ FTP_CWD_SIZE ], const char * readType ); + bool openFile( const char * path, const char * readType ); +// bool openFile( char path[ FTP_CWD_SIZE ], int readTypeInt ); +#endif +// bool openFile( char path[ FTP_CWD_SIZE ], const char * readType ); +// bool openFile( const char * path, const char * readType ); + uint32_t fileSize( FTP_FILE file ); + +#if STORAGE_TYPE == STORAGE_SPIFFS || STORAGE_TYPE == STORAGE_LITTLEFS +#if ESP8266 || ARDUINO_ARCH_RP2040 + uint32_t capacity() { + FSInfo fi; + STORAGE_MANAGER.info(fi); + + return fi.totalBytes >> 1; + }; + uint32_t free() { + FSInfo fi; + STORAGE_MANAGER.info(fi); + + return (fi.totalBytes - fi.usedBytes) >> 1; + }; +#else + uint32_t capacity() { + return STORAGE_MANAGER.totalBytes() >> 1; + }; + uint32_t free() { + return (STORAGE_MANAGER.totalBytes() - + STORAGE_MANAGER.usedBytes()) >> 1; + }; +#endif +#elif STORAGE_TYPE == STORAGE_SD || STORAGE_TYPE == STORAGE_SD_MMC + uint32_t capacity() { return true; }; + uint32_t free() { return true; }; +#elif STORAGE_TYPE == STORAGE_SEEED_SD + uint32_t capacity() { + return STORAGE_MANAGER.totalBytes() >> 1; + }; + uint32_t free() { + return (STORAGE_MANAGER.totalBytes() - + STORAGE_MANAGER.usedBytes()) >> 1; + }; +#elif STORAGE_TYPE == STORAGE_SDFAT1 + uint32_t capacity() { return STORAGE_MANAGER.card()->cardSize() >> 1; }; + uint32_t free() { return STORAGE_MANAGER.vol()->freeClusterCount() * + STORAGE_MANAGER.vol()->sectorsPerCluster() >> 1; }; +#elif STORAGE_TYPE == STORAGE_SDFAT2 + uint32_t capacity() { return STORAGE_MANAGER.card()->sectorCount() >> 1; }; + uint32_t free() { return STORAGE_MANAGER.vol()->freeClusterCount() * + STORAGE_MANAGER.vol()->sectorsPerCluster() >> 1; }; +#elif STORAGE_TYPE == STORAGE_SPIFM + uint32_t capacity() { return flash.size() >> 10; }; + uint32_t free() { return 0; }; // TODO // +#elif STORAGE_TYPE == STORAGE_FATFS + uint32_t capacity() { return STORAGE_MANAGER.capacity(); }; + uint32_t free() { return STORAGE_MANAGER.free(); }; +#elif STORAGE_TYPE == STORAGE_FFAT + uint32_t capacity() { return STORAGE_MANAGER.totalBytes(); }; + uint32_t free() { return STORAGE_MANAGER.freeBytes(); }; +#endif + bool legalChar( char c ) // Return true if char c is allowed in a long file name + { + if( c == '"' || c == '*' || c == '?' || c == ':' || + c == '<' || c == '>' || c == '|' ) + return false; +#if STORAGE_TYPE == STORAGE_FATFS + return 0x1f < c && c < 0xff; +#else + return 0x1f < c && c < 0x7f; +#endif + } + + IPAddress localIp; // IP address of server as seen by clients + IPAddress dataIp; // IP address of client for data + FTP_SERVER_NETWORK_SERVER_CLASS ftpServer; + FTP_SERVER_NETWORK_SERVER_CLASS dataServer; + + + FTP_CLIENT_NETWORK_CLASS client; + FTP_CLIENT_NETWORK_CLASS data; + + FTP_FILE file; + FTP_DIR dir; + + ftpCmd cmdStage; // stage of ftp command connexion + ftpTransfer transferStage; // stage of data connexion + ftpDataConn dataConn; // type of data connexion + + bool anonymousConnection = false; + + uint8_t __attribute__((aligned(4))) // need to be aligned to 32bit for Esp8266 SPIClass::transferBytes() + buf[ FTP_BUF_SIZE ]; // data buffer for transfers + char cmdLine[ FTP_CMD_SIZE ]; // where to store incoming char from client + char cwdName[ FTP_CWD_SIZE ]; // name of current directory + char rnfrName[ FTP_CWD_SIZE ]; // name of file for RNFR command + const char * user; // user name + const char * pass; // password + char command[ 5 ]; // command sent by client + bool rnfrCmd; // previous command was RNFR + char * parameter; // point to begin of parameters sent by client + const char * welcomeMessage; + uint16_t cmdPort, + pasvPort, + dataPort; + uint16_t iCL; // pointer to cmdLine next incoming char + uint16_t nbMatch; + + uint32_t millisDelay, // + millisEndConnection, // + millisBeginTrans, // store time of beginning of a transaction + bytesTransfered; // +}; + +#endif // FTP_SERVER_H diff --git a/lib/SimpleFTPServer/FtpServerKey.h b/lib/SimpleFTPServer/FtpServerKey.h new file mode 100644 index 00000000..6a0cbd1c --- /dev/null +++ b/lib/SimpleFTPServer/FtpServerKey.h @@ -0,0 +1,128 @@ +/* + * FtpServer Arduino, esp8266 and esp32 library for Ftp Server + * Derived form Jean-Michel Gallego version + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +/******************************************************************************* + ** ** + ** SETTINGS FOR FTP SERVER ** + ** ** + *******************************************************************************/ + +#ifndef FTP_SERVER_CONFIG_H +#define FTP_SERVER_CONFIG_H + +// Uncomment to enable printing out nice debug messages. +// #define FTP_SERVER_DEBUG +// #define FTP_ADDITIONAL_DEBUG + +// Define where debug output will be printed. +#define DEBUG_PRINTER Serial + +#define STORAGE_SDFAT1 1 // Library SdFat version 1.4.x +#define STORAGE_SDFAT2 2 // Library SdFat version >= 2.0.2 +#define STORAGE_SPIFM 3 // Libraries Adafruit_SPIFlash and SdFat-Adafruit-Fork +#define STORAGE_FATFS 4 // Library FatFs +#define STORAGE_SD 5 // Standard SD library (suitable for Arduino esp8266 and esp32 +#define STORAGE_SPIFFS 6 // SPIFFS +#define STORAGE_LITTLEFS 7 // LITTLEFS +#define STORAGE_SEEED_SD 8 // Seeed_SD library +#define STORAGE_FFAT 9 // ESP32 FFAT +#define STORAGE_SD_MMC 10 // SD_MMC library + +#define NETWORK_ESP8266_ASYNC (1) +#define NETWORK_ESP8266 (2) // Standard ESP8266WiFi +#define NETWORK_ESP8266_242 (3) // ESP8266WiFi before 2.4.2 core +#define NETWORK_W5100 (4) // Standard Arduino Ethernet library +#define NETWORK_ETHERNET (4) // Standard Arduino Ethernet library +#define NETWORK_ENC28J60 (5) // UIPEthernet library +#define NETWORK_ESP32 (6) // Standard WiFi library +#define NETWORK_RP2040_WIFI (6) // Raspberry Pi Pico W standard WiFi library +#define NETWORK_ESP32_ETH (7) // Standard ETH library +#define NETWORK_WiFiNINA (8) // Standard WiFiNINA library +#define NETWORK_SEEED_RTL8720DN (9) // Standard SEED WiFi library +#define NETWORK_ETHERNET_LARGE (10) +#define NETWORK_ETHERNET_ENC (11) // EthernetENC library (evolution of UIPEthernet +#define NETWORK_ETHERNET_STM (12) +#define NETWORK_UIPETHERNET (13) // UIPEthernet library same of NETWORK_ENC28J60 +#define NETWORK_ETHERNET_GENERIC (14) // Ethernet generic + +// esp8266 configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP8266 NETWORK_ESP8266 + #define DEFAULT_STORAGE_TYPE_ESP8266 STORAGE_LITTLEFS +#endif +// esp32 configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 NETWORK_ESP32 + #define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_LITTLEFS + /** +To use Ethernet.h with esp32 fix would be to change in Ethernet.h the line +class EthernetServer : public Server { +to +class EthernetServer : public Stream { + +or + +in \esp32\2.0.6\cores\esp32\Server.h +A workaround is to change line 28 of the ESP32 core's Server.h from: + virtual void begin(uint16_t port=0) =0; +to + virtual void begin() =0; +However, the last one, that will break anything that uses the ESP32 WiFi library's WebServer class. + +https://github.com/arduino-libraries/Ethernet/issues/193 +https://github.com/arduino-libraries/Ethernet/issues/88 + * + */ +#endif +// Standard AVR Arduino configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO NETWORK_W5100 + #define DEFAULT_STORAGE_TYPE_ARDUINO STORAGE_SD +#endif +// STM32 configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 NETWORK_W5100 + #define DEFAULT_STORAGE_TYPE_STM32 STORAGE_SDFAT2 +#endif +// Raspberry Pi Pico (rp2040) configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_RP2040 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_RP2040 NETWORK_RP2040_WIFI + #define DEFAULT_STORAGE_TYPE_RP2040 STORAGE_LITTLEFS +#endif + +// Arduino SAMD21 like Arduino MKR Nano 33 IoT or Wio Terminal +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO_SAMD +// Wio Terminal +// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN +// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SEEED_SD + +// Arduino SAMD + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_WiFiNINA + #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SD +#endif + +#define UTF8_SUPPORT + +//#define SD_CS_PIN 4 +// Disconnect client after 5 minutes of inactivity (expressed in seconds) +#define FTP_TIME_OUT 5 * 60 + + +// Wait for authentication for 10 seconds (expressed in seconds) +#define FTP_AUTH_TIME_OUT 10 + + +// Size of file buffer for read/write +// Transfer speed depends of this value +// Best value depends on many factors: SD card, client side OS, ... +// But it can be reduced to 512 if memory usage is critical. +#define FTP_BUF_SIZE 1024 //2048 //1024 // 512 + +#endif // FTP_SERVER_CONFIG_H diff --git a/lib/SimpleFTPServer/LICENSE.md b/lib/SimpleFTPServer/LICENSE.md new file mode 100644 index 00000000..54153141 --- /dev/null +++ b/lib/SimpleFTPServer/LICENSE.md @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved. + +You may copy, alter and reuse this code in any way you like, but please leave +reference to www.mischianti.org in your comments if you redistribute this code. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/SimpleFTPServer/README.md b/lib/SimpleFTPServer/README.md new file mode 100644 index 00000000..17d083ec --- /dev/null +++ b/lib/SimpleFTPServer/README.md @@ -0,0 +1,108 @@ +# SimpleFTPServer + +[Instruction on FTP server on esp8266 and esp32](https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32) +[Simple FTP Server library now with support for Wio Terminal and SD](https://www.mischianti.org/2021/07/01/simple-ftp-server-library-now-with-support-for-wio-terminal-and-sd/) + +#### Simple FTP Server for + - Raspberry Pi Pico W (Flash: LittleFS) (To test SD and SdFat) + - esp8266 (Flash: SPIFFs, LittleFS. SD: SD, SdFat 2) + - esp32 (SPIFFS, LITTLEFS, FFAT, SD: SD, SdFat) + - stm32 (SdFat, SPI flash) + - Arduino (SD with 8.3 file format, SD: SD, SdFat 2) + - Wio Terminal (SdFat 2, Seed SD, and native FAT) + +#### Changelog +- 2022-02-02 2.1.6 Fix esp8266 Ethernet (w5x00) issue and explain solution for ESP32 Ethernet (w5x00), add new Networks management +- 2022-01-13 2.1.5 Fix SPIFM external SPI Flash date management (add SPIFM esp32 example) +- 2022-09-21 2.1.4 Add support for Raspberry Pi Pico W and rp2040 boards, Fix SD card config +- 2022-09-20 2.1.3 Soft AP IP management, more disconnect event and SD_MCC +- 2022-05-21 2.1.2 Fix SD path (#19) +- 2022-05-21 2.1.1 Minor fix +- 2022-03-30 2.1.0 Add UTF8 support and enabled It by default (Thanks to @plaber) +- 2022-03-30 2.0.0 Complete support for STM32 with SD and SPI Flash minor bux fix and HELP command support +- 2022-03-17 1.3.0 Fix enc28j60 and w5500 support and restructuring for local settings +- 2022-02-25 1.2.1 Fix anonymous user begin and fix SPIFFS wrong display +- 2022-02-22 1.2.0 Add anonymous user and implement correct RFC (#9 now work correctly with File Explorer) +- 2022-02-01 1.1.1 Add workaround to start FTP server before connection, add end and setLocalIP method. + + +

When I develop a new solution I'd like to divide the application in layer, and so I'd like focus my attention in only one aspect at time.

+ + + +

In detail I separate the REST layer (written inside the microcontroller) and the Front-End (written in Angular, React/Redux or vanilla JS), so I'd like to upload new web interface directly to the microcontroller via FTP.

+ + + +
+ + + +

For static information (Web pages for examples), that not change frequently, esp8266 or esp32 have internal SPIFFS (SPI Flash File System) and you can upload data via Arduino IDE as explained in the article "WeMos D1 mini (esp8266), integrated SPIFFS Filesystem" for esp8266 or "ESP32: integrated SPIFFS FileSystem" for esp32 or with LittleFS "WeMos D1 mini (esp8266), integrated LittleFS Filesystem" but for fast operation and future support It's usefully use FTP.

+ + + + +```cpp +/* + * FtpServer esp8266 and esp32 with SPIFFS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#ifdef ESP8266 +#include +#elif defined ESP32 +#include +#include "SPIFFS.h" +#endif + +#include + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASS"; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// +#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount + if (SPIFFS.begin(true)) { +#elif defined ESP8266 + if (SPIFFS.begin()) { +#endif + Serial.println("SPIFFS opened!"); + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. set ports in ESP8266FtpServer.h (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + // server.handleClient(); //example if running a webserver you still need to call .handleClient(); + +} +``` + +https://downloads.arduino.cc/libraries/logs/github.com/xreef/SimpleFTPServer/ \ No newline at end of file diff --git a/lib/SimpleFTPServer/SimpleFTPServer.h b/lib/SimpleFTPServer/SimpleFTPServer.h new file mode 100644 index 00000000..dc92d46b --- /dev/null +++ b/lib/SimpleFTPServer/SimpleFTPServer.h @@ -0,0 +1,18 @@ +/* + * FtpServer Arduino, esp8266 and esp32 library for Ftp Server + * Derived form https://github.com/nailbuster/esp8266FTPServer + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#ifndef SIMPLE_FTP_SERVER_H +#define SIMPLE_FTP_SERVER_H + +#include + +#endif + +#pragma once diff --git a/lib/SimpleFTPServer/examples/Arduino_Ethernet/Arduino_Ethernet.ino b/lib/SimpleFTPServer/examples/Arduino_Ethernet/Arduino_Ethernet.ino new file mode 100644 index 00000000..af178521 --- /dev/null +++ b/lib/SimpleFTPServer/examples/Arduino_Ethernet/Arduino_Ethernet.ino @@ -0,0 +1,62 @@ +/* + * FtpServer Arduino with Ethernet library and w5100 shield + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include +#include +#include "SD.h" + +#include + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xE1 }; + +// Set the static IP address to use if the DHCP fails to assign +byte macAddr[] = {0x5e, 0xa4, 0x18, 0xf0, 0x8a, 0xf2}; +IPAddress arduinoIP(192, 168, 1, 177); +IPAddress dnsIP(192, 168, 1, 1); +IPAddress gatewayIP(192, 168, 1, 1); +IPAddress subnetIP(255, 255, 255, 0); + +FtpServer ftpSrv; + +void setup(void){ + Serial.begin(115200); + delay(2000); + // If other chips are connected to SPI bus, set to high the pin connected + // to their CS before initializing Flash memory + pinMode( 4, OUTPUT ); + digitalWrite( 4, HIGH ); + pinMode( 10, OUTPUT ); + digitalWrite( 10, HIGH ); + + Serial.print("Starting SD."); + while (!SD.begin(4)) { + Serial.print("."); + } + Serial.println("finish!"); + + // start the Ethernet connection: + Serial.print("Starting ethernet."); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + Ethernet.begin(macAddr, arduinoIP, dnsIP, gatewayIP, subnetIP); + }else{ + Serial.println("ok to configure Ethernet using DHCP"); + } + + Serial.print("IP address "); + Serial.println(Ethernet.localIP()); + + Serial.println("SPIFFS opened!"); + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/Arduino_Ethernet_SdFat2/Arduino_Ethernet_SdFat2.ino b/lib/SimpleFTPServer/examples/Arduino_Ethernet_SdFat2/Arduino_Ethernet_SdFat2.ino new file mode 100644 index 00000000..3892b1f6 --- /dev/null +++ b/lib/SimpleFTPServer/examples/Arduino_Ethernet_SdFat2/Arduino_Ethernet_SdFat2.ino @@ -0,0 +1,133 @@ +/* + * FtpServer Arduino with Ethernet library and w5100 shield + * With SdFat version > 2 (full name and more size) + * + * #ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO + * #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ARDUINO NETWORK_W5100 + * #define DEFAULT_STORAGE_TYPE_ARDUINO STORAGE_SDFAT2 + * #endif + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include +#include +#include +#include + +// Define Chip Select for your SD card according to hardware +// #define CS_SDCARD 4 // SD card reader of Ehernet shield +#define CS_SDCARD 4 // Chip Select for SD card reader on Due + +// Define Reset pin for W5200 or W5500 +// set to -1 for other ethernet chip or if Arduino reset board is used + #define W5x00_RESET -1 +//#define W5x00_RESET 8 // on Due +// #define W5x00_RESET 3 // on MKR + + +// Object for File system +SdFat sd; + +// Object for FtpServer +// Command port and Data port in passive mode can be defined here +// FtpServer ftpSrv( 221, 25000 ); +// FtpServer ftpSrv( 421 ); // Default data port in passive mode is 55600 +FtpServer ftpSrv; // Default command port is 21 ( !! without parenthesis !! ) + +// Mac address of ethernet adapter +// byte mac[] = { 0x90, 0xa2, 0xda, 0x00, 0x00, 0x00 }; +byte mac[] = { 0x00, 0xaa, 0xbb, 0xcc, 0xde, 0xef }; + +// IP address of FTP server +// if set to 0, use DHCP for the routeur to assign IP +// IPAddress serverIp( 192, 168, 1, 40 ); +IPAddress serverIp( 0, 0, 0, 0 ); + +// External IP address of FTP server +// In passive mode, when accessing the serveur from outside his subnet, it can be +// necessary with some clients to reply them with the server's external ip address +// IPAddress externalIp( 192, 168, 1, 2 ); + +ArduinoOutStream cout( Serial ); + + +void setup() +{ + Serial.begin( 115200 ); + cout << F("=== Test of FTP Server with SdFat ") << SD_FAT_VERSION << F(" file system ===") << endl; + + // If other chips are connected to SPI bus, set to high the pin connected + // to their CS before initializing Flash memory + pinMode( 4, OUTPUT ); + digitalWrite( 4, HIGH ); + pinMode( 10, OUTPUT ); + digitalWrite( 10, HIGH ); + + // Mount the SD card memory + cout << F("Mount the SD card memory... "); + if( ! sd.begin( CS_SDCARD, SD_SCK_MHZ( 50 ))) + { + cout << F("Unable to mount SD card") << endl; + while( true ) ; + } + pinMode( CS_SDCARD, OUTPUT ); + digitalWrite( CS_SDCARD, HIGH ); + cout << F("ok") << endl; + + // Show capacity and free space of SD card + cout << F("Capacity of card: ") << long( sd.card()->sectorCount() >> 1 ) + << F(" kBytes") << endl; + cout << F("Free space on card: ") + << long( sd.vol()->freeClusterCount() * sd.vol()->sectorsPerCluster() >> 1 ) + << F(" kBytes") << endl; + + // Send reset to Ethernet module + if( W5x00_RESET > -1 ) + { + pinMode( W5x00_RESET, OUTPUT ); + digitalWrite( W5x00_RESET, LOW ); + delay( 200 ); + digitalWrite( W5x00_RESET, HIGH ); + delay( 200 ); + } + + // Initialize the network + cout << F("Initialize ethernet module ... "); + if( serverIp[0] != 0 ) + Ethernet.begin( mac, serverIp ); + else if( Ethernet.begin( mac ) == 0 ) + { + cout << F("failed!") << endl; + while( true ) ; + } + uint16_t wizModule[] = { 0, 5100, 5200, 5500 }; + cout << F("W") << wizModule[ Ethernet.hardwareStatus()] << F(" ok") << endl; + serverIp = Ethernet.localIP(); + cout << F("IP address of server: ") + << int( serverIp[0]) << "." << int( serverIp[1]) << "." + << int( serverIp[2]) << "." << int( serverIp[3]) << endl; + + // Initialize the FTP server + ftpSrv.begin("user","password"); + // ftpSrv.init( externalIp ); + // ftpSrv.init( IPAddress( 11, 22, 33, 44 )); + + // Default username and password are set to 'arduino' and 'test' + // but can then be changed by calling ftpSrv.credentials() + // ftpSrv.credentials( "myname", "123" ); + + cout << F("Free stack: ") << FreeStack() << endl; + + cout << "Viaaa!"; +} + +void loop() +{ + ftpSrv.handleFTP(); + + // more processes... +} diff --git a/lib/SimpleFTPServer/examples/Arduino_esp32_SD/Arduino_esp32_SD.ino b/lib/SimpleFTPServer/examples/Arduino_esp32_SD/Arduino_esp32_SD.ino new file mode 100644 index 00000000..8f497a2f --- /dev/null +++ b/lib/SimpleFTPServer/examples/Arduino_esp32_SD/Arduino_esp32_SD.ino @@ -0,0 +1,96 @@ +/* + * FtpServer esp8266 and esp32 with SD + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include +#include "SD.h" + +#include + +const char* ssid = ""; +const char* password = ""; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + Serial.print(">>>>>>>>>>>>>>> _callback " ); + Serial.print(ftpOperation); + /* FTP_CONNECT, + * FTP_DISCONNECT, + * FTP_FREE_SPACE_CHANGE + */ + Serial.print(" "); + Serial.print(freeSpace); + Serial.print(" "); + Serial.println(totalSpace); + + // freeSpace : totalSpace = x : 360 + + if (ftpOperation == FTP_CONNECT) Serial.println(F("CONNECTED")); + if (ftpOperation == FTP_DISCONNECT) Serial.println(F("DISCONNECTED")); +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + Serial.print(">>>>>>>>>>>>>>> _transferCallback " ); + Serial.print(ftpOperation); + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ + Serial.print(" "); + Serial.print(name); + Serial.print(" "); + Serial.println(transferredSize); +}; + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + SPI.begin(14, 12, 15, 13); //SCK, MISO, MOSI,SS + + if (SD.begin(13, SPI)) { + Serial.println("SD opened!"); + + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + // server.handleClient(); //example if running a webserver you still need to call .handleClient(); + +} diff --git a/lib/SimpleFTPServer/examples/ESP32_AP_FFAT_WiFi/ESP32_AP_FFAT_WiFi.ino b/lib/SimpleFTPServer/examples/ESP32_AP_FFAT_WiFi/ESP32_AP_FFAT_WiFi.ino new file mode 100644 index 00000000..4c7efa93 --- /dev/null +++ b/lib/SimpleFTPServer/examples/ESP32_AP_FFAT_WiFi/ESP32_AP_FFAT_WiFi.ino @@ -0,0 +1,124 @@ +/* + * FtpServer esp32 with FFat FS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include "Arduino.h" +#include "FS.h" +#include "FFat.h" + +#include + +#ifdef STA_MODE + const char* ssid = ""; + const char* password = ""; +#endif +const char* ssid_AP = "ESP32"; +const char* password_AP = "aabbccdd77"; + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup(void){ + Serial.begin(115200); + + //AP mode + WiFi.mode(WIFI_AP); + WiFi.softAP(ssid_AP, password_AP); + delay(1000); + //IPAddress IP = IPAddress (10, 10, 10, 1); + //IPAddress NMask = IPAddress (255, 255, 255, 0); + //WiFi.softAPConfig(IP, IP, NMask); + Serial.print("Set AP named:"); + Serial.println(ssid_AP); + IPAddress myIP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(myIP); + +#ifdef STA_MODE + // STA mode + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +#endif + + /////FTP Setup, ensure FFAT is started before ftp; ///////// + Serial.print(F("Inizializing FS...")); + if (FFat.begin(true)){ + Serial.println(F("done.")); + }else{ + Serial.println(F("fail.")); + while (true) { delay(1000); }; + } + + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("Start FTP with user: user and passwd: password!"); + ftpSrv.begin("user","password"); //username, password for ftp. (default 21, 50009 for PASV) + ftpSrv.setLocalIp(myIP); +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/ESP32_FFAT_WiFi/ESP32_FFAT_WiFi.ino b/lib/SimpleFTPServer/examples/ESP32_FFAT_WiFi/ESP32_FFAT_WiFi.ino new file mode 100644 index 00000000..df97b956 --- /dev/null +++ b/lib/SimpleFTPServer/examples/ESP32_FFAT_WiFi/ESP32_FFAT_WiFi.ino @@ -0,0 +1,105 @@ +/* + * FtpServer esp32 with FFat FS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include "Arduino.h" +#include "FS.h" +#include "FFat.h" + +#include + +const char* ssid = ""; +const char* password = ""; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + /////FTP Setup, ensure FFAT is started before ftp; ///////// + Serial.print(F("Inizializing FS...")); + if (FFat.begin(true)){ + Serial.println(F("done.")); + }else{ + Serial.println(F("fail.")); + while (true) { delay(1000); }; + } + + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("Start FTP with user: user and passwd: password!"); + ftpSrv.begin("user","password"); //username, password for ftp. (default 21, 50009 for PASV) + +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/ESP32_FFAT_enc28j60/ESP32_FFAT_enc28j60.ino b/lib/SimpleFTPServer/examples/ESP32_FFAT_enc28j60/ESP32_FFAT_enc28j60.ino new file mode 100644 index 00000000..78b6bb19 --- /dev/null +++ b/lib/SimpleFTPServer/examples/ESP32_FFAT_enc28j60/ESP32_FFAT_enc28j60.ino @@ -0,0 +1,130 @@ +/* + * FtpServer esp32 with FFat and EthernetENC (or UIPEthernet) + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include "Arduino.h" +#include +#include + +#include "FS.h" +#include "FFat.h" + +#include + +#define MACADDRESS 0x00,0x01,0x02,0x03,0x04,0x05 +#define MYIPADDR 192,168,1,28 +#define MYIPMASK 255,255,255,0 +#define MYDNS 192,168,1,1 +#define MYGW 192,168,1,1 + +uint8_t macaddress[6] = {MACADDRESS}; + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + Serial.print(">>>>>>>>>>>>>>> _callback " ); + Serial.print(ftpOperation); + /* FTP_CONNECT, + * FTP_DISCONNECT, + * FTP_FREE_SPACE_CHANGE + */ + Serial.print(" "); + Serial.print(freeSpace); + Serial.print(" "); + Serial.println(totalSpace); + + // freeSpace : totalSpace = x : 360 + + if (ftpOperation == FTP_CONNECT) Serial.println(F("CONNECTED")); + if (ftpOperation == FTP_DISCONNECT) Serial.println(F("DISCONNECTED")); +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + Serial.print(">>>>>>>>>>>>>>> _transferCallback " ); + Serial.print(ftpOperation); + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ + Serial.print(" "); + Serial.print(name); + Serial.print(" "); + Serial.println(transferredSize); +}; + + +void setup(void){ + Serial.begin(115200); + + Serial.println("Begin Ethernet"); + + Ethernet.init(5); + if (Ethernet.begin(macaddress)) { // Dynamic IP setup + Serial.println("DHCP OK!"); + }else{ + Serial.println("Failed to configure Ethernet using DHCP"); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + + IPAddress ip(MYIPADDR); + IPAddress dns(MYDNS); + IPAddress gw(MYGW); + IPAddress sn(MYIPMASK); + Ethernet.begin(macaddress, ip, dns, gw, sn); + Serial.println("STATIC OK!"); + } + delay(5000); + + + Serial.print("Local IP : "); + Serial.println(Ethernet.localIP()); + Serial.print("Subnet Mask : "); + Serial.println(Ethernet.subnetMask()); + Serial.print("Gateway IP : "); + Serial.println(Ethernet.gatewayIP()); + Serial.print("DNS Server : "); + Serial.println(Ethernet.dnsServerIP()); + + Serial.println("Ethernet Successfully Initialized"); + + /////FTP Setup, ensure FFat is started before ftp; ///////// + if (FFat.begin(true)) { + Serial.println("FFat opened!"); + + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + ftpSrv.begin("user","password"); //username, password for ftp. set ports in ESP8266FtpServer.h (default 21, 50009 for PASV) + Serial.println("FTP server started!"); + } else { + Serial.println("FFat opened FAIL!!!!!"); + } + +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + // server.handleClient(); //example if running a webserver you still need to call .handleClient(); + +} diff --git a/lib/SimpleFTPServer/examples/ESP32_SPIFM_WiFi/ESP32_SPIFM_WiFi.ino b/lib/SimpleFTPServer/examples/ESP32_SPIFM_WiFi/ESP32_SPIFM_WiFi.ino new file mode 100644 index 00000000..866d65c3 --- /dev/null +++ b/lib/SimpleFTPServer/examples/ESP32_SPIFM_WiFi/ESP32_SPIFM_WiFi.ino @@ -0,0 +1,148 @@ +/** + * ESP32 and external SPI Flash FTP server + * + * +// esp32 configuration +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32 NETWORK_ESP32 + #define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_SPIFM +#endif + * + */ + +#include +#include "SdFat.h" +#include "Adafruit_SPIFlash.h" +#include +#include + +Adafruit_FlashTransport_SPI flashTransport(SS, SPI); // Set CS and SPI interface +Adafruit_SPIFlash flash(&flashTransport); + +// file system object from SdFat +FatFileSystem fatfs; + +const char* ssid = ""; +const char* password = ""; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.print(F("FTP: Free space change, free ")); + Serial.print(freeSpace); + Serial.print(F(" of ")); + Serial.println(totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.print(F("FTP: Upload of file ")); + Serial.print(name); + Serial.print(F(" ")); + Serial.print(transferredSize); + Serial.println(F("bytes")); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup() +{ + // Initialize serial port and wait for it to open before continuing. + Serial.begin(115200); + while (!Serial) { + delay(100); + } + Serial.println("FTP with external SPIFlash"); + + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + Serial.print("SN address: "); + Serial.println(WiFi.subnetMask()); + + if (flash.begin()) { + Serial.println(F("Device finded and supported!")); + } else { + Serial.println(F("Problem to discover and configure device, check wiring also!")); + while(1) yield(); + } + // Set 4Mhz SPI speed + flashTransport.setClockSpeed(4000000, 4000000); // added to prevent speed problem + + Serial.print("JEDEC ID: "); Serial.println(flash.getJEDECID(), HEX); + Serial.print("Flash size: "); Serial.println(flash.size()); + Serial.flush(); + + // First call begin to mount the filesystem. Check that it returns true + // to make sure the filesystem was mounted. + if (!fatfs.begin(&flash)) { + Serial.println("Error, failed to mount newly formatted filesystem!"); + Serial.println("Was the flash chip formatted with the SdFat_format example?"); + while(1) yield(); + } + Serial.println("Mounted filesystem!"); + Serial.println(); + + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("Starting FTP Server!"); + ftpSrv.begin("user","password"); //username, password for ftp. (default 21, 50009 for PASV) +// ftpSrv.beginAnonymous(); + +} + +// The loop function is called in an endless loop +void loop() +{ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/RaspberryPiPicoW_LittleFS_WiFi/RaspberryPiPicoW_LittleFS_WiFi.ino b/lib/SimpleFTPServer/examples/RaspberryPiPicoW_LittleFS_WiFi/RaspberryPiPicoW_LittleFS_WiFi.ino new file mode 100644 index 00000000..4cef1e97 --- /dev/null +++ b/lib/SimpleFTPServer/examples/RaspberryPiPicoW_LittleFS_WiFi/RaspberryPiPicoW_LittleFS_WiFi.ino @@ -0,0 +1,103 @@ +/* + * FtpServer Raspberry Pi Pico W with LittleFS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#include +#include + +#include + +const char* ssid = ""; +const char* password = ""; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup(void){ + Serial.begin(115200); + + while (!Serial) {delay(100);}; + + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + if (LittleFS.begin()) { + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("LittleFS opened!"); + ftpSrv.begin("user","password"); //username, password for ftp. (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/STM32_SPIFlash_enc28j60/STM32_SPIFlash_enc28j60.ino b/lib/SimpleFTPServer/examples/STM32_SPIFlash_enc28j60/STM32_SPIFlash_enc28j60.ino new file mode 100644 index 00000000..761cd9a3 --- /dev/null +++ b/lib/SimpleFTPServer/examples/STM32_SPIFlash_enc28j60/STM32_SPIFlash_enc28j60.ino @@ -0,0 +1,136 @@ +/** + * SimpleFTPServer ^1.3.0 on STM32 (need FLASH > 64K) + * and ethernet w5500 + * SPI Flash with Adafruit_SPIFlash and SdFat-Adafruit-Fork library + * + +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 NETWORK_ETHERNET_ENC + #define DEFAULT_STORAGE_TYPE_STM32 STORAGE_SPIFM +#endif + + * + * @author Renzo Mischianti + * @details https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + * @version 0.1 + * @date 2022-03-22 + * + * @copyright Copyright (c) 2022 + * + */ + +#include + +#include + +#include "SdFat.h" +#include "Adafruit_SPIFlash.h" + +#include + +Adafruit_FlashTransport_SPI flashTransport(SS, SPI); // Set CS and SPI interface +Adafruit_SPIFlash flash(&flashTransport); + +// file system object from SdFat +FatFileSystem fatfs; + +#define ETHERNET_CS_PIN PA3 + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +#define MYIPADDR 192,168,1,28 +#define MYIPMASK 255,255,255,0 +#define MYDNS 192,168,1,1 +#define MYGW 192,168,1,1 + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + // Initialize serial port and wait for it to open before continuing. + Serial.begin(115200); + while (!Serial) { + delay(100); + } + Serial.println("Adafruit SPI Flash FatFs Full Usage Example"); + + // Initialize flash library and check its chip ID. + if (!flash.begin()) { + Serial.println("Error, failed to initialize flash chip!"); + while(1) yield(); + } + + Serial.print("JEDEC ID: "); Serial.println(flash.getJEDECID(), HEX); + Serial.print("Flash size: "); Serial.println(flash.size()); + Serial.flush(); + + // First call begin to mount the filesystem. Check that it returns true + // to make sure the filesystem was mounted. + if (!fatfs.begin(&flash)) { + Serial.println("Error, failed to mount newly formatted filesystem!"); + Serial.println("Was the flash chip formatted with the SdFat_format example?"); + while(1) yield(); + } + Serial.println("Mounted filesystem!"); + Serial.println(); + + + // You can use Ethernet.init(pin) to configure the CS pin + Ethernet.init(ETHERNET_CS_PIN); + + if (Ethernet.begin(mac)) { // Dynamic IP setup + Serial.println(F("DHCP OK!")); + }else{ + Serial.println(F("Failed to configure Ethernet using DHCP")); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println(F("Ethernet shield was not found. Sorry, can't run without hardware. :(")); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println(F("Ethernet cable is not connected.")); + } + + IPAddress ip(MYIPADDR); + IPAddress dns(MYDNS); + IPAddress gw(MYGW); + IPAddress sn(MYIPMASK); + Ethernet.begin(mac, ip, dns, gw, sn); + Serial.println("STATIC OK!"); + } + delay(5000); + + + Serial.print("Local IP : "); + Serial.println(Ethernet.localIP()); + Serial.print("Subnet Mask : "); + Serial.println(Ethernet.subnetMask()); + Serial.print("Gateway IP : "); + Serial.println(Ethernet.gatewayIP()); + Serial.print("DNS Server : "); + Serial.println(Ethernet.dnsServerIP()); + + Serial.println("Ethernet Successfully Initialized"); + Serial.println(); + + + // Initialize the FTP server + ftpSrv.begin("user","password"); + Serial.println("Ftp server started!"); +} + +void loop() +{ + ftpSrv.handleFTP(); + + // more processes... +} diff --git a/lib/SimpleFTPServer/examples/STM32_SdFat_enc28j60/STM32_SdFat_enc28j60.ino b/lib/SimpleFTPServer/examples/STM32_SdFat_enc28j60/STM32_SdFat_enc28j60.ino new file mode 100644 index 00000000..614ecdd8 --- /dev/null +++ b/lib/SimpleFTPServer/examples/STM32_SdFat_enc28j60/STM32_SdFat_enc28j60.ino @@ -0,0 +1,135 @@ +/** + * SimpleFTPServer ^1.3.0 on STM32 (need FLASH > 64K) + * and ethernet w5500 + * SD connected on secondary SPI or primary + * + +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 NETWORK_W5100 + #define DEFAULT_STORAGE_TYPE_STM32 STORAGE_SDFAT2 +#endif + + * + * @author Renzo Mischianti + * @details https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + * @version 0.1 + * @date 2022-03-22 + * + * @copyright Copyright (c) 2022 + * + */ +#include +#include +#include +#include + +// Ethernet CS +#define ETHERNET_CS_PIN PA3 + +// To use SD with primary SPI +#define SD_CS_PIN PA2 + +// To use SD with secondary SPI +// #define SD_CS_PIN PB12 +// static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN); +// #define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2) + +SdFat sd; + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +#define MYIPADDR 192,168,1,28 +#define MYIPMASK 255,255,255,0 +#define MYDNS 192,168,1,1 +#define MYGW 192,168,1,1 + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + Serial.begin( 115200 ); + + while (!Serial) { delay(100); } + + pinMode( SD_CS_PIN, OUTPUT ); + digitalWrite( SD_CS_PIN, HIGH ); + pinMode( ETHERNET_CS_PIN, OUTPUT ); + digitalWrite( ETHERNET_CS_PIN, HIGH ); + + Serial.print("\nInitializing SD card..."); + + // To use SD with secondary SPI + // if (!sd.begin(SD2_CONFIG)) { + // To use SD with primary SPI + if (!sd.begin(SD_CS_PIN)) { + Serial.println(F("initialization failed. Things to check:")); + Serial.println(F("* is a card inserted?")); + Serial.println(F("* is your wiring correct?")); + Serial.println(F("* did you change the chipSelect pin to match your shield or module?")); + while (1); + } else { + Serial.println(F("Wiring is correct and a card is present.")); + } + + // Show capacity and free space of SD card + Serial.print(F("Capacity of card: ")); Serial.print(long( sd.card()->sectorCount() >> 1 )); Serial.println(F(" kBytes")); + + // You can use Ethernet.init(pin) to configure the CS pin + Ethernet.init(ETHERNET_CS_PIN); + + if (Ethernet.begin(mac)) { // Dynamic IP setup + Serial.println(F("DHCP OK!")); + }else{ + Serial.println(F("Failed to configure Ethernet using DHCP")); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println(F("Ethernet shield was not found. Sorry, can't run without hardware. :(")); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println(F("Ethernet cable is not connected.")); + } + + IPAddress ip(MYIPADDR); + IPAddress dns(MYDNS); + IPAddress gw(MYGW); + IPAddress sn(MYIPMASK); + Ethernet.begin(mac, ip, dns, gw, sn); + Serial.println("STATIC OK!"); + } + delay(5000); + + + Serial.print("Local IP : "); + Serial.println(Ethernet.localIP()); + Serial.print("Subnet Mask : "); + Serial.println(Ethernet.subnetMask()); + Serial.print("Gateway IP : "); + Serial.println(Ethernet.gatewayIP()); + Serial.print("DNS Server : "); + Serial.println(Ethernet.dnsServerIP()); + + Serial.println("Ethernet Successfully Initialized"); + Serial.println(); + + + // Initialize the FTP server + ftpSrv.begin("user","password"); + Serial.println("Ftp server started!"); +} + +void loop() +{ + ftpSrv.handleFTP(); + + // more processes... +} diff --git a/lib/SimpleFTPServer/examples/STM32_SdFat_w5500/STM32_SdFat_w5500.ino b/lib/SimpleFTPServer/examples/STM32_SdFat_w5500/STM32_SdFat_w5500.ino new file mode 100644 index 00000000..4276cf20 --- /dev/null +++ b/lib/SimpleFTPServer/examples/STM32_SdFat_w5500/STM32_SdFat_w5500.ino @@ -0,0 +1,128 @@ +/** + * SimpleFTPServer ^1.3.0 on STM32 (need FLASH > 64K) + * and ethernet w5500 + * SD connected on secondary SPI or primary + * + +#ifndef DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 + #define DEFAULT_FTP_SERVER_NETWORK_TYPE_STM32 NETWORK_W5100 + #define DEFAULT_STORAGE_TYPE_STM32 STORAGE_SDFAT2 +#endif + + * + * @author Renzo Mischianti + * @details https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + * @version 0.1 + * @date 2022-03-22 + * + * @copyright Copyright (c) 2022 + * + */ +#include +#include +#include + +#include + +// To use SD with primary SPI +// #define SD_CS_PIN PA4 + +// To use SD with secondary SPI +#define SD_CS_PIN PB12 +static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN); +#define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2) + +SdFat sd; + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +#define MYIPADDR 192,168,1,28 +#define MYIPMASK 255,255,255,0 +#define MYDNS 192,168,1,1 +#define MYGW 192,168,1,1 + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + Serial.begin( 115200 ); + + while (!Serial) { delay(100); } + + Serial.print("\nInitializing SD card..."); + + // Secondary SPI for SD + if (!sd.begin(SD2_CONFIG)) { + // Primary SPI for SD + // if (!SD.begin(SD_CS_PIN)) { + Serial.println(F("initialization failed. Things to check:")); + Serial.println(F("* is a card inserted?")); + Serial.println(F("* is your wiring correct?")); + Serial.println(F("* did you change the chipSelect pin to match your shield or module?")); + while (1); + } else { + Serial.println(F("Wiring is correct and a card is present.")); + } + + // Show capacity and free space of SD card + Serial.print(F("Capacity of card: ")); Serial.print(long( sd.card()->sectorCount() >> 1 )); Serial.println(F(" kBytes")); + + // You can use Ethernet.init(pin) to configure the CS pin + Ethernet.init(PA4); + + if (Ethernet.begin(mac)) { // Dynamic IP setup + Serial.println(F("DHCP OK!")); + }else{ + Serial.println(F("Failed to configure Ethernet using DHCP")); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println(F("Ethernet shield was not found. Sorry, can't run without hardware. :(")); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println(F("Ethernet cable is not connected.")); + } + + IPAddress ip(MYIPADDR); + IPAddress dns(MYDNS); + IPAddress gw(MYGW); + IPAddress sn(MYIPMASK); + Ethernet.begin(mac, ip, dns, gw, sn); + Serial.println("STATIC OK!"); + } + delay(5000); + + + Serial.print("Local IP : "); + Serial.println(Ethernet.localIP()); + Serial.print("Subnet Mask : "); + Serial.println(Ethernet.subnetMask()); + Serial.print("Gateway IP : "); + Serial.println(Ethernet.gatewayIP()); + Serial.print("DNS Server : "); + Serial.println(Ethernet.dnsServerIP()); + + Serial.println("Ethernet Successfully Initialized"); + Serial.println(); + + + // Initialize the FTP server + ftpSrv.begin("user","password"); + Serial.println("Ftp server started!"); +} + +void loop() +{ + ftpSrv.handleFTP(); + + // more processes... +} diff --git a/lib/SimpleFTPServer/examples/Wio_terminal/Wio_terminal.ino b/lib/SimpleFTPServer/examples/Wio_terminal/Wio_terminal.ino new file mode 100644 index 00000000..24801348 --- /dev/null +++ b/lib/SimpleFTPServer/examples/Wio_terminal/Wio_terminal.ino @@ -0,0 +1,106 @@ +/* + * FtpServer Wio Terminal + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + */ + +#include +#include "SD/Seeed_SD.h" + +// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN +// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SEEED_SD + +#include + +#include + +FtpServer ftpSrv; + +const char *ssid = ""; +const char *password = ""; + +void listDir(const char* dirname, uint8_t levels) { + Serial.print("Listing directory: "); + Serial.println(dirname); + + File root = SD.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while (file) { + if (file.isDirectory()) { + Serial.print(" DIR : "); + Serial.println(file.name()); + if (levels) { + listDir(file.name(), levels - 1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + + +void setup() +{ + Serial.begin(115200); + delay(1000); + + pinMode(5, OUTPUT); + digitalWrite(5, HIGH); + + while (!SD.begin(SDCARD_SS_PIN,SDCARD_SPI,4000000UL)) { + Serial.println("Card Mount Failed"); + return; + } + + // We start by connecting to a WiFi network + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.print(ssid); + + WiFi.mode(WIFI_STA); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("Connecting to "); + Serial.println(ssid); + WiFi.begin(ssid, password); + Serial.print("."); + delay(500); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + delay(1000); + + Serial.print("Starting SD."); + + Serial.println("finish!"); + + listDir("/", 0); + + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. + } + + void loop(void) { + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + } diff --git a/lib/SimpleFTPServer/examples/Wio_terminal_SdFat/Wio_terminal_SdFat.ino b/lib/SimpleFTPServer/examples/Wio_terminal_SdFat/Wio_terminal_SdFat.ino new file mode 100644 index 00000000..4db9a041 --- /dev/null +++ b/lib/SimpleFTPServer/examples/Wio_terminal_SdFat/Wio_terminal_SdFat.ino @@ -0,0 +1,104 @@ +/* + * FtpServer Wio Terminal with SdFat library + * and with callbacks to the main actions of FTP server + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + * + */ + +#include "SdFat.h" + +#include + +#include + +// #define DEFAULT_FTP_SERVER_NETWORK_TYPE_SAMD NETWORK_SEEED_RTL8720DN +// #define DEFAULT_STORAGE_TYPE_SAMD STORAGE_SDFAT2 + +#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, 2) +SdFs sd; + +FtpServer ftpSrv; + +const char *ssid = ""; +const char *password = ""; + +void setup() +{ + Serial.begin(115200); + delay(1000); + + pinMode(5, OUTPUT); + digitalWrite(5, HIGH); + + // Initialize the SD. + if (!sd.begin(SD_CONFIG)) { + sd.initErrorHalt(&Serial); + } + FsFile dir; + FsFile file; + + // Open root directory + if (!dir.open("/")){ + Serial.println("dir.open failed"); + } + + // We start by connecting to a WiFi network + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.print(ssid); + + WiFi.mode(WIFI_STA); + + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("Connecting to "); + Serial.println(ssid); + WiFi.begin(ssid, password); + Serial.print("."); + delay(500); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + delay(1000); + + Serial.print("Starting SD."); + + Serial.println("finish!"); + + while (file.openNext(&dir, O_RDONLY)) { + file.printFileSize(&Serial); + Serial.write(' '); + file.printModifyDateTime(&Serial); + Serial.write(' '); + file.printName(&Serial); + if (file.isDir()) { + // Indicate a directory. + Serial.write('/'); + } + Serial.println(); + file.close(); + } + if (dir.getError()) { + Serial.println("openNext failed"); + } else { + Serial.println("Done!"); + } + + + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. + } + + void loop(void) { + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + } diff --git a/lib/SimpleFTPServer/examples/Wio_terminal_SdFat_TFT_monitor/Wio_terminal_SdFat_TFT_monitor.ino b/lib/SimpleFTPServer/examples/Wio_terminal_SdFat_TFT_monitor/Wio_terminal_SdFat_TFT_monitor.ino new file mode 100644 index 00000000..533944c5 --- /dev/null +++ b/lib/SimpleFTPServer/examples/Wio_terminal_SdFat_TFT_monitor/Wio_terminal_SdFat_TFT_monitor.ino @@ -0,0 +1,367 @@ +/* + * FtpServer Wio Terminal with SdFat library + * and with callbacks to the main actions of FTP server + * and a monitor on TFT + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/category/my-libraries/simple-ftp-server/ + * + */ + +#include "SdFat.h" + +#include + + +#include // Hardware-specific library +#include + +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +#define DEG2RAD 0.0174532925 + +byte inc = 0; +unsigned int col = 0; + +#define SD_CONFIG SdSpiConfig(SDCARD_SS_PIN, 2) +SdFs sd; + +FtpServer ftpSrv; + +const char *ssid = "reef-casa-sopra"; +const char *password = "aabbccdd77"; + +#define MAIN_TOP 110 + +#define FREE_SPACE_PIE_X 80 +#define FREE_SPACE_PIE_Y MAIN_TOP+40 +#define FREE_SPACE_PIE_RADIUS 50 + +void freeSpacePieData(unsigned int freeSpace, unsigned int totalSpace) { + int pieFree = 360 - (freeSpace * 360 / totalSpace); + + fillSegment(FREE_SPACE_PIE_X, FREE_SPACE_PIE_Y, 0, pieFree, FREE_SPACE_PIE_RADIUS, TFT_RED); + fillSegment(FREE_SPACE_PIE_X, FREE_SPACE_PIE_Y, pieFree, 360 - pieFree, FREE_SPACE_PIE_RADIUS, TFT_BLUE); + + // Set "cursor" at top left corner of display (0,0) and select font 2 + // (cursor will move to next line automatically during printing with 'tft.println' + // or stay on the line is there is room for the text with tft.print) + tft.setCursor(FREE_SPACE_PIE_X + 80, MAIN_TOP, 2); + // Set the font colour to be white with a black background, set text size multiplier to 1 + tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(1); + // We can now plot text on screen using the "print" class + Serial.print(freeSpace/1000);Serial.print("Mb/");Serial.print(String(totalSpace/1000));Serial.println("Mb"); + tft.print(freeSpace/1000);tft.print("Mb/");tft.print(String(totalSpace/1000));tft.println("Mb"); +} + +void connectedDisconnected(bool connected) { + tft.fillCircle(FREE_SPACE_PIE_X + 80 + 10, MAIN_TOP+25+7, 10, (connected)?TFT_GREEN:TFT_RED); + + tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25, 2); + tft.println(" "); + + tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25, 2); + (connected)?tft.println("Connected!"):tft.println("Disconnected!"); +} + +void transfer(bool transfer, bool upload) { + tft.fillCircle(FREE_SPACE_PIE_X + 80 + 10, MAIN_TOP+25+25+7, 10, (transfer)?(upload)?TFT_GREEN:TFT_BLUE:TFT_RED); + + tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25+25, 2); + tft.println(" "); + + tft.setCursor(FREE_SPACE_PIE_X + 80 + 25, MAIN_TOP+25+25, 2); + (transfer)?tft.println((upload)?"Upload!":"Download!"):tft.println("Idle!"); +} + +//index - starting at, n- how many chars +char* subString(const char *s, int index, int n){ + char* b = (char*) malloc((strlen(s) + 1) * sizeof(char)); + strcpy(b,s); + + Serial.println("--------------------------------------"); + Serial.println(s); + Serial.println(index); + Serial.println(n); + char *res = new char[n + 1]; + Serial.println(res); + sprintf(res, "%.*s", n, b + index); + Serial.println(res); + free(b); + return res; +} + + +void fileTransfer(FtpTransferOperation ftpOperation, const char* filename, unsigned int transferredSize) { + int yoffset = 2; + + tft.setCursor(20, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2); + tft.println(F(" ")); + + tft.setCursor(20, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2); + int lenfile = strlen(filename); + Serial.println(lenfile); + if (lenfile>22) { + + tft.print(subString(filename, 0, 16));tft.print(F("~")); + tft.print( subString(filename, (lenfile-4), 4) ); + } else { + tft.print(filename); + } + tft.setCursor(20+160, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2); + tft.print(F(" ")); + tft.setCursor(20+160, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2); + tft.print(transferredSize);tft.print("Kb"); + + tft.setCursor(320-55, MAIN_TOP+(FREE_SPACE_PIE_RADIUS*2)+yoffset, 2); + switch (ftpOperation) { + case FTP_UPLOAD: + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.print(F("Upload")); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + break; + case FTP_DOWNLOAD: + tft.setTextColor(TFT_BLUE, TFT_BLACK); + tft.print(F("Down")); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + break; + case FTP_TRANSFER_STOP: + tft.setTextColor(TFT_GREEN, TFT_BLACK); + tft.print(F("OK")); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + break; + case FTP_TRANSFER_ERROR: + tft.setTextColor(TFT_RED, TFT_BLACK); + tft.print(F("Error")); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + break; + + default: + break; + } + +} + +void wifiStrenght (int8_t RSSI, bool connection = false) { + Serial.print("RSSI --> ");Serial.println(RSSI); + int marginX = 30; + + int startX = 90; + int widthW = 320-(startX+marginX); + + int startY = 60; + int heightY = 10; + + tft.setCursor(marginX, startY - 5, 2); + tft.print(F(" ")); + tft.setCursor(marginX, startY - 5, 2); + + if (connection) { + tft.print(F("Connectint to: ")); + tft.print(ssid); + }else{ + tft.println("WiFi str: "); + + // 120 : 120-RSSI = 300 : x + + tft.drawRoundRect(startX, startY, widthW, heightY, 5, TFT_WHITE); + + uint32_t colorRSSI = TFT_GREEN; + if (abs(RSSI)<55) { + colorRSSI = TFT_GREEN; + } else if (abs(RSSI)<75) { + colorRSSI = TFT_YELLOW; + } else if (abs(RSSI)<75) { + colorRSSI = TFT_RED; + } + + tft.fillRoundRect(startX+1, startY+1, (120+RSSI)*widthW/120, heightY-2, 5, colorRSSI); + + tft.setCursor(marginX, startY + 15, 2); + + tft.print("IP: "); + tft.println(WiFi.localIP()); + } +} + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + Serial.print(">>>>>>>>>>>>>>> _callback " ); + Serial.print(ftpOperation); + /* FTP_CONNECT, + * FTP_DISCONNECT, + * FTP_FREE_SPACE_CHANGE + */ + Serial.print(" "); + Serial.print(freeSpace); + Serial.print(" "); + Serial.println(totalSpace); + + // freeSpace : totalSpace = x : 360 + + freeSpacePieData(freeSpace, totalSpace); + + if (ftpOperation == FTP_CONNECT) connectedDisconnected(true); + if (ftpOperation == FTP_DISCONNECT) connectedDisconnected(false); +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + Serial.print(">>>>>>>>>>>>>>> _transferCallback " ); + Serial.print(ftpOperation); + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ + Serial.print(" "); + Serial.print(name); + Serial.print(" "); + Serial.println(transferredSize); + + (ftpOperation==FTP_UPLOAD || ftpOperation==FTP_DOWNLOAD)?transfer(true, ftpOperation==FTP_UPLOAD):transfer(false, false); + + fileTransfer(ftpOperation, name, transferredSize); +}; + + +void setup() +{ + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.begin(115200); + delay(1000); + + tft.init(); + + tft.begin(); + + tft.setRotation(3); + + tft.fillScreen(TFT_BLACK); + + tft.setCursor(0, 0); + + tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.setTextSize(2); + + tft.fillRoundRect(3, 3, 320-6, 40, 5, TFT_WHITE); + + tft.drawCentreString("www.mischianti.org", 160, 14,1); + tft.setTextColor(TFT_WHITE, TFT_BLACK); + + + freeSpacePieData(0, 0); + connectedDisconnected(false); + transfer(false, false); + + wifiStrenght(0, true); + + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.print(ssid); + + WiFi.mode(WIFI_STA); + + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + WiFi.begin(ssid, password); + Serial.print("."); + tft.print(F(".")); + delay(500); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + wifiStrenght(WiFi.RSSI()); + + delay(1000); + + if (!sd.begin(SD_CONFIG)) { + sd.initErrorHalt(&Serial); + } + FsFile dir; + FsFile file; + + // Open root directory + if (!dir.open("/")){ + Serial.println("dir.open failed"); + } + + ftpSrv.begin("wioterminal","wioterminal"); //username, password for ftp. + +} + +void loop() { + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} + + +// ######################################################################### +// Draw circle segments +// ######################################################################### + +// x,y == coords of centre of circle +// start_angle = 0 - 359 +// sub_angle = 0 - 360 = subtended angle +// r = radius +// colour = 16 bit colour value + +int fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour) { + // Calculate first pair of coordinates for segment start + float sx = cos((start_angle - 90) * DEG2RAD); + float sy = sin((start_angle - 90) * DEG2RAD); + uint16_t x1 = sx * r + x; + uint16_t y1 = sy * r + y; + + // Draw colour blocks every inc degrees + for (int i = start_angle; i < start_angle + sub_angle; i++) { + + // Calculate pair of coordinates for segment end + int x2 = cos((i + 1 - 90) * DEG2RAD) * r + x; + int y2 = sin((i + 1 - 90) * DEG2RAD) * r + y; + + tft.fillTriangle(x1, y1, x2, y2, x, y, colour); + + // Copy segment end to sgement start for next segment + x1 = x2; + y1 = y2; + } +} + + +// ######################################################################### +// Return the 16 bit colour with brightness 0-100% +// ######################################################################### +unsigned int brightness(unsigned int colour, int brightness) { + byte red = colour >> 11; + byte green = (colour & 0x7E0) >> 5; + byte blue = colour & 0x1F; + + blue = (blue * brightness) / 100; + green = (green * brightness) / 100; + red = (red * brightness) / 100; + + return (red << 11) + (green << 5) + blue; +} + diff --git a/lib/SimpleFTPServer/examples/esp8266_esp32_LittleFS/esp8266_esp32_LittleFS.ino b/lib/SimpleFTPServer/examples/esp8266_esp32_LittleFS/esp8266_esp32_LittleFS.ino new file mode 100644 index 00000000..0d82e764 --- /dev/null +++ b/lib/SimpleFTPServer/examples/esp8266_esp32_LittleFS/esp8266_esp32_LittleFS.ino @@ -0,0 +1,110 @@ +/* + * FtpServer esp8266 and esp32 with LittleFS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#ifdef ESP8266 +#include +#include +#elif defined ESP32 +#include +#include +#include +#endif + +#include + +const char* ssid = ""; +const char* password = ""; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// +#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount + if (LittleFS.begin(true)) { +#elif defined ESP8266 + if (LittleFS.begin()) { +#endif + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("LittleFS opened!"); + ftpSrv.begin("user","password"); //username, password for ftp. (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! +} diff --git a/lib/SimpleFTPServer/examples/esp8266_esp32_SPIFFS/esp8266_esp32_SPIFFS.ino b/lib/SimpleFTPServer/examples/esp8266_esp32_SPIFFS/esp8266_esp32_SPIFFS.ino new file mode 100644 index 00000000..c67a1ade --- /dev/null +++ b/lib/SimpleFTPServer/examples/esp8266_esp32_SPIFFS/esp8266_esp32_SPIFFS.ino @@ -0,0 +1,110 @@ +/* + * FtpServer esp8266 and esp32 with SPIFFS + * + * AUTHOR: Renzo Mischianti + * + * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32 + * + */ + +#ifdef ESP8266 +#include +#elif defined ESP32 +#include +#include "SPIFFS.h" +#endif + +#include + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASS"; + + +FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial + +void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){ + switch (ftpOperation) { + case FTP_CONNECT: + Serial.println(F("FTP: Connected!")); + break; + case FTP_DISCONNECT: + Serial.println(F("FTP: Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace); + break; + default: + break; + } +}; +void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){ + switch (ftpOperation) { + case FTP_UPLOAD_START: + Serial.println(F("FTP: Upload start!")); + break; + case FTP_UPLOAD: + Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize); + break; + case FTP_TRANSFER_STOP: + Serial.println(F("FTP: Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + Serial.println(F("FTP: Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ +}; + +void setup(void){ + Serial.begin(115200); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// + + /////FTP Setup, ensure SPIFFS is started before ftp; ///////// +#ifdef ESP32 //esp32 we send true to format spiffs if cannot mount + if (SPIFFS.begin(true)) { +#elif defined ESP8266 + if (SPIFFS.begin()) { +#endif + ftpSrv.setCallback(_callback); + ftpSrv.setTransferCallback(_transferCallback); + + Serial.println("SPIFFS opened!"); + ftpSrv.begin("esp8266","esp8266"); //username, password for ftp. (default 21, 50009 for PASV) + } +} +void loop(void){ + ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!! + // server.handleClient(); //example if running a webserver you still need to call .handleClient(); + +} diff --git a/lib/SimpleFTPServer/keywords.txt b/lib/SimpleFTPServer/keywords.txt new file mode 100644 index 00000000..5cc521ad --- /dev/null +++ b/lib/SimpleFTPServer/keywords.txt @@ -0,0 +1,17 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +SimpleFtpServer KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 +setLocalIp KEYWORD2 +credentials KEYWORD2 +setCallback KEYWORD2 +setTransferCallback KEYWORD2 +handleFTP KEYWORD2 \ No newline at end of file diff --git a/lib/SimpleFTPServer/library.json b/lib/SimpleFTPServer/library.json new file mode 100644 index 00000000..9ad3dfc9 --- /dev/null +++ b/lib/SimpleFTPServer/library.json @@ -0,0 +1,21 @@ +{ + "name": "SimpleFTPServer", + "description": "Simple FTP Server for using esp8266, esp32, STM32, Arduino", + "keywords": "arduino, esp8266, esp32, stm32, rp2040, Raspberry Pi, ftp, FtpServer, spiffs, Fat, LittleFS, Ethernet, WiFi, WiFiNINA", + "homepage": "https://www.mischianti.org/category/my-libraries/simple-ftp-server/", + "authors": + { + "name": "Renzo Mischianti", + "email": "renzo.mischianti@gmail.com", + "url": "https://www.mischianti.org" + }, + "repository": + { + "type": "git", + "url": "https://github.com/xreef/SimpleFTPServer" + }, + "url": "https://www.mischianti.org", + "frameworks": "Arduino", + "version": "2.1.6", + "platforms": "*" +} diff --git a/lib/SimpleFTPServer/library.properties b/lib/SimpleFTPServer/library.properties new file mode 100644 index 00000000..e94fd11b --- /dev/null +++ b/lib/SimpleFTPServer/library.properties @@ -0,0 +1,11 @@ +name=SimpleFTPServer +version=2.1.6 +author=Renzo Mischianti +maintainer=Renzo Mischianti +sentence=Simple FTP server for esp8266, esp32, STM32, Raspberry Pi Pico and Arduino +paragraph=Simple FTP server for Raspberry Pi Pico W (LittleFS), esp8266 (SPIFFS and LittleFS or SD, SdFat 2.x), esp32 (SPIFFS, LittleFS and FFAT or SD, SdFat 2.x) and Arduino (SdFat, SD basic lib with 8.3 file format), Wio Terminal (Seed_SD, SdFat 2.x), Arduino MKR (SdFat 2), STM32 (Flash >64K SdFat 2.x and SPI Flash). Support w5500, w5100 and enc28j60. With internal callback to chck the phase of communication. +category=Communication +url=https://www.mischianti.org/category/my-libraries/simple-ftp-server/ +repository=https://github.com/xreef/SimpleFTPServer.git +architectures=* +includes=SimpleFTPServer.h \ No newline at end of file diff --git a/lib/decoder/include/shared/theengs.h b/lib/decoder/include/shared/theengs.h new file mode 100644 index 00000000..3ed58da7 --- /dev/null +++ b/lib/decoder/include/shared/theengs.h @@ -0,0 +1,39 @@ +/* + TheengsDecoder - Decode things and devices + + Copyright: (c)Florian ROBERT + + This file is part of TheengsDecoder. + + TheengsDecoder is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TheengsDecoder is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _THEENGS_H_ +#define _THEENGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void* Theengs_NewDecoder(); +void Theengs_DestroyDecoder(void* decoder); +const char* Theengs_DecodeBLE(void* decoder, const char* json_data); +const char* Theengs_GetProperties(void* decoder, const char* model_id); +const char* Theengs_GetAttribute(void* decoder, const char* model_id, const char* attribute); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _THEENGS_H_ \ No newline at end of file diff --git a/lib/decoder/src/decoder.h b/lib/decoder/src/decoder.h new file mode 100644 index 00000000..f31e131d --- /dev/null +++ b/lib/decoder/src/decoder.h @@ -0,0 +1,1112 @@ +/* + TheengsDecoder - Decode things and devices + + Copyright: (c)Florian ROBERT + + This file is part of TheengsDecoder. + + TheengsDecoder is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TheengsDecoder is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +//#define DEBUG_DECODER +#ifdef BLE_PART1 +#define PART1_XIAOMI +#endif +#ifdef BLE_PART2 +#define PART2_OTHER +#endif + + +#ifndef _DECODER_H_ +#define _DECODER_H_ + +//#define ARDUINOJSON_USE_LONG_LONG 1 +#include "ArduinoJson.h" + + + +#include +#include + +#include "devices.h" + +#ifdef DEBUG_DECODER +# include +# define DEBUG_PRINT(...) \ + { printf(__VA_ARGS__); } +#else +# define DEBUG_PRINT(...) \ + {} +#endif + +#ifdef UNIT_TESTING +# define TEST_MAX_DOC 16384UL +# include +static size_t peakDocSize = 0; +#endif + +#define SVC_DATA "servicedata" +#define MFG_DATA "manufacturerdata" + +class TheengsDecoder; +typedef double (TheengsDecoder::*decoder_function)(const char* data_str, + int offset, int data_length, + bool reverse, bool canBeNegative, bool isFloat); + +typedef double (TheengsDecoder::*staticbitdecoder_function)(const char* data_str, + const char* source_str, int offset, int bitindex, + const char* falseresult, const char* trueresult); + + + +class TheengsDecoder { +public: + + TheengsDecoder() {} + ~TheengsDecoder() {} +/* + int decodeBLEJson(JsonObject& jsondata); + void setMinServiceDataLen(size_t len); + void setMinManufacturerDataLen(size_t len); + std::string getTheengProperties(const char* model_id); + std::string getTheengProperties(int mod_index); + std::string getTheengAttribute(const char* model_id, const char* attribute); + std::string getTheengAttribute(int model_id, const char* attribute); + int getTheengModel(JsonDocument& doc, const char* model_id); +#ifdef UNIT_TESTING + int testDocMax(); +#endif +*/ + enum BLE_ID_NUM { + UNKNOWN_MODEL = -1, + HHCCJCY01HHCC = 0, + LYWSD02, + LYWSDCGQ, + CGP1W, + CGG1_STOCK, + CGG1_ATC1441, + CGG1_PVVX, + CGG1_STOCK_2, + CGDN1, + CGD1, + CGDK2_STOCK, + CGDK2_PVVX, + CGDK2_ATC1441, + CGH1, + JQJCY01YM, + IBSTHBP01B, + IBT_2X, + IBT_2XS, + IBT4XS, + IBT6XS_SOLIS, + MIBAND, + XMTZC04HMKG, + XMTZC04HMLB, + XMTZC05HMKG, + XMTZC05HMLB, + TPMS, + KKM_K6P, + KKM_K9, + LYWSD03MMC_ATC, + LYWSD03MMC_PVVX, + LYWSD03MMC_PVVX_DECR, + LYWSD03MMC_PVVX_ENCR, + CGPR1, + THERMOBEACON, + H5055, + H5072, + H5074, + H5102, + H5106, + H5179, + HHCCJCY10, + MUE4094RT, + MOKOBEACON, + MOKOBEACONXPRO, + INODEEM, + RUUVITAG_RAWV1, + RUUVITAG_RAWV2, + SBCS, + SBCU, + SBMS, + SBMT, + SBOT, + SBS1, + SHT4X, + SCD4X, + SKALE, + SMARTDRY, + BC08, + BM1IN1, + BM3IN1, + BM4IN1, + MS_CDP, + GAEN, + HHCCPOT002, + BPARASITE, + BWBSDOO, + BM2, + BM6, + RDL52832, + ABN03, + ABN07, + ABTEMP, + AMPHIRO, + ORALB_BT, + PH10, + TPTH, + MOPEKA, + T201, + T301, + NUT, + ITAG, + TAGIT, + TILE, + TILEN, + JHT_F525, + IBEACON, + APPLE_CONT, + APPLE_CONTAT, + SERVICE_DATA, + SBBT_002C, + SBBT_002C_ENCR, + SBDW_002C, + SBDW_002C_ENCR, + SBMO_003Z, + SBMO_003Z_ENCR, + BLE_ID_MAX + }; +/* +private: + void reverse_hex_data(const char* in, char* out, int l); + double value_from_hex_string(const char* data_str, int offset, int data_length, bool reverse, bool canBeNegative = true, bool isFloat = false); + double bf_value_from_hex_string(const char* data_str, int offset, int data_length, bool reverse, bool canBeNegative = true, bool isFloat = false); + bool data_index_is_valid(const char* str, size_t index, size_t len); + bool data_length_is_valid(size_t data_len, size_t default_min, const JsonArray& condition, int *idx); + uint8_t getBinaryData(char ch); + bool evaluateDatalength(std::string op, size_t data_len, size_t req_len); + bool checkPropCondition(const JsonArray& prop, const char* svc_data, const char* mfg_data); + bool checkDeviceMatch(const JsonArray& condition, const char* svc_data, const char* mfg_data, + const char* dev_name, const char* svc_uuid, const char* mac_id); + std::string sanitizeJsonKey(const char* key_in); + */ + + size_t m_docMax = 12000; + size_t m_minSvcDataLen = 20; + size_t m_minMfgDataLen = 16; + + + +/* + * @brief Revert the string data 2 by 2 to get the correct endianness + */ +void reverse_hex_data(const char* in, char* out, int l) { + int i = l, j = 0; + while (i) { + out[j] = in[i - 2]; + out[j + 1] = in[i - 1]; + i -= 2; + j += 2; + } + out[l] = '\0'; +} + +double bf_value_from_hex_string(const char* data_str, + int offset, int data_length, + bool reverse, bool canBeNegative, bool isFloat) { + DEBUG_PRINT("extracting BCF data\n"); + + long value = (long)value_from_hex_string(data_str, offset, data_length, reverse, false, false); + double d_value = ((((value >> 8) * 100) + (uint8_t)value)) / 100.0; + + if (canBeNegative) { + if (data_length == 4 && value > SHRT_MAX) { + d_value = -d_value + (SCHAR_MAX + 1); + } + } + + return d_value; +} + +/* + * @brief Extracts the data value from the data string + */ +double value_from_hex_string(const char* data_str, + int offset, int data_length, + bool reverse, bool canBeNegative, bool isFloat) { + DEBUG_PRINT("offset: %d, len %d, rev %u, neg, %u, flo, %u\n", + offset, data_length, reverse, canBeNegative, isFloat); + std::string data(&data_str[offset], data_length); + + if (reverse) { + reverse_hex_data(&data_str[offset], &data[0], data_length); + } + + double value = 0; + if (!isFloat) { + value = strtoll(data.c_str(), NULL, 16); + DEBUG_PRINT("extracted value from %s = %lld\n", data.c_str(), (long long)value); + } else { + long longV = strtol(data.c_str(), NULL, 16); + float floatV = *((float*)&longV); + DEBUG_PRINT("extracted float value from %s = %f\n", data.c_str(), floatV); + value = floatV; + } + + if (canBeNegative) { + if (data_length <= 2 && value > SCHAR_MAX) { + value -= (UCHAR_MAX + 1); + } else if (data_length == 4 && value > SHRT_MAX) { + value -= (USHRT_MAX + 1); + } + } + + return value; +} + +/* + * @brief Removes the underscores at the beginning of key strings + * when duplicate properties exist in a device. + */ +std::string sanitizeJsonKey(const char* key_in) { + unsigned int key_index = 0; + while (key_in[key_index] == '_') { + key_index++; + } + return std::string(key_in + key_index); +} + +/* + * @brief Checks to ensure accessing data at the index + length of the string is valid. + */ +bool data_index_is_valid(const char* str, size_t index, size_t len) { + if (strlen(str) < (index + len)) { + return false; + } + return true; +} + +bool data_length_is_valid(size_t data_len, size_t default_min, + const JsonArray& condition, int* idx) { + std::string op = condition[*idx + 1].as(); + if (!op.empty() && op.length() > 2) { + return (data_len >= default_min); + } + + if (!condition[*idx + 2].is()) { + *idx = -1; + return false; + } + + size_t req_len = condition[*idx + 2].as(); + + *idx += 2; + return evaluateDatalength(op, data_len, req_len); +} + +uint8_t getBinaryData(char ch) { + uint8_t data = 0; + if (ch >= '0' && ch <= '9') + data = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + data = 10 + (ch - 'a'); + + return data; +} + +bool evaluateDatalength(std::string op, size_t data_len, size_t req_len) { + if (op == "=" && data_len == req_len) return true; + if (op == ">=" && data_len >= req_len) return true; + if (op == ">" && data_len > req_len) return true; + if (op == "<=" && data_len <= req_len) return true; + if (op == "<" && data_len < req_len) return true; + + return false; +} + +bool checkDeviceMatch(const JsonArray& condition, + const char* svc_data, + const char* mfg_data, + const char* dev_name, + const char* svc_uuid, + const char* mac_id) { + bool match = false; + int cond_size = condition.size(); + + for (int i = 0; i < cond_size;) { + if (condition[i].is()) { + DEBUG_PRINT("found nested array\n"); + match = checkDeviceMatch(condition[i], svc_data, mfg_data, dev_name, svc_uuid, mac_id); + + if (++i < cond_size) { + if (!match && *condition[i].as() == '|') { + } else if (match && *condition[i].as() == '&') { + match = false; + } else { + break; + } + i++; + } else { + break; + } + } + + const char* cmp_str = nullptr; + const char* cond_str = condition[i].as(); + if (svc_data != nullptr && strstr(cond_str, SVC_DATA) != nullptr) { + if (data_length_is_valid(strlen(svc_data), m_minSvcDataLen, condition, &i)) { + cmp_str = svc_data; + match = true; + } else { + match = false; + if (i < 0) { + break; + } + } + } else if (mfg_data != nullptr && strstr(cond_str, MFG_DATA) != nullptr) { + if (data_length_is_valid(strlen(mfg_data), m_minMfgDataLen, condition, &i)) { + cmp_str = mfg_data; + match = true; + } else { + match = false; + if (i < 0) { + break; + } + } + } else if (dev_name != nullptr && strstr(cond_str, "name") != nullptr) { + cmp_str = dev_name; + } else if (svc_uuid != nullptr && strstr(cond_str, "uuid") != nullptr) { + cmp_str = svc_uuid; + } else { + break; + } + + if (!match && cmp_str == nullptr) { + while (i < cond_size && *cond_str != '|') { + if (!condition[++i].is()) { + continue; + } + cond_str = condition[i].as(); + } + + if (i < cond_size && cond_str != nullptr) { + i++; + continue; + } + } + + cond_str = condition[++i].as(); + if (cmp_str != nullptr && cond_str != nullptr && *cond_str != '&' && *cond_str != '|') { + if (cmp_str == svc_uuid && !strncmp(cmp_str, "0x", 2)) { + cmp_str += 2; + } + + if (strstr(cond_str, "contain") != nullptr) { + if (strstr(cmp_str, condition[++i].as()) != nullptr) { + match = true; // (strstr(cond_str, "not_") != nullptr) ? false : true; + } else { + match = false; // (strstr(cond_str, "not_") != nullptr) ? true : false; + } + i++; + } else if (strstr(cond_str, "mac@index") != nullptr) { + size_t cond_index = condition[++i].as(); + size_t cond_len = 12; + const char* string_to_compare = nullptr; + std::string mac_string = mac_id; + + // remove colons and make lower case + for (int x = 0; x < mac_string.length(); x++) { + if (mac_string[x] == ':') { + mac_string.erase(x, 1); + } + mac_string[x] = tolower(mac_string[x]); + } + + string_to_compare = mac_string.c_str(); + + if (strstr(cond_str, "revmac@index") != nullptr) { + char* reverse_mac_string = (char*)malloc(strlen(string_to_compare) + 1); + + reverse_hex_data(string_to_compare, reverse_mac_string, 12); + string_to_compare = reverse_mac_string; + } + + if (!data_index_is_valid(cmp_str, cond_index, cond_len)) { + DEBUG_PRINT("Invalid data %s; skipping\n", cmp_str); + match = false; + break; + } + + DEBUG_PRINT("comparing value: %s to %s at index %zu\n", + &cmp_str[cond_index], + string_to_compare, + cond_index); + + if (strncmp(&cmp_str[cond_index], + string_to_compare, + 12) == 0) { + match = true; + } else { + match = false; + } + + i++; + } else if (strstr(cond_str, "index") != nullptr) { + size_t cond_index = condition[++i].as(); + size_t cond_len = strlen(condition[++i].as()); + + if (!data_index_is_valid(cmp_str, cond_index, cond_len)) { + DEBUG_PRINT("Invalid data %s; skipping\n", cmp_str); + match = false; + break; + } + + bool inverse = false; + if (*condition[i].as() == '!') { + inverse = true; + i++; + } + + DEBUG_PRINT("comparing value: %s to %s at index %zu\n", + &cmp_str[cond_index], + condition[i].as(), + cond_index); + + if (strncmp(&cmp_str[cond_index], + condition[i].as(), + cond_len) == 0) { + match = inverse ? false : true; + } else { + match = inverse ? true : false; + } + + i++; + } + + cond_str = condition[i].as(); + } + + if (i < cond_size && cond_str != nullptr) { + if (!match && *cond_str == '|') { + i++; + continue; + } else if (match && *cond_str == '&') { + i++; + match = false; + continue; + } else if (match) { // check for AND case before exit + while (i < cond_size && *cond_str != '&') { + if (!condition[++i].is()) { + continue; + } + cond_str = condition[i].as(); + } + + if (i < cond_size && cond_str != nullptr) { + i++; + match = false; + continue; + } + } + } + break; + } + return match; +} + +bool checkPropCondition(const JsonArray& prop_condition, + const char* svc_data, + const char* mfg_data) { + int cond_size = prop_condition.size(); + bool cond_met = prop_condition.isNull(); + + if (!cond_met) { + for (int i = 0; i < cond_size; i += 4) { + if (prop_condition[i].is()) { + DEBUG_PRINT("found nested array\n"); + cond_met = checkPropCondition(prop_condition[i], svc_data, mfg_data); + + if (++i < cond_size) { + if (!cond_met && *prop_condition[i].as() == '|') { + } else if (cond_met && *prop_condition[i].as() == '&') { + cond_met = false; + } else { + break; + } + i++; + } else { + break; + } + } + + bool inverse = 0; + const char* prop_data_src = prop_condition[i]; + const char* data_src = nullptr; + + if (svc_data && strstr(prop_data_src, SVC_DATA) != nullptr) { + data_src = svc_data; + } else if (mfg_data && strstr(prop_data_src, MFG_DATA) != nullptr) { + data_src = mfg_data; + } + + if (data_src) { + if (prop_condition[i + 1].is()) { + inverse = *(const char*)prop_condition[i + 2] == '!'; + size_t cond_len = strlen(prop_condition[i + 2 + inverse].as()); + if (strstr((const char*)prop_condition[i + 2], "bit") != nullptr) { + char ch = *(data_src + prop_condition[i + 1].as()); + uint8_t data = getBinaryData(ch); + + uint8_t shift = prop_condition[i + 3].as(); + uint8_t val = prop_condition[i + 4].as(); + if (((data >> shift) & 0x01) == val) { + cond_met = true; + } + i += 2; + } else if (!strncmp(&data_src[prop_condition[i + 1].as()], + prop_condition[i + 2 + inverse].as(), cond_len)) { + cond_met = inverse ? false : true; + } else if (strncmp(&data_src[prop_condition[i + 1].as()], + prop_condition[i + 2 + inverse].as(), cond_len)) { + cond_met = inverse ? true : false; + } + } else { + std::string op = prop_condition[i + 1].as(); + size_t data_len = strlen(data_src); + size_t req_len = prop_condition[i + 2].as(); + + cond_met = evaluateDatalength(op, data_len, req_len); + } + } else { + DEBUG_PRINT("ERROR property condition data source invalid\n"); + return false; + } + + i += inverse; + + if (cond_size > (i + 3)) { + if (!cond_met && *prop_condition[i + 3].as() == '|') { + continue; + } else if (cond_met && *prop_condition[i + 3].as() == '&') { + cond_met = false; + continue; + } else { + break; + } + } + } + } + + return cond_met; +} + +/* + * @brief Compares the input json values to the known devices and + * decodes the data if a match is found. + */ +int decodeBLEJson(JsonObject& jsondata) { +#ifdef UNIT_TESTING + DynamicJsonDocument doc(TEST_MAX_DOC); +#else + DynamicJsonDocument doc(m_docMax); +#endif + const char* svc_data = jsondata[SVC_DATA].as(); + const char* mfg_data = jsondata[MFG_DATA].as(); + const char* dev_name = jsondata["name"].as(); + const char* svc_uuid = jsondata["servicedatauuid"].as(); + const char* mac_id = jsondata["id"].as(); + int success = -1; + + // if there is no data to decode just return + if (svc_data == nullptr && mfg_data == nullptr && dev_name == nullptr) { + DEBUG_PRINT("Invalid data\n"); + return success; + } + + /* loop through the devices and attempt to match the input data to a device parameter set */ + for (auto i_main = 0; i_main < sizeof(_devices) / sizeof(_devices[0]); ++i_main) { + DeserializationError error = deserializeJson(doc, _devices[i_main][0]); + if (error) { + DEBUG_PRINT("deserializeJson() failed: %s\n", error.c_str()); +#ifdef UNIT_TESTING + assert(0); +#endif + return success; + } +#ifdef UNIT_TESTING + if (doc.memoryUsage() > peakDocSize) + peakDocSize = doc.memoryUsage(); +#endif + + /* found a match, extract the data */ + JsonArray selectedCondition; +#ifdef NO_MAC_ADDR + if (doc.containsKey("conditionnomac")) { + selectedCondition = doc["conditionnomac"]; + } else { + selectedCondition = doc["condition"]; + } +#else + selectedCondition = doc["condition"]; +#endif + if (checkDeviceMatch(selectedCondition, svc_data, mfg_data, dev_name, svc_uuid, mac_id)) { + jsondata["brand"] = doc["brand"]; + jsondata["model"] = doc["model"]; + jsondata["model_id"] = doc["model_id"]; + if (doc.containsKey("tag")) { + doc.add("type"); + doc["type"] = NULL; + + std::string tagstring = doc["tag"]; + int type = strtol(tagstring.substr(0, 2).c_str(), NULL, 16); + + switch (type) { + case 1: + doc["type"] = "THB"; // Termperature, Humidity, Battery + break; + case 2: + doc["type"] = "THBX"; // Termperature, Humidity, Battery, Extra + break; + case 3: + doc["type"] = "BBQ"; // Multip probe temperatures only + break; + case 4: + doc["type"] = "CTMO"; // Contact and/or Motion sensor + break; + case 5: + doc["type"] = "SCALE"; // weight scale + break; + case 6: + doc["type"] = "BCON"; // iBeacon protocol + break; + case 7: + doc["type"] = "ACEL"; // acceleration + break; + case 8: + doc["type"] = "BATT"; // battery + break; + case 9: + doc["type"] = "PLANT"; // plant sensors + break; + case 10: + doc["type"] = "TIRE"; // tire pressure monitoring system + break; + case 11: + doc["type"] = "BODY"; // health monitoring devices + break; + case 12: + doc["type"] = "ENRG"; // energy monitoring devices + break; + case 13: + doc["type"] = "WCVR"; // window covering + break; + case 14: + doc["type"] = "ACTR"; // ON/OFF actuators + break; + case 15: + doc["type"] = "AIR"; // air environmental monitoring devices + break; + case 16: + doc["type"] = "TRACK"; // Bluetooth tracker + break; + case 17: + doc["type"] = "BTN"; // Button + break; + case 254: + doc["type"] = "RMAC"; // random MAC address devices + break; + case 255: + doc["type"] = "UNIQ"; // unique devices + break; + } + + if (!doc["type"].isNull()) { + jsondata["type"] = doc["type"]; + } else { + DEBUG_PRINT("ERROR - no valid device type present in model tag property\n"); + } + + // Octet Byte[1] bits[7-0] - True/False tags + if (tagstring.length() >= 4) { + // bits[3-0] + uint8_t data = getBinaryData(tagstring[3]); + + if (((data >> 0) & 0x01) == 1) { // CIDC - NOT Company ID Compliant + doc.add("cidc"); + doc["cidc"] = false; + jsondata["cidc"] = doc["cidc"]; + } + + if (((data >> 1) & 0x01) == 1) { // Active Scanning required + doc.add("acts"); + doc["acts"] = true; + jsondata["acts"] = doc["acts"]; + } + + if (((data >> 2) & 0x01) == 1) { // Continuous Scanning required + doc.add("cont"); + doc["cont"] = true; + jsondata["cont"] = doc["cont"]; + } + + if (((data >> 3) & 0x01) == 1) { // Presence tracking conpatible + doc.add("track"); + doc["track"] = true; + jsondata["track"] = doc["track"]; + } + } + + // Octet Byte[2] - Encryption Model + if (tagstring.length() >= 6) { + int encrmode = strtol(tagstring.substr(4, 2).c_str(), NULL, 16); + DEBUG_PRINT("encrmode: %d\n", encrmode); + if (encrmode > 0) { + doc.add("encr"); + doc["encr"] = encrmode; + jsondata["encr"] = doc["encr"]; + } + } + } + + JsonObject properties = doc["properties"]; + + /* Loop through all the devices properties and extract the values */ + for (JsonPair kv : properties) { + JsonObject prop = kv.value().as(); + + if (checkPropCondition(prop["condition"], svc_data, mfg_data)) { + JsonArray decoder = prop["decoder"]; + if (strstr((const char*)decoder[0], "value_from_hex_data") != nullptr) { + const char* src = svc_data; + if (strstr((const char*)decoder[1], MFG_DATA)) { + src = mfg_data; + } + + /* use a double for all values and cast later if required */ + double temp_val; + static double cal_val = 0; + + if (data_index_is_valid(src, decoder[2].as(), decoder[3].as())) { + decoder_function dec_fun = &TheengsDecoder::value_from_hex_string; + + if (strstr((const char*)decoder[0], "bf") != nullptr) { + dec_fun = &TheengsDecoder::bf_value_from_hex_string; + } + + temp_val = (this->*dec_fun)(src, decoder[2].as(), + decoder[3].as(), + decoder[4].as(), + decoder[5].isNull() ? true : decoder[5].as(), + decoder[6].isNull() ? false : decoder[6].as()); + + } else { + break; + } + + /* Do any required post processing of the value */ + if (prop.containsKey("post_proc")) { + JsonArray post_proc = prop["post_proc"]; + for (unsigned int i = 0; i < post_proc.size(); i += 2) { + if (cal_val && post_proc[i + 1].as() != NULL && + strncmp(post_proc[i + 1].as(), ".cal", 4) == 0) { + switch (*post_proc[i].as()) { + case '/': + temp_val /= cal_val; + break; + case '*': + temp_val *= cal_val; + break; + case '-': + temp_val -= cal_val; + break; + case '+': + temp_val += cal_val; + break; + } + } else { + if (strlen(post_proc[i].as()) == 1) { + switch (*post_proc[i].as()) { + case '/': + temp_val /= post_proc[i + 1].as(); + break; + case '*': + temp_val *= post_proc[i + 1].as(); + break; + case '-': + temp_val -= post_proc[i + 1].as(); + break; + case '+': + temp_val += post_proc[i + 1].as(); + break; + case '%': { + long val = (long)temp_val; + temp_val = val % post_proc[i + 1].as(); + break; + } + case '<': { + long val = (long)temp_val; + temp_val = val << post_proc[i + 1].as(); + break; + } + case '>': { + long val = (long)temp_val; + temp_val = val >> post_proc[i + 1].as(); + break; + } + case '!': { + bool val = (bool)temp_val; + temp_val = !val; + break; + } + case '&': { + long long val = (long long)temp_val; + temp_val = val & post_proc[i + 1].as(); + break; + } + } + } else if (strncmp(post_proc[i].as(), "max", 3) == 0) { + if (temp_val > post_proc[i + 1].as()) { + temp_val = post_proc[i + 1].as(); + } + } else if (strncmp(post_proc[i].as(), "min", 3) == 0) { + if (temp_val < post_proc[i + 1].as()) { + temp_val = post_proc[i + 1].as(); + } + } + } + } + } + + /* If there is any underscores at the beginning of the property name, there is multiple + * properties of this type, we need remove the underscores for creating the key. + */ + std::string _key = sanitizeJsonKey(kv.key().c_str()); + + /* calculation values extracted from data are not added to the decoded output + * instead we store them temporarily to use with the next data properties. + */ + if (_key == ".cal") { + cal_val = temp_val; + continue; + } + + /* Cast to a different value type if specified */ + if (prop.containsKey("is_bool")) { + jsondata[_key] = (bool)temp_val; + } else { + jsondata[_key] = temp_val; + } + + /* If the property is temp in C, make sure to convert and add temp in F */ + if (_key.find("tempc", 0, 5) != std::string::npos) { + double tc = jsondata[_key]; + _key[4] = 'f'; + jsondata[_key] = tc * 1.8 + 32; + _key[4] = 'c'; + } + + /* If the property is with suffix _cm, make sure to convert and add length in inches */ + if (_key.find("_cm", _key.length() - 3, 3) != std::string::npos) { + double tc = jsondata[_key]; + _key.replace(_key.length() - 3, 3, "_in"); + jsondata[_key] = tc / 2.54; + _key.replace(_key.length() - 3, 3, "_cm"); + } + + success = i_main; + DEBUG_PRINT("found value = %s : %.2f\n", _key.c_str(), jsondata[_key].as()); + } else if (strstr((const char*)decoder[0], "static_value") != nullptr) { + if (strstr((const char*)decoder[0], "bit") != nullptr) { + JsonArray staticbitdecoder = prop["decoder"]; + const char* data_src = nullptr; + + if (svc_data && strstr((const char*)staticbitdecoder[1], SVC_DATA) != nullptr) { + data_src = svc_data; + } else if (mfg_data && strstr((const char*)staticbitdecoder[1], MFG_DATA) != nullptr) { + data_src = mfg_data; + } + + char ch = *(data_src + staticbitdecoder[2].as()); + uint8_t data = getBinaryData(ch); + uint8_t shift = staticbitdecoder[3].as(); + int x = 4 + ((data >> shift) & 0x01); + + jsondata[sanitizeJsonKey(kv.key().c_str())] = staticbitdecoder[x]; + success = i_main; + } else { + jsondata[sanitizeJsonKey(kv.key().c_str())] = decoder[1]; + success = i_main; + } + } else if (strstr((const char*)decoder[0], "string_from_hex_data") != nullptr) { + const char* src = svc_data; + if (strstr((const char*)decoder[1], MFG_DATA)) { + src = mfg_data; + } + + std::string value(src + decoder[2].as(), decoder[3].as()); + + /* Lookup table */ + if (prop.containsKey("lookup")) { + JsonArray lookup = prop["lookup"]; + for (unsigned int i = 0; i < lookup.size(); i += 2) { + if (lookup[i].as() == value) { + value = lookup[i + 1].as(); + jsondata[sanitizeJsonKey(kv.key().c_str())] = value; + success = i_main; + break; + } + } + } else { + jsondata[sanitizeJsonKey(kv.key().c_str())] = value; + success = i_main; + } + } else if (strstr((const char*)decoder[0], "mac_from_hex_data") != nullptr) { + const char* src = svc_data; + if (strstr((const char*)decoder[1], MFG_DATA)) { + src = mfg_data; + } + + std::string value(src + decoder[2].as(), 12); + + // reverse MAC + if (strstr((const char*)decoder[0], "revmac_from_hex_data") != nullptr) { + const char* mac_string = nullptr; + mac_string = value.c_str(); + char* reverse_mac_string = (char*)malloc(strlen(mac_string) + 1); + reverse_hex_data(mac_string, reverse_mac_string, 12); + value = reverse_mac_string; + free(reverse_mac_string); + } + + // upper case MAC + for (int x = 0; x <= 12; x++) { + value[x] = toupper(value[x]); + } + + // add colons + for (int x = 2; x <= 14; x += 3) { + value.insert(x, 1, ':'); + } + + jsondata[sanitizeJsonKey(kv.key().c_str())] = value; + success = i_main; + } + } + } + return success; + } + } + return success; +} + +int getTheengModel(JsonDocument& doc, const char* model_id) { + int mid_len = strlen(model_id); + + for (auto i = 0; i < sizeof(_devices) / sizeof(_devices[0]); ++i) { + DeserializationError error = deserializeJson(doc, _devices[i][0]); + if (error) { + DEBUG_PRINT("deserializeJson() failed: %s\n", error.c_str()); +#ifdef UNIT_TESTING + assert(0); +#endif + break; + } +#ifdef UNIT_TESTING + if (doc.memoryUsage() > peakDocSize) + peakDocSize = doc.memoryUsage(); +#endif + + if (doc.containsKey("model_id")) { + if (strlen(doc["model_id"].as()) != mid_len) { + continue; + } + if (!strncmp(model_id, doc["model_id"], mid_len)) { + return i; + } + } + } + + return -1; +} + +std::string getTheengProperties(int mod_index) { + return (mod_index < 0 || mod_index >= BLE_ID_NUM::BLE_ID_MAX) ? "" : _devices[mod_index][1]; +} + +std::string getTheengProperties(const char* model_id) { +#ifdef UNIT_TESTING + DynamicJsonDocument doc(TEST_MAX_DOC); +#else + DynamicJsonDocument doc(m_docMax); +#endif + int mod_index = getTheengModel(doc, model_id); + return (mod_index < 0 || mod_index >= BLE_ID_NUM::BLE_ID_MAX) ? "" : _devices[mod_index][1]; +} + +std::string getTheengAttribute(int model_id, const char* attribute) { +#ifdef UNIT_TESTING + DynamicJsonDocument doc(TEST_MAX_DOC); +#else + DynamicJsonDocument doc(m_docMax); +#endif + std::string ret_attr = ""; + if (model_id >= 0 && model_id < BLE_ID_NUM::BLE_ID_MAX) { + DeserializationError error = deserializeJson(doc, _devices[model_id][0]); + if (error) { + DEBUG_PRINT("deserializeJson() failed: %s\n", error.c_str()); +#ifdef UNIT_TESTING + assert(0); +#endif + } else if (!doc[attribute].isNull()) { + ret_attr = doc[attribute].as(); + } + } + + return ret_attr; +} + +std::string getTheengAttribute(const char* model_id, const char* attribute) { +#ifdef UNIT_TESTING + DynamicJsonDocument doc(TEST_MAX_DOC); +#else + DynamicJsonDocument doc(m_docMax); +#endif + int mod_index = getTheengModel(doc, model_id); + + if (mod_index >= 0 && !doc[attribute].isNull()) { + return std::string(doc[attribute].as()); + } + return ""; +} + +void setMinServiceDataLen(size_t len) { + m_minSvcDataLen = len; +} + +void setMinManufacturerDataLen(size_t len) { + m_minMfgDataLen = len; +} + +#ifdef UNIT_TESTING +int testDocMax() { + if (peakDocSize > m_docMax) { + DEBUG_PRINT("Error: peak doc size > max; peak: %lu, max: %lu\n", peakDocSize, m_docMax); + } + return m_docMax - peakDocSize; +} +#endif + + +}; + +#endif \ No newline at end of file diff --git a/lib/decoder/src/devices.h b/lib/decoder/src/devices.h new file mode 100644 index 00000000..dc2824bb --- /dev/null +++ b/lib/decoder/src/devices.h @@ -0,0 +1,245 @@ +/* + TheengsDecoder - Decode things and devices + + Copyright: (c)Florian ROBERT + + This file is part of TheengsDecoder. + + TheengsDecoder is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TheengsDecoder is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _DEVICE_H_ +#define _DEVICE_H_ + +//ТАК странно сделано, что бы не менять порядок файлов и было проще отследить изменения в оригинальной библиотеке +//#pragma once + +#if defined(PART1_XIAOMI) +#include "devices/CGD1_json.h" +#include "devices/CGDK2_json.h" +#include "devices/CGG1_json.h" +#include "devices/CGDN1_json.h" +#include "devices/CGH1_json.h" +#include "devices/CGP1W_json.h" +#include "devices/CGPR1_json.h" +#endif +#if defined(PART2_OTHER) +#include "devices/GAEN_json.h" +#include "devices/H5055_json.h" +#include "devices/H5072_json.h" +#include "devices/H5074_json.h" +#include "devices/H5102_json.h" +#include "devices/H5106_json.h" +#include "devices/H5179_json.h" +#endif +#if defined(PART1_XIAOMI) +#include "devices/HHCCJCY10_json.h" +#include "devices/HHCCJCY01HHCC_json.h" +#include "devices/HHCCPOT002_json.h" +#include "devices/IBS_THBP01B_json.h" +#include "devices/IBT_2X_json.h" +#include "devices/IBT_4XS_json.h" +#include "devices/IBT_6XS_SOLIS6_json.h" +#include "devices/JQJCY01YM_json.h" +#include "devices/LYWSD02_json.h" +#include "devices/LYWSD03MMC_json.h" +#include "devices/LYWSD03MMC_ENCR_json.h" +#include "devices/LYWSDCGQ_json.h" +#endif +#if defined(PART2_OTHER) +#include "devices/MBXPRO_json.h" +#include "devices/MS_CDP_json.h" +#endif +#if defined(PART1_XIAOMI) +#include "devices/MUE4094RT_json.h" +#include "devices/Miband_json.h" +#include "devices/XMTZC04HMKG_json.h" +#include "devices/XMTZC04HMLB_json.h" +#include "devices/XMTZC05HMKG_json.h" +#include "devices/XMTZC05HMLB_json.h" +#endif +#if defined(PART2_OTHER) +#include "devices/Mokobeacon_json.h" +#include "devices/RDL52832_json.h" +#include "devices/RuuviTag_RAWv1_json.h" +#include "devices/RuuviTag_RAWv2_json.h" +#include "devices/SBCS_json.h" +#include "devices/SBCU_json.h" +#include "devices/SBMS_json.h" +#include "devices/SBMT_json.h" +#include "devices/SBOT_json.h" +#include "devices/SBS1_json.h" +#include "devices/SHT4X_json.h" +#include "devices/SCD4X_json.h" +#include "devices/Skale_json.h" +#include "devices/SmartDry_json.h" +#include "devices/TPMS_json.h" +#include "devices/KKM_K6P_json.h" +#include "devices/KKM_K9_json.h" +#include "devices/ThermoBeacon_json.h" +#include "devices/ABN03_json.h" +#include "devices/ABN07_json.h" +#include "devices/ABTemp_json.h" +#include "devices/Amphiro_json.h" +#include "devices/OralB_json.h" +#include "devices/PH10_json.h" +#include "devices/TPTH_json.h" +#include "devices/Mopeka_json.h" +#include "devices/T201_json.h" +#include "devices/T301_json.h" +#include "devices/tracker_json.h" +#include "devices/iNodeEM_json.h" +#include "devices/BC08_json.h" +#include "devices/BM1IN1_json.h" +#include "devices/BM3IN1_json.h" +#include "devices/BM4IN1_json.h" +#include "devices/BPARASITE_json.h" +#include "devices/BWBSDOO_json.h" +#include "devices/BM2_json.h" +#include "devices/BM6_json.h" +#include "devices/JHT_F525_json.h" +#include "devices/iBeacon_json.h" +#endif +#if defined(PART1_XIAOMI) +#include "devices/APPLE_json.h" +#include "devices/ServiceData_json.h" +#endif +#if defined(PART2_OTHER) +#include "devices/SBBT_002C_json.h" +#include "devices/SBBT_002C_ENCR_json.h" +#include "devices/SBDW_002C_json.h" +#include "devices/SBDW_002C_ENCR_json.h" +#include "devices/SBMO_003Z_json.h" +#include "devices/SBMO_003Z_ENCR_json.h" +#endif + +const char* _devices[][2] = { +#if defined(PART1_XIAOMI) + {_HHCCJCY01HHCC_json, _HHCCJCY01HHCC_json_props}, + {_LYWSD02_json, _LYWSD02_json_props}, + {_LYWSDCGQ_json, _LYWSDCGQ_json_props}, + {_CGP1W_json, _CGP1W_json_props}, + {_CGG1_json_STOCK, _CGG1_json_props}, + {_CGG1_json_ATC1441, _CGG1_json_props}, + {_CGG1_json_PVVX, _CGG1_json_props}, + {_CGG1_json_STOCK_2, _CGG1_json_props}, + {_CGDN1_json, _CGDN1_json_props}, + {_CGD1_json, _CGD1_json_props}, + {_CGDK2_json_STOCK, _CGDK2_json_props}, + {_CGDK2_json_PVVX, _CGDK2_json_props}, + {_CGDK2_json_ATC1441, _CGDK2_json_props}, + {_CGH1_json, _CGH1_json_props}, + {_JQJCY01YM_json, _JQJCY01YM_json_props}, + {_IBS_THBP01B_json, _IBS_THBP01B_json_props}, + {_IBT_2X_json_2X, _IBT_2X_json_props}, + {_IBT_2X_json_2XS, _IBT_2X_json_props}, + {_IBT_4XS_json, _IBT_4XS_json_props}, + {_IBT_6XS_SOLIS6_json, _IBT_6XS_SOLIS6_json_props}, + {_Miband_json, _Miband_json_props}, + {_XMTZC04HMKG_json, _XMTZC04HMKG_json_props}, + {_XMTZC04HMLB_json, _XMTZC04HMLB_json_props}, + {_XMTZC05HMKG_json, _XMTZC05HMKG_json_props}, + {_XMTZC05HMLB_json, _XMTZC05HMLB_json_props}, +#endif +#if defined(PART2_OTHER) + {_TPMS_json, _TPMS_json_props}, + {_KKM_K6P_json, _KKM_K6P_json_props}, + {_KKM_K9_json, _KKM_K9_json_props}, +#endif +#if defined(PART1_XIAOMI) + {_LYWSD03MMC_json_ATC, _LYWSD03MMC_json_props}, + {_LYWSD03MMC_json_PVVX, _LYWSD03MMC_json_props}, + {_LYWSD03MMC_json_PVVX_DECR, _LYWSD03MMC_json_props}, + {_LYWSD03MMC_ENCR_json_PVVX, _LYWSD03MMC_ENCR_json_props}, + {_CGPR1_json, _CGPR1_json_props}, +#endif +#if defined(PART2_OTHER) + {_ThermoBeacon_json, _ThermoBeacon_json_props}, + {_H5055_json, _H5055_json_props}, + {_H5072_json, _H5072_json_props}, + {_H5074_json, _H5074_json_props}, + {_H5102_json, _H5102_json_props}, + {_H5106_json, _H5106_json_props}, + {_H5179_json, _H5179_json_props}, +#endif +#if defined(PART1_XIAOMI) + {_HHCCJCY10_json, _HHCCJCY10_json_props}, + {_MUE4094RT_json, _MUE4094RT_json_props}, +#endif +#if defined(PART2_OTHER) + {_Mokobeacon_json, _Mokobeacon_json_props}, + {_MBXPRO_json, _MBXPRO_json_props}, + {_iNodeEM_json, _iNodeEM_json_props}, + {_RuuviTag_RAWv1_json, _RuuviTag_RAWv1_json_props}, + {_RuuviTag_RAWv2_json, _RuuviTag_RAWv2_json_props}, + {_SBCS_json, _SBCS_json_props}, + {_SBCU_json, _SBCU_json_props}, + {_SBMS_json, _SBMS_json_props}, + {_SBMT_json, _SBMT_json_props}, + {_SBOT_json, _SBOT_json_props}, + {_SBS1_json, _SBS1_json_props}, + {_SHT4X_json, _SHT4X_json_props}, + {_SCD4X_json, _SCD4X_json_props}, + {_Skale_json, _Skale_json_props}, + {_SmartDry_json, _SmartDry_json_props}, + {_BC08_json, _BC08_json_props}, + {_BM1IN1_json, _BM1IN1_json_props}, + {_BM3IN1_json, _BM3IN1_json_props}, + {_BM4IN1_json, _BM4IN1_json_props}, + {_MS_CDP_json, _MS_CDP_json_props}, + {_GAEN_json, _GAEN_json_props}, +#endif +#if defined(PART1_XIAOMI) + {_HHCCPOT002_json, _HHCCPOT002_json_props}, +#endif +#if defined(PART2_OTHER) + {_BPARASITE_json, _BPARASITE_json_props}, + {_BWBSDOO_json, _BWBSDOO_json_props}, + {_BM2_json, _BM2_json_props}, + {_BM6_json, _BM6_json_props}, + {_RDL52832_json, _RDL52832_json_props}, + {_ABN03_json, _ABN03_json_props}, + {_ABN07_json, _ABN07_json_props}, + {_ABTemp_json, _ABTemp_json_props}, + {_AMPHIRO_json, _AMPHIRO_json_props}, + {_OralB_json, _OralB_json_props}, + {_PH10_json, _PH10_json_props}, + {_TPTH_json, _TPTH_json_props}, + {_Mopeka_json, _Mopeka_json_props}, + {_T201_json, _T201_json_props}, + {_T301_json, _T301_json_props}, + {_tracker_json_nut, _tracker_json_props}, + {_tracker_json_itag, _tracker_json_props}, + {_tracker_json_tagit, _tracker_json_props}, + {_tracker_json_tile, _tracker_json_props}, + {_tracker_json_tilename, _tracker_json_props}, + {_JHT_F525_json, _JHT_F525_json_props}, + {_ibeacon_json, _ibeacon_json_props}, +#endif +#if defined(PART1_XIAOMI) + {_APPLE_json, _APPLE_json_props}, + {_APPLE_json_at, _APPLE_json_props}, + {_ServiceData_json, _ServiceData_json_props}, +#endif +#if defined(PART2_OTHER) + {_SBBT_002C_json, _SBBT_002C_json_props}, + {_SBBT_002C_ENCR_json, _SBBT_002C_ENCR_json_props}, + {_SBDW_002C_json, _SBDW_002C_json_props}, + {_SBDW_002C_ENCR_json, _SBDW_002C_ENCR_json_props}, + {_SBMO_003Z_json, _SBMO_003Z_json_props}, + {_SBMO_003Z_ENCR_json, _SBMO_003Z_ENCR_json_props}, +#endif +}; + +#endif \ No newline at end of file diff --git a/lib/decoder/src/devices/ABN03_json.h b/lib/decoder/src/devices/ABN03_json.h new file mode 100644 index 00000000..69f7fb99 --- /dev/null +++ b/lib/decoder/src/devices/ABN03_json.h @@ -0,0 +1,55 @@ +const char* _ABN03_json = "{\"brand\":\"April Brother\",\"model\":\"N03\",\"model_id\":\"ABN03\",\"tag\":\"0208\",\"condition\":[\"servicedata\",\"=\",30,\"index\",0,\"ab03\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,true,true],\"post_proc\":[\"/\",8]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,true,false],\"post_proc\":[\"/\",2]},\"lux\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",26,4,true,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false,false]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"servicedata\",4]}}}"; +/* R""""( +{ + "brand":"April Brother", + "model":"N03", + "model_id":"ABN03", + "tag":"0208", + "condition":["servicedata", "=", 30, "index", 0, "ab03"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 18, 4, true, true], + "post_proc":["/", 8] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 22, 4, true, false], + "post_proc":["/", 2] + }, + "lux":{ + "decoder":["value_from_hex_data", "servicedata", 26, 4, true, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false, false] + }, + "mac":{ + "decoder":["mac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +const char* _ABN03_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"lux\":{\"unit\":\"lx\",\"name\":\"illuminance\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "lux":{ + "unit":"lx", + "name":"illuminance" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/ABN07_json.h b/lib/decoder/src/devices/ABN07_json.h new file mode 100644 index 00000000..0c284ed9 --- /dev/null +++ b/lib/decoder/src/devices/ABN07_json.h @@ -0,0 +1,52 @@ +const char* _ABN07_json = "{\"brand\":\"April Brother\",\"model\":\"N07\",\"model_id\":\"ABN07\",\"tag\":\"010a\",\"condition\":[\"servicedata\",\"=\",22,\"index\",0,\"40\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"asensor_\"],\"properties\":{\"packet\":{\"condition\":[\"servicedata\",2,\"00\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false]},\"batt\":{\"condition\":[\"servicedata\",6,\"01\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,false,false]},\"tempc\":{\"condition\":[\"servicedata\",10,\"02\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,true,true],\"post_proc\":[\"/\",100]},\"hum\":{\"condition\":[\"servicedata\",16,\"03\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,true,false],\"post_proc\":[\"/\",100]}}}"; +/* R""""( +{ + "brand":"April Brother", + "model":"N07", + "model_id":"ABN07", + "tag":"010a", + "condition":["servicedata", "=", 22, "index", 0, "40", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "asensor_"], + "properties":{ + "packet":{ + "condition":["servicedata", 2, "00"], + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false] + }, + "batt":{ + "condition":["servicedata", 6, "01"], + "decoder":["value_from_hex_data", "servicedata", 8, 2, false, false] + }, + "tempc":{ + "condition":["servicedata", 10, "02"], + "decoder":["value_from_hex_data", "servicedata", 12, 4, true, true], + "post_proc":["/", 100] + }, + "hum":{ + "condition":["servicedata", 16, "03"], + "decoder":["value_from_hex_data", "servicedata", 18, 4, true, false], + "post_proc":["/", 100] + } + } +})"""";*/ + +const char* _ABN07_json_props = "{\"properties\":{\"packet\":{\"unit\":\"int\",\"name\":\"packet id\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"}}}"; +/*R""""( +{ + "properties":{ + "packet":{ + "unit":"int", + "name":"packet id" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/ABTemp_json.h b/lib/decoder/src/devices/ABTemp_json.h new file mode 100644 index 00000000..b5afbfa4 --- /dev/null +++ b/lib/decoder/src/devices/ABTemp_json.h @@ -0,0 +1,68 @@ +const char* _ABTemp_json = "{\"brand\":\"April Brother\",\"model\":\"ABTemp\",\"model_id\":\"ABTemp\",\"tag\":\"0608\",\"condition\":[\"manufacturerdata\",\"=\",50,\"index\",0,\"4c000215b5b182c7eab14988aa99b5c1517008d9\"],\"properties\":{\"mfid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",0,4]},\"uuid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",8,32]},\"major\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",40,4,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",44,2,false]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",46,2,false]},\"txpower\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",48,2,false]},\"mac\":{\"condition\":[\"servicedata\",\"=\",22],\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",0]}}}"; +/*R""""( +{ + "brand":"April Brother", + "model":"ABTemp", + "model_id":"ABTemp", + "tag":"0608", + "condition":["manufacturerdata", "=", 50, "index", 0, "4c000215b5b182c7eab14988aa99b5c1517008d9"], + "properties":{ + "mfid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 0, 4] + }, + "uuid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 8, 32] + }, + "major":{ + "decoder":["value_from_hex_data", "manufacturerdata", 40, 4, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 44, 2, false] + }, + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 46, 2, false] + }, + "txpower":{ + "decoder":["value_from_hex_data","manufacturerdata", 48, 2, false] + }, + "mac":{ + "condition":["servicedata", "=", 22], + "decoder":["revmac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +const char* _ABTemp_json_props = "{\"properties\":{\"mfid\":{\"unit\":\"hex\",\"name\":\"manufacturer id\"},\"uuid\":{\"unit\":\"hex\",\"name\":\"service uuid\"},\"major\":{\"unit\":\"hex\",\"name\":\"major value\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"txpower\":{\"unit\":\"dBm\",\"name\":\"tx power @ 1 m\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "mfid":{ + "unit":"hex", + "name":"manufacturer id" + }, + "uuid":{ + "unit":"hex", + "name":"service uuid" + }, + "major":{ + "unit":"hex", + "name":"major value" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "txpower":{ + "unit":"dBm", + "name":"tx power @ 1 m" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/APPLE_json.h b/lib/decoder/src/devices/APPLE_json.h new file mode 100644 index 00000000..59518d3d --- /dev/null +++ b/lib/decoder/src/devices/APPLE_json.h @@ -0,0 +1,40 @@ +const char* _APPLE_json = "{\"brand\":\"Apple\",\"model\":\"Apple Continuity\",\"model_id\":\"APPLE_CONT\",\"tag\":\"fe\",\"condition\":[\"manufacturerdata\",\">=\",10,\"index\",0,\"4c000\",\"|\",\"manufacturerdata\",\">=\",10,\"index\",0,\"4c001\",\"&\",\"manufacturerdata\",\"<\",50],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Apple device\"]}}}"; +/*R""""( +{ + "brand":"Apple", + "model":"Apple Continuity", + "model_id":"APPLE_CONT", + "tag":"fe", + "condition":["manufacturerdata", ">=", 10, "index", 0, "4c000", "|", "manufacturerdata", ">=", 10, "index", 0, "4c001", "&", "manufacturerdata", "<", 50], + "properties":{ + "device":{ + "decoder":["static_value", "Apple device"] + } + } +})"""";*/ + +const char* _APPLE_json_at = "{\"brand\":\"Apple\",\"model\":\"Apple Continuity\",\"model_id\":\"APPLE_CONTAT\",\"tag\":\"fe\",\"condition\":[\"manufacturerdata\",\">\",50,\"index\",0,\"4c000\",\"|\",\"manufacturerdata\",\">\",50,\"index\",0,\"4c001\"],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Apple device\"]}}}"; +/*R""""( +{ + "brand":"Apple", + "model":"Apple Continuity", + "model_id":"APPLE_CONTAT", + "tag":"fe", + "condition":["manufacturerdata", ">", 50, "index", 0, "4c000", "|", "manufacturerdata", ">", 50, "index", 0, "4c001"], + "properties":{ + "device":{ + "decoder":["static_value", "Apple device"] + } + } +})"""";*/ + +const char* _APPLE_json_props = "{\"properties\":{\"device\":{\"unit\":\"string\",\"name\":\"device\"}}}"; +/*R""""( +{ + "properties":{ + "device":{ + "unit":"string", + "name":"device" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/Amphiro_json.h b/lib/decoder/src/devices/Amphiro_json.h new file mode 100644 index 00000000..565fe8f5 --- /dev/null +++ b/lib/decoder/src/devices/Amphiro_json.h @@ -0,0 +1,55 @@ +const char* _AMPHIRO_json = "{\"brand\":\"Oras\",\"model\":\"Hydractiva Digital\",\"model_id\":\"ADHS\",\"tag\":\"0c01\",\"condition\":[\"manufacturerdata\",\"=\",42,\"index\",0,\"eefa\"],\"properties\":{\"session\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",4,6,false,false]},\"seconds\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,4,false,false]},\"litres\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,6,false,false],\"post_proc\":[\"/\",2560]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",26,2,false,false]},\"energy\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,false,false],\"post_proc\":[\"/\",100]}}}"; +/*R""""( +{ + "brand":"Oras", + "model":"Hydractiva Digital", + "model_id":"ADHS", + "tag":"0c01", + "condition":["manufacturerdata", "=", 42, "index", 0, "eefa"], + "properties":{ + "session":{ + "decoder":["value_from_hex_data", "manufacturerdata", 4, 6, false, false] + }, + "seconds":{ + "decoder":["value_from_hex_data", "manufacturerdata", 10, 4, false, false] + }, + "litres":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 6, false, false], + "post_proc":["/", 2560] + }, + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 26, 2, false, false] + }, + "energy":{ + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, false, false], + "post_proc":["/", 100] + } + } +})"""";*/ + +const char* _AMPHIRO_json_props = "{\"properties\":{\"session\":{\"unit\":\"int\",\"name\":\"session\"},\"seconds\":{\"unit\":\"s\",\"name\":\"duration\"},\"litres\":{\"unit\":\"L\",\"name\":\"volume\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"energy\":{\"unit\":\"kWh\",\"name\":\"energy\"}}}"; +/*R""""( +{ + "properties":{ + "session":{ + "unit":"int", + "name":"session" + }, + "seconds":{ + "unit":"s", + "name":"duration" + }, + "litres":{ + "unit":"L", + "name":"volume" + }, + "tempc": { + "unit": "°C", + "name": "temperature" + }, + "energy":{ + "unit":"kWh", + "name":"energy" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BC08_json.h b/lib/decoder/src/devices/BC08_json.h new file mode 100644 index 00000000..e43dab2a --- /dev/null +++ b/lib/decoder/src/devices/BC08_json.h @@ -0,0 +1,59 @@ +const char* _BC08_json = "{\"brand\":\"BlueCharm\",\"model\":\"Beacon 08/04P/021\",\"model_id\":\"KSensor\",\"tag\":\"0708\",\"condition\":[\"servicedata\",\"=\",26,\"index\",0,\"21010b\",\"&\",\"uuid\",\"index\",0,\"feaa\"],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false],\"post_proc\":[\"/\",256,\"*\",100,\">\",0,\"/\",100]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",10,2,false,true],\"post_proc\":[\"+\",\".cal\"]},\"accx\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,4,false,true]},\"accy\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,false,true]},\"accz\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,false,true]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,4,false,false],\"post_proc\":[\"/\",1000]}}}"; +/*R""""( +{ + "brand":"BlueCharm", + "model":"Beacon 08/04P/021", + "model_id":"KSensor", + "tag":"0708", + "condition":["servicedata", "=", 26, "index", 0, "21010b", "&", "uuid", "index", 0, "feaa"], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false], + "post_proc":["/", 256, "*", 100, ">", 0, "/", 100] + }, + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 10, 2, false, true], + "post_proc":["+", ".cal"] + }, + "accx":{ + "decoder":["value_from_hex_data", "servicedata", 14, 4, false, true] + }, + "accy":{ + "decoder":["value_from_hex_data", "servicedata", 18, 4, false, true] + }, + "accz":{ + "decoder":["value_from_hex_data", "servicedata", 22, 4, false, true] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 6, 4, false, false], + "post_proc":["/", 1000] + } + } +})"""";*/ + +const char* _BC08_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"},\"accz\":{\"unit\":\"m/s²\",\"name\":\"acceleration z\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + }, + "accz":{ + "unit":"m/s²", + "name":"acceleration z" + }, + "volt":{ + "unit":"V", + "name":"voltage" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BM1IN1_json.h b/lib/decoder/src/devices/BM1IN1_json.h new file mode 100644 index 00000000..de87bdef --- /dev/null +++ b/lib/decoder/src/devices/BM1IN1_json.h @@ -0,0 +1,33 @@ +const char* _BM1IN1_json = "{\"brand\":\"Blue Maestro\",\"model\":\"Tempo Disc\",\"model_id\":\"TD1in1\",\"tag\":\"0108\",\"condition\":[\"manufacturerdata\",\"index\",4,\"0d\",\"&\",\"manufacturerdata\",\"=\",24,\"index\",0,\"3301\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,false,true],\"post_proc\":[\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false]}}}"; +/*R""""( +{ + "brand":"Blue Maestro", + "model":"Tempo Disc", + "model_id":"TD1in1", + "tag":"0108", + "condition":["manufacturerdata", "index", 4, "0d", "&", "manufacturerdata", "=", 24, "index", 0, "3301"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, false, true], + "post_proc":["/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false] + } + } +})"""";*/ + +const char* _BM1IN1_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties": { + "tempc": { + "unit": "°C", + "name": "temperature" + }, + "batt": { + "unit": "%", + "name": "battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BM2_json.h b/lib/decoder/src/devices/BM2_json.h new file mode 100644 index 00000000..b715a7b4 --- /dev/null +++ b/lib/decoder/src/devices/BM2_json.h @@ -0,0 +1,33 @@ +const char* _BM2_json = "{\"brand\":\"GENERIC\",\"model\":\"BM2 Battery Monitor\",\"model_id\":\"BM2\",\"tag\":\"0808\",\"condition\":[\"manufacturerdata\",\"=\",50,\"index\",0,\"4c000215655f83caae16a10a702e31f30d58dd82\"],\"properties\":{\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",48,2,false]},\"device\":{\"decoder\":[\"static_value\",\"BM2 Tracker\"]}}}"; + +/*R""""( +{ + "brand":"GENERIC", + "model":"BM2 Battery Monitor", + "model_id":"BM2", + "tag":"0808", + "condition":["manufacturerdata", "=", 50, "index", 0, "4c000215655f83caae16a10a702e31f30d58dd82"], + "properties":{ + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 48, 2, false] + }, + "device":{ + "decoder":["static_value", "BM2 Tracker"] + } + } +})"""";*/ + +const char* _BM2_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"device\":{\"unit\":\"string\",\"name\":\"tracker device\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + }, + "device":{ + "unit":"string", + "name":"tracker device" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BM3IN1_json.h b/lib/decoder/src/devices/BM3IN1_json.h new file mode 100644 index 00000000..4d9b87b5 --- /dev/null +++ b/lib/decoder/src/devices/BM3IN1_json.h @@ -0,0 +1,49 @@ +const char* _BM3IN1_json = "{\"brand\":\"Blue Maestro\",\"model\":\"Tempo Disc\",\"model_id\":\"TD3in1\",\"tag\":\"0208\",\"condition\":[\"manufacturerdata\",\"index\",4,\"16\",\"|\",\"manufacturerdata\",\"index\",4,\"17\",\"&\",\"manufacturerdata\",\"=\",32,\"index\",0,\"3301\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,false,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,false,false],\"post_proc\":[\"/\",10]},\"tempc2_dp\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,false,true],\"post_proc\":[\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false]}}}"; +/*R""""( +{ + "brand":"Blue Maestro", + "model":"Tempo Disc", + "model_id":"TD3in1", + "tag":"0208", + "condition":["manufacturerdata", "index", 4, "16", "|", "manufacturerdata", "index", 4, "17", "&", "manufacturerdata", "=", 32, "index", 0, "3301"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, false, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, false, false], + "post_proc":["/", 10] + }, + "tempc2_dp":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, false, true], + "post_proc":["/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false] + } + } +})"""";*/ + +const char* _BM3IN1_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"tempc2_dp\":{\"unit\":\"°C\",\"name\":\"dew point\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties": { + "tempc": { + "unit": "°C", + "name": "temperature" + }, + "hum": { + "unit": "%", + "name": "humidity" + }, + "tempc2_dp": { + "unit": "°C", + "name": "dew point" + }, + "batt": { + "unit": "%", + "name": "battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BM4IN1_json.h b/lib/decoder/src/devices/BM4IN1_json.h new file mode 100644 index 00000000..09481551 --- /dev/null +++ b/lib/decoder/src/devices/BM4IN1_json.h @@ -0,0 +1,49 @@ +const char* _BM4IN1_json = "{\"brand\":\"Blue Maestro\",\"model\":\"Tempo Disc\",\"model_id\":\"TD4in1\",\"tag\":\"0208\",\"condition\":[\"manufacturerdata\",\"index\",4,\"1b\",\"&\",\"manufacturerdata\",\"=\",32,\"index\",0,\"3301\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,false,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,false,false],\"post_proc\":[\"/\",10]},\"pres\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,false,false],\"post_proc\":[\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false]}}}"; +/*R""""( +{ + "brand":"Blue Maestro", + "model":"Tempo Disc", + "model_id":"TD4in1", + "tag":"0208", + "condition":["manufacturerdata", "index", 4, "1b", "&", "manufacturerdata", "=", 32, "index", 0, "3301"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, false, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, false, false], + "post_proc":["/", 10] + }, + "pres":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, false, false], + "post_proc":["/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false] + } + } +})"""";*/ + +const char* _BM4IN1_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"pres\":{\"unit\":\"hPa\",\"name\":\"pressure\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties": { + "tempc": { + "unit": "°C", + "name": "temperature" + }, + "hum": { + "unit": "%", + "name": "humidity" + }, + "pres":{ + "unit":"hPa", + "name":"pressure" + }, + "batt": { + "unit": "%", + "name": "battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BM6_json.h b/lib/decoder/src/devices/BM6_json.h new file mode 100644 index 00000000..d5f558a2 --- /dev/null +++ b/lib/decoder/src/devices/BM6_json.h @@ -0,0 +1,32 @@ +const char* _BM6_json = "{\"brand\":\"GENERIC\",\"model\":\"BM6 Battery Monitor\",\"model_id\":\"BM6\",\"tag\":\"0808\",\"condition\":[\"manufacturerdata\",\"=\",50,\"index\",0,\"4c0002153ba29cd9a42c894856badaf2606ef777\"],\"properties\":{\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",42,2,false]},\"device\":{\"decoder\":[\"static_value\",\"BM6 Tracker\"]}}}"; +/*R""""( +{ + "brand":"GENERIC", + "model":"BM6 Battery Monitor", + "model_id":"BM6", + "tag":"0808", + "condition":["manufacturerdata", "=", 50, "index", 0, "4c0002153ba29cd9a42c894856badaf2606ef777"], + "properties":{ + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 42, 2, false] + }, + "device":{ + "decoder":["static_value", "BM6 Tracker"] + } + } +})"""";*/ + +const char* _BM6_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"device\":{\"unit\":\"string\",\"name\":\"tracker device\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + }, + "device":{ + "unit":"string", + "name":"tracker device" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BPARASITE_json.h b/lib/decoder/src/devices/BPARASITE_json.h new file mode 100644 index 00000000..db7b0574 --- /dev/null +++ b/lib/decoder/src/devices/BPARASITE_json.h @@ -0,0 +1,72 @@ +const char* _BPARASITE_json = "{\"brand\":\"rbaron\",\"model\":\"b-parasite\",\"model_id\":\"BPv1.0-1.2\",\"tag\":\"0904\",\"condition\":[\"servicedata\",\">=\",32,\"index\",0,\"1\",\"|\",\"servicedata\",\">=\",32,\"index\",0,\"2\",\"&\",\"uuid\",\"index\",0,\"181a\"],\"properties\":{\"tempc\":{\"condition\":[\"servicedata\",0,\"1\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,4,false,true],\"post_proc\":[\"/\",1000]},\"_tempc\":{\"condition\":[\"servicedata\",0,\"2\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,4,false,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,false,false],\"post_proc\":[\"/\",655.35]},\"moi\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,4,false,false],\"post_proc\":[\"/\",655.35]},\"lux\":{\"condition\":[\"servicedata\",1,\"bit\",0,1],\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,false,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,4,false,false],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"servicedata\",20]}}}"; + +/* R""""( +{ + "brand":"rbaron", + "model":"b-parasite", + "model_id":"BPv1.0-1.2", + "tag":"0904", + "condition":["servicedata", ">=", 32, "index", 0, "1", "|", "servicedata", ">=", 32, "index", 0, "2", "&", "uuid", "index", 0, "181a"], + "properties":{ + "tempc":{ + "condition":["servicedata", 0, "1"], + "decoder":["value_from_hex_data", "servicedata", 8, 4, false, true], + "post_proc":["/", 1000] + }, + "_tempc":{ + "condition":["servicedata", 0, "2"], + "decoder":["value_from_hex_data", "servicedata", 8, 4, false, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, false, false], + "post_proc":["/", 655.35] + }, + "moi":{ + "decoder":["value_from_hex_data", "servicedata", 16, 4, false, false], + "post_proc":["/", 655.35] + }, + "lux":{ + "condition":["servicedata", 1, "bit", 0, 1], + "decoder":["value_from_hex_data", "servicedata", 32, 4, false, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 4, false, false], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["mac_from_hex_data", "servicedata", 20] + } + } +})"""";*/ + +const char* _BPARASITE_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"moi\":{\"unit\":\"%\",\"name\":\"moisture\"},\"lux\":{\"unit\":\"lx\",\"name\":\"illuminance\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "moi":{ + "unit":"%", + "name":"moisture" + }, + "lux":{ + "unit":"lx", + "name":"illuminance" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/BWBSDOO_json.h b/lib/decoder/src/devices/BWBSDOO_json.h new file mode 100644 index 00000000..05b3ef77 --- /dev/null +++ b/lib/decoder/src/devices/BWBSDOO_json.h @@ -0,0 +1,32 @@ +const char* _BWBSDOO_json = "{\"brand\":\"Otio/BeeWi\",\"model\":\"Door & Window Sensor\",\"model_id\":\"BSDOO\",\"tag\":\"0405\",\"condition\":[\"manufacturerdata\",\"=\",14,\"index\",4,\"080c\"],\"properties\":{\"open\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",9,0,false,true]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,2,false,false]}}}"; +/*R""""( +{ + "brand":"Otio/BeeWi", + "model":"Door & Window Sensor", + "model_id":"BSDOO", + "tag":"0405", + "condition":["manufacturerdata", "=", 14, "index", 4, "080c"], + "properties":{ + "open":{ + "decoder":["bit_static_value", "manufacturerdata", 9, 0, false, true] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 2, false, false] + } + } +})"""";*/ + +const char* _BWBSDOO_json_props = "{\"properties\":{\"open\":{\"unit\":\"status\",\"name\":\"door\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "open":{ + "unit":"status", + "name":"door" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/CGD1_json.h b/lib/decoder/src/devices/CGD1_json.h new file mode 100644 index 00000000..7ed72549 --- /dev/null +++ b/lib/decoder/src/devices/CGD1_json.h @@ -0,0 +1,31 @@ +#include "common_props.h" + +const char* _CGD1_json = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"Alarm Clock\",\"model_id\":\"CGC1/CGD1\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",34,\"index\",2,\"0c\",\"|\",\"servicedata\",\"=\",34,\"index\",2,\"1e\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,2,false,false],\"post_proc\":[\"&\",127]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; + +/*R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"Alarm Clock", + "model_id":"CGC1/CGD1", + "tag":"01", + "condition":["servicedata", "=", 34, "index", 2, "0c", "|", "servicedata", "=", 34, "index", 2, "1e", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 32, 2, false, false], + "post_proc":["&", 127] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +const char* _CGD1_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/CGDK2_json.h b/lib/decoder/src/devices/CGDK2_json.h new file mode 100644 index 00000000..9105a1a0 --- /dev/null +++ b/lib/decoder/src/devices/CGDK2_json.h @@ -0,0 +1,87 @@ +#include "common_props.h" + +const char* _CGDK2_json_STOCK = "{\"brand\":\"Qingping\",\"model\":\"TH Lite\",\"model_id\":\"CGDK2\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",34,\"index\",2,\"10\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; +/*R""""( +{ + "brand":"Qingping", + "model":"TH Lite", + "model_id":"CGDK2", + "tag":"01", + "condition":["servicedata", "=", 34, "index", 2, "10", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +// ATC1441 +const char* _CGDK2_json_ATC1441 = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"TH Lite\",\"model_id\":\"CGDK2_ATC1441\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",26,\"&\",\"uuid\",\"index\",0,\"181a\",\"&\",\"name\",\"index\",0,\"CGDK\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,false],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,2,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,false],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"TH Lite", + "model_id":"CGDK2_ATC1441", + "tag":"0102", + "condition":["servicedata", "=", 26, "&", "uuid", "index", 0, "181a", "&", "name", "index", 0, "CGDK"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, false], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 18, 2, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, false], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["mac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +// PVVX +const char* _CGDK2_json_PVVX = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"TH Lite\",\"model_id\":\"CGDK2_PVVX\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",30,\"&\",\"uuid\",\"index\",0,\"181a\",\"&\",\"name\",\"index\",0,\"CGDK\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,4,true],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,2,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"TH Lite", + "model_id":"CGDK2_PVVX", + "tag":"0102", + "condition":["servicedata", "=", 30, "&", "uuid", "index", 0, "181a", "&", "name", "index", 0, "CGDK"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 4, true], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 24, 2, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +const char* _CGDK2_json_props = _common_BVTH_props; diff --git a/lib/decoder/src/devices/CGDN1_json.h b/lib/decoder/src/devices/CGDN1_json.h new file mode 100644 index 00000000..aaa38a82 --- /dev/null +++ b/lib/decoder/src/devices/CGDN1_json.h @@ -0,0 +1,55 @@ +const char* _CGDN1_json = "{\"brand\":\"Qingping\",\"model\":\"Air Monitor Lite\",\"model_id\":\"CGDN1\",\"tag\":\"0f\",\"condition\":[\"servicedata\",\"=\",48,\"index\",2,\"0e\",\"|\",\"servicedata\",\"=\",48,\"index\",2,\"24\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true,false],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"pm25\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,true,false]},\"pm10\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",36,4,true,false]},\"co2\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",44,4,true,false]}}}"; +/*R""""( +{ + "brand":"Qingping", + "model":"Air Monitor Lite", + "model_id":"CGDN1", + "tag":"0f", + "condition":["servicedata", "=", 48, "index", 2, "0e", "|", "servicedata", "=", 48, "index", 2, "24", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true, false], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "pm25":{ + "decoder":["value_from_hex_data", "servicedata", 32, 4, true, false] + }, + "pm10":{ + "decoder":["value_from_hex_data", "servicedata", 36, 4, true, false] + }, + "co2":{ + "decoder":["value_from_hex_data", "servicedata", 44, 4, true, false] + } + } +})"""";*/ + +const char* _CGDN1_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"pm25\":{\"unit\":\"μg/m³\",\"name\":\"PM2.5\"},\"pm10\":{\"unit\":\"μg/m³\",\"name\":\"PM10\"},\"co2\":{\"unit\":\"ppm\",\"name\":\"carbon dioxide\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "pm25":{ + "unit":"μg/m³", + "name":"PM2.5" + }, + "pm10":{ + "unit":"μg/m³", + "name":"PM10" + }, + "co2":{ + "unit":"ppm", + "name":"carbon dioxide" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/CGG1_json.h b/lib/decoder/src/devices/CGG1_json.h new file mode 100644 index 00000000..df0b6ea3 --- /dev/null +++ b/lib/decoder/src/devices/CGG1_json.h @@ -0,0 +1,125 @@ +#include "common_props.h" + +const char* _CGG1_json_STOCK = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"Round TH\",\"model_id\":\"CGG1\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",34,\"index\",2,\"07\",\"|\",\"servicedata\",\"=\",34,\"index\",2,\"16\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true],\"post_proc\":[\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,2,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; +/* R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"Round TH", + "model_id":"CGG1", + "tag":"01", + "condition":["servicedata", "=", 34, "index", 2, "07", "|", "servicedata", "=", 34, "index", 2, "16", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 24, 4, true], + "post_proc":["/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 32, 2, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +// ATC1441 +const char* _CGG1_json_ATC1441 = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"Round TH\",\"model_id\":\"CGG1_ATC1441\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",26,\"&\",\"uuid\",\"index\",0,\"181a\",\"&\",\"name\",\"index\",0,\"CGG\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,false],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,2,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,false],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"Round TH", + "model_id":"CGG1_ATC1441", + "tag":"0102", + "condition":["servicedata", "=", 26, "&", "uuid", "index", 0, "181a", "&", "name", "index", 0, "CGG"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, false], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 18, 2, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, false], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["mac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +// PVVX +const char* _CGG1_json_PVVX = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"Round TH\",\"model_id\":\"CGG1_PVVX\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",30,\"&\",\"uuid\",\"index\",0,\"181a\",\"&\",\"name\",\"index\",0,\"CGG\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,4,true],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,2,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"Round TH", + "model_id":"CGG1_PVVX", + "tag":"0102", + "condition":["servicedata", "=", 30, "&", "uuid", "index", 0, "181a", "&", "name", "index", 0, "CGG"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 4, true], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 24, 2, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +const char* _CGG1_json_STOCK_2 = "{\"brand\":\"ClearGrass/Qingping\",\"model\":\"Round TH\",\"model_id\":\"CGG1\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",30,\"|\",\"servicedata\",\"=\",32,\"|\",\"servicedata\",\"=\",36,\"&\",\"name\",\"index\",0,\"Qingping Temp & RH\",\"|\",\"name\",\"index\",0,\"ClearGrass Temp & RH\",\"&\",\"uuid\",\"index\",0,\"fe95\"],\"properties\":{\"tempc\":{\"condition\":[\"servicedata\",\">=\",32,\"&\",\"servicedata\",23,\"!\",\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"condition\":[\"servicedata\",\"=\",36,\"&\",\"servicedata\",23,\"!\",\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,true],\"post_proc\":[\"/\",10]},\"_hum\":{\"condition\":[\"servicedata\",\"=\",32,\"&\",\"servicedata\",23,\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true],\"post_proc\":[\"/\",10]},\"batt\":{\"condition\":[\"servicedata\",\"=\",30],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,2,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/* +R""""( +{ + "brand":"ClearGrass/Qingping", + "model":"Round TH", + "model_id":"CGG1", + "tag":"0102", + "condition":["servicedata", "=", 30, "|", "servicedata", "=", 32, "|", "servicedata", "=", 36, "&", "name", "index", 0, "Qingping Temp & RH", "|", "name", "index", 0, "ClearGrass Temp & RH", "&","uuid", "index", 0, "fe95"], + "properties":{ + "tempc":{ + "condition":["servicedata", ">=", 32, "&", "servicedata", 23, "!", "6"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "condition":["servicedata", "=", 36, "&", "servicedata", 23, "!", "6"], + "decoder":["value_from_hex_data", "servicedata", 32, 4, true], + "post_proc":["/", 10] + }, + "_hum":{ + "condition":["servicedata", "=", 32, "&", "servicedata", 23, "6"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true], + "post_proc":["/", 10] + }, + "batt":{ + "condition":["servicedata", "=", 30], + "decoder":["value_from_hex_data", "servicedata", 28, 2, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _CGG1_json_props = _common_BVTH_props; diff --git a/lib/decoder/src/devices/CGH1_json.h b/lib/decoder/src/devices/CGH1_json.h new file mode 100644 index 00000000..d9235802 --- /dev/null +++ b/lib/decoder/src/devices/CGH1_json.h @@ -0,0 +1,37 @@ +const char* _CGH1_json = "{\"brand\":\"Qingping\",\"model\":\"Contact Sensor\",\"model_id\":\"CGH1\",\"tag\":\"0404\",\"condition\":[\"servicedata\",\"=\",34,\"index\",2,\"04\",\"|\",\"servicedata\",\"=\",28,\"index\",2,\"04\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"open\":{\"condition\":[\"servicedata\",\"=\",28],\"decoder\":[\"bit_static_value\",\"servicedata\",21,0,true,false]},\"_open\":{\"condition\":[\"servicedata\",\"=\",34],\"decoder\":[\"bit_static_value\",\"servicedata\",33,0,true,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; +/*R""""( +{ + "brand":"Qingping", + "model":"Contact Sensor", + "model_id":"CGH1", + "tag":"0404", + "condition":["servicedata", "=", 34, "index", 2, "04", "|", "servicedata", "=", 28, "index", 2, "04", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "open":{ + "condition":["servicedata", "=", 28], + "decoder":["bit_static_value", "servicedata", 21, 0, true, false] + }, + "_open":{ + "condition":["servicedata", "=", 34], + "decoder":["bit_static_value", "servicedata", 33, 0, true, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +const char* _CGH1_json_props = "{\"properties\":{\"open\":{\"unit\":\"status\",\"name\":\"door\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "open":{ + "unit":"status", + "name":"door" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/CGP1W_json.h b/lib/decoder/src/devices/CGP1W_json.h new file mode 100644 index 00000000..228a6387 --- /dev/null +++ b/lib/decoder/src/devices/CGP1W_json.h @@ -0,0 +1,49 @@ +const char* _CGP1W_json = "{\"brand\":\"ClearGrass\",\"model\":\"Weather Station\",\"model_id\":\"CGP1W\",\"tag\":\"02\",\"condition\":[\"servicedata\",\"=\",42,\"index\",2,\"09\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"pres\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; +/*R""""( +{ + "brand":"ClearGrass", + "model":"Weather Station", + "model_id":"CGP1W", + "tag":"02", + "condition":["servicedata", "=", 42, "index", 2, "09", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "pres":{ + "decoder":["value_from_hex_data", "servicedata", 32, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +const char* _CGP1W_json_props = "{\"properties\":{\"pres\":{\"unit\":\"hPa\",\"name\":\"pressure\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "pres":{ + "unit":"hPa", + "name":"pressure" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ \ No newline at end of file diff --git a/lib/decoder/src/devices/CGPR1_json.h b/lib/decoder/src/devices/CGPR1_json.h new file mode 100644 index 00000000..484177a7 --- /dev/null +++ b/lib/decoder/src/devices/CGPR1_json.h @@ -0,0 +1,58 @@ +const char* _CGPR1_json = "{\"brand\":\"Qingping\",\"model\":\"Motion & Light\",\"model_id\":\"CGPR1\",\"tag\":\"0404\",\"condition\":[\"servicedata\",\"=\",28,\"index\",2,\"12\",\"|\",\"servicedata\",\"=\",34,\"index\",2,\"12\",\"|\",\"servicedata\",\"=\",40,\"index\",2,\"12\",\"&\",\"uuid\",\"index\",0,\"fdcd\"],\"properties\":{\"lux\":{\"condition\":[\"servicedata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,true,false]},\"_lux\":{\"condition\":[\"servicedata\",\"=\",34],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,true,false]},\"motion\":{\"condition\":[\"servicedata\",\"=\",34],\"decoder\":[\"bit_static_value\",\"servicedata\",21,0,false,true]},\"_motion\":{\"condition\":[\"servicedata\",\"=\",28],\"decoder\":[\"bit_static_value\",\"servicedata\",21,0,false,true]},\"batt\":{\"condition\":[\"servicedata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,2,false,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",4]}}}"; +/* +R""""( +{ + "brand":"Qingping", + "model":"Motion & Light", + "model_id":"CGPR1", + "tag":"0404", + "condition":["servicedata", "=", 28, "index", 2, "12", "|", "servicedata", "=", 34, "index", 2, "12", "|", "servicedata", "=", 40, "index", 2, "12", "&", "uuid", "index", 0, "fdcd"], + "properties":{ + "lux":{ + "condition":["servicedata", "=", 40], + "decoder":["value_from_hex_data", "servicedata", 32, 4, true, false] + }, + "_lux":{ + "condition":["servicedata", "=", 34], + "decoder":["value_from_hex_data", "servicedata", 22, 4, true, false] + }, + "motion":{ + "condition":["servicedata", "=", 34], + "decoder":["bit_static_value", "servicedata", 21, 0, false, true] + }, + "_motion":{ + "condition":["servicedata", "=", 28], + "decoder":["bit_static_value", "servicedata", 21, 0, false, true] + }, + "batt":{ + "condition":["servicedata", "=", 40], + "decoder":["value_from_hex_data", "servicedata", 20, 2, false, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 4] + } + } +})"""";*/ + +const char* _CGPR1_json_props = "{\"properties\":{\"lux\":{\"unit\":\"lx\",\"name\":\"illuminance\"},\"motion\":{\"unit\":\"status\",\"name\":\"motion\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "lux":{ + "unit":"lx", + "name":"illuminance" + }, + "motion":{ + "unit":"status", + "name":"motion" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/GAEN_json.h b/lib/decoder/src/devices/GAEN_json.h new file mode 100644 index 00000000..bf61c13e --- /dev/null +++ b/lib/decoder/src/devices/GAEN_json.h @@ -0,0 +1,33 @@ +const char* _GAEN_json = "{\"brand\":\"GENERIC\",\"model\":\"GAEN\",\"model_id\":\"GAEN\",\"tag\":\"fe\",\"condition\":[\"uuid\",\"index\",0,\"fd6f\"],\"properties\":{\"rpi\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",0,32]},\"aem\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",32,8]}}}"; + +/*R""""( +{ + "brand":"GENERIC", + "model":"GAEN", + "model_id":"GAEN", + "tag":"fe", + "condition":["uuid", "index", 0, "fd6f"], + "properties":{ + "rpi":{ + "decoder":["string_from_hex_data", "servicedata", 0, 32] + }, + "aem":{ + "decoder":["string_from_hex_data", "servicedata", 32, 8] + } + } +})"""";*/ + +const char* _GAEN_json_props = "{\"properties\":{\"rpi\":{\"unit\":\"hex\",\"name\":\"rolling proximity identifier\"},\"aem\":{\"unit\":\"hex\",\"name\":\"associated encrypted metadata\"}}}"; +/*R""""( +{ + "properties":{ + "rpi":{ + "unit":"hex", + "name":"rolling proximity identifier" + }, + "aem":{ + "unit":"hex", + "name":"associated encrypted metadata" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/H5055_json.h b/lib/decoder/src/devices/H5055_json.h new file mode 100644 index 00000000..67921bd9 --- /dev/null +++ b/lib/decoder/src/devices/H5055_json.h @@ -0,0 +1,74 @@ +const char* _H5055_json = "{\"brand\":\"Govee\",\"model\":\"Bluetooth BBQ Thermometer\",\"model_id\":\"H5055\",\"tag\":\"0301\",\"condition\":[\"manufacturerdata\",\">=\",41,\"index\",12,\"06\",\"|\",\"manufacturerdata\",\">=\",41,\"index\",12,\"20\",\"&\",\"manufacturerdata\",\"index\",26,\"06\",\"|\",\"manufacturerdata\",\">=\",41,\"index\",26,\"20\",\"&\",\"manufacturerdata\",\"index\",40,\"0\"],\"properties\":{\"tempc1\":{\"condition\":[\"manufacturerdata\",14,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,0,\"&\",\"manufacturerdata\",10,\"bit\",2,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,4,true,false]},\"tempc2\":{\"condition\":[\"manufacturerdata\",28,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,0,\"&\",\"manufacturerdata\",10,\"bit\",2,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,false]},\"tempc3\":{\"condition\":[\"manufacturerdata\",14,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,0,\"&\",\"manufacturerdata\",10,\"bit\",2,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,4,true,false]},\"tempc4\":{\"condition\":[\"manufacturerdata\",28,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,0,\"&\",\"manufacturerdata\",10,\"bit\",2,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,false]},\"tempc5\":{\"condition\":[\"manufacturerdata\",14,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,1,\"&\",\"manufacturerdata\",10,\"bit\",2,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,4,true,false]},\"tempc6\":{\"condition\":[\"manufacturerdata\",28,\"!\",\"ffff\",\"&\",\"manufacturerdata\",10,\"bit\",3,1,\"&\",\"manufacturerdata\",10,\"bit\",2,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,2,false]}}}"; +/*R""""( +{ + "brand":"Govee", + "model":"Bluetooth BBQ Thermometer", + "model_id":"H5055", + "tag":"0301", + "condition":["manufacturerdata", ">=", 41, "index", 12, "06", "|", "manufacturerdata", ">=", 41, "index", 12, "20", "&", "manufacturerdata", "index", 26, "06", "|", "manufacturerdata", ">=", 41, "index", 26, "20", "&", "manufacturerdata", "index", 40, "0"], + "properties":{ + "tempc1":{ + "condition":["manufacturerdata", 14, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 0, "&", "manufacturerdata", 10, "bit", 2, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 14, 4, true, false] + }, + "tempc2":{ + "condition":["manufacturerdata", 28, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 0, "&", "manufacturerdata", 10, "bit", 2, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, false] + }, + "tempc3":{ + "condition":["manufacturerdata", 14, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 0, "&", "manufacturerdata", 10, "bit", 2, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 14, 4, true, false] + }, + "tempc4":{ + "condition":["manufacturerdata", 28, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 0, "&", "manufacturerdata", 10, "bit", 2, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, false] + }, + "tempc5":{ + "condition":["manufacturerdata", 14, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 1, "&", "manufacturerdata", 10, "bit", 2, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 14, 4, true, false] + }, + "tempc6":{ + "condition":["manufacturerdata", 28, "!", "ffff", "&", "manufacturerdata", 10, "bit", 3, 1, "&", "manufacturerdata", 10, "bit", 2, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 2, false] + } + } +})"""";*/ + +const char* _H5055_json_props = "{\"properties\":{\"tempc1\":{\"unit\":\"°C\",\"name\":\"tempc1\"},\"tempc2\":{\"unit\":\"°C\",\"name\":\"tempc2\"},\"tempc3\":{\"unit\":\"°C\",\"name\":\"tempc3\"},\"tempc4\":{\"unit\":\"°C\",\"name\":\"tempc4\"},\"tempc5\":{\"unit\":\"°C\",\"name\":\"tempc5\"},\"tempc6\":{\"unit\":\"°C\",\"name\":\"tempc6\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + + "tempc1":{ + "unit":"°C", + "name":"tempc1" + }, + "tempc2":{ + "unit":"°C", + "name":"tempc2" + }, + "tempc3":{ + "unit":"°C", + "name":"tempc3" + }, + "tempc4":{ + "unit":"°C", + "name":"tempc4" + }, + "tempc5":{ + "unit":"°C", + "name":"tempc5" + }, + "tempc6":{ + "unit":"°C", + "name":"tempc6" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/H5072_json.h b/lib/decoder/src/devices/H5072_json.h new file mode 100644 index 00000000..190ceb58 --- /dev/null +++ b/lib/decoder/src/devices/H5072_json.h @@ -0,0 +1,33 @@ +#include "common_props.h" + +const char* _H5072_json = "{\"brand\":\"Govee\",\"model\":\"Thermo-Hygrometer\",\"model_id\":\"H5072/75\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"GVH5072\",\"|\",\"name\",\"index\",0,\"GVH5075\",\"&\",\"manufacturerdata\",\">=\",16,\"index\",0,\"88ec\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",6,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,6,false,false],\"post_proc\":[\"/\",1000,\">\",0,\"/\",10]},\"_tempc\":{\"condition\":[\"manufacturerdata\",6,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,6,false,false],\"post_proc\":[\"-\",8388608,\"/\",10000,\"*\",-1]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,6,false,false],\"post_proc\":[\"%\",1000,\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,2,false,false]}}}"; + +/* R""""( +{ + "brand":"Govee", + "model":"Thermo-Hygrometer", + "model_id":"H5072/75", + "tag":"0103", + "condition":["name", "index", 0, "GVH5072", "|", "name", "index", 0, "GVH5075", "&", "manufacturerdata", ">=", 16, "index", 0, "88ec"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 6, "bit", 3, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 6, 6, false, false], + "post_proc":["/", 1000, ">", 0, "/", 10] + }, + "_tempc":{ + "condition":["manufacturerdata", 6, "bit", 3, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 6, 6, false, false], + "post_proc":["-", 8388608, "/", 10000, "*", -1] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 6, false, false], + "post_proc":["%", 1000, "/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 2, false, false] + } + } +})"""";*/ + +const char* _H5072_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/H5074_json.h b/lib/decoder/src/devices/H5074_json.h new file mode 100644 index 00000000..740c4f31 --- /dev/null +++ b/lib/decoder/src/devices/H5074_json.h @@ -0,0 +1,26 @@ +#include "common_props.h" + +const char* _H5074_json = "{\"brand\":\"Govee\",\"model\":\"Thermo-Hygrometer\",\"model_id\":\"H5074\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"Govee_H5074\",\"&\",\"manufacturerdata\",\">=\",18,\"index\",0,\"88ec\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,4,true,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,4,true,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,2,false,false]}}}"; +/* R""""( +{ + "brand":"Govee", + "model":"Thermo-Hygrometer", + "model_id":"H5074", + "tag":"0103", + "condition":["name", "index", 0, "Govee_H5074", "&", "manufacturerdata", ">=", 18, "index", 0, "88ec"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 4, true, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 10, 4, true, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 14, 2, false, false] + } + } +})"""";*/ + +const char* _H5074_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/H5102_json.h b/lib/decoder/src/devices/H5102_json.h new file mode 100644 index 00000000..f6bf58ca --- /dev/null +++ b/lib/decoder/src/devices/H5102_json.h @@ -0,0 +1,33 @@ +#include "common_props.h" + +const char* _H5102_json = "{\"brand\":\"Govee\",\"model\":\"Smart Thermo-Hygrometer\",\"model_id\":\"H5100/01/02/04/74/77\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"GVH5100\",\"|\",\"name\",\"index\",0,\"GVH5101\",\"|\",\"name\",\"index\",0,\"GVH5102\",\"|\",\"name\",\"index\",0,\"GVH5104\",\"|\",\"name\",\"index\",0,\"GVH5174\",\"|\",\"name\",\"index\",0,\"GVH5177\",\"&\",\"manufacturerdata\",\">=\",16,\"index\",0,\"0100\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",8,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,6,false,false],\"post_proc\":[\"/\",1000,\">\",0,\"/\",10]},\"_tempc\":{\"condition\":[\"manufacturerdata\",8,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,6,false,false],\"post_proc\":[\"&\",8388607,\"/\",1000,\">\",0,\"/\",10,\"*\",-1]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,6,false,false],\"post_proc\":[\"%\",1000,\"/\",10]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,2,false,false]}}}"; + +/* R""""( +{ + "brand":"Govee", + "model":"Smart Thermo-Hygrometer", + "model_id":"H5100/01/02/04/74/77", + "tag":"0103", + "condition":["name", "index", 0, "GVH5100", "|", "name", "index", 0, "GVH5101", "|", "name", "index", 0, "GVH5102", "|", "name", "index", 0, "GVH5104", "|", "name", "index", 0, "GVH5174", "|", "name", "index", 0, "GVH5177", "&", "manufacturerdata", ">=", 16, "index", 0, "0100"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 8, "bit", 3, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 8, 6, false, false], + "post_proc":["/", 1000, ">", 0, "/", 10] + }, + "_tempc":{ + "condition":["manufacturerdata", 8, "bit", 3, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 8, 6, false, false], + "post_proc":["&", 8388607, "/", 1000, ">", 0, "/", 10, "*", -1] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 6, false, false], + "post_proc":["%", 1000, "/", 10] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 14, 2, false, false] + } + } +})"""";*/ + +const char* _H5102_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/H5106_json.h b/lib/decoder/src/devices/H5106_json.h new file mode 100644 index 00000000..70aa94b4 --- /dev/null +++ b/lib/decoder/src/devices/H5106_json.h @@ -0,0 +1,52 @@ +const char* _H5106_json = "{\"brand\":\"Govee\",\"model\":\"Smart Air Quality Monitor\",\"model_id\":\"H5106\",\"tag\":\"0f03\",\"condition\":[\"name\",\"index\",0,\"GVH5106\",\"&\",\"manufacturerdata\",\">=\",16,\"index\",0,\"0100\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",8,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,8,false,false],\"post_proc\":[\"/\",1000000,\">\",0,\"/\",10]},\"_tempc\":{\"condition\":[\"manufacturerdata\",8,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,8,false,false],\"post_proc\":[\"&\",2147483647,\"/\",1000000,\">\",0,\"/\",10,\"*\",-1]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,8,false,false],\"post_proc\":[\"&\",2147483647,\"%\",1000000,\"/\",1000,\">\",0,\"/\",10]},\".cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,8,false,false],\"post_proc\":[\"&\",2147483647,\"/\",1000,\">\",0,\"*\",1000]},\"pm25\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,8,false,false],\"post_proc\":[\"&\",2147483647,\"-\",\".cal\"]}}}"; +/* R""""( +{ + "brand":"Govee", + "model":"Smart Air Quality Monitor", + "model_id":"H5106", + "tag":"0f03", + "condition":["name", "index", 0, "GVH5106", "&", "manufacturerdata", ">=", 16, "index", 0, "0100"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 8, "bit", 3, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 8, 8, false, false], + "post_proc":["/", 1000000, ">", 0, "/", 10] + }, + "_tempc":{ + "condition":["manufacturerdata", 8, "bit", 3, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 8, 8, false, false], + "post_proc":["&", 2147483647, "/", 1000000, ">", 0, "/", 10, "*", -1] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 8, false, false], + "post_proc":["&", 2147483647, "%", 1000000, "/", 1000, ">", 0, "/", 10] + }, + ".cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 8, false, false], + "post_proc":["&", 2147483647, "/", 1000, ">", 0, "*", 1000] + }, + "pm25":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 8, false, false], + "post_proc":["&", 2147483647, "-", ".cal"] + } + } +})"""";*/ + +const char* _H5106_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"pm25\":{\"unit\":\"μg/m³\",\"name\":\"PM2.5\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "pm25":{ + "unit":"μg/m³", + "name":"PM2.5" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/H5179_json.h b/lib/decoder/src/devices/H5179_json.h new file mode 100644 index 00000000..7c92eca2 --- /dev/null +++ b/lib/decoder/src/devices/H5179_json.h @@ -0,0 +1,26 @@ +#include "common_props.h" + +const char* _H5179_json = "{\"brand\":\"Govee\",\"model\":\"Thermo-Hygrometer\",\"model_id\":\"H5179\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"Govee_H5179\",\"&\",\"manufacturerdata\",\"=\",22,\"index\",0,\"0188ec\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,4,true,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,true,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,2,false,false]}}}"; +/* R""""( +{ + "brand":"Govee", + "model":"Thermo-Hygrometer", + "model_id":"H5179", + "tag":"0103", + "condition":["name", "index", 0, "Govee_H5179", "&", "manufacturerdata", "=", 22, "index", 0, "0188ec"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 4, true, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, true, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 2, false, false] + } + } +})"""";*/ + +const char* _H5179_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/HHCCJCY01HHCC_json.h b/lib/decoder/src/devices/HHCCJCY01HHCC_json.h new file mode 100644 index 00000000..e404c058 --- /dev/null +++ b/lib/decoder/src/devices/HHCCJCY01HHCC_json.h @@ -0,0 +1,58 @@ +const char* _HHCCJCY01HHCC_json = "{\"brand\":\"Xiaomi/VegTrug\",\"model\":\"MiFlora\",\"model_id\":\"HHCCJCY01HHCC\",\"tag\":\"09\",\"condition\":[\"servicedata\",\"index\",4,\"9800\",\"|\",\"servicedata\",\"index\",4,\"bc03\",\"&\",\"uuid\",\"index\",0,\"fe95\"],\"properties\":{\"tempc\":{\"condition\":[\"servicedata\",24,\"0410\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,4,true],\"post_proc\":[\"/\",10]},\"moi\":{\"condition\":[\"servicedata\",24,\"0810\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false]},\"lux\":{\"condition\":[\"servicedata\",24,\"0710\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,6,true]},\"fer\":{\"condition\":[\"servicedata\",24,\"0910\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,4,true]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/*R""""( +{ + "brand":"Xiaomi/VegTrug", + "model":"MiFlora", + "model_id":"HHCCJCY01HHCC", + "tag":"09", + "condition":["servicedata", "index", 4, "9800", "|", "servicedata", "index", 4, "bc03", "&", "uuid", "index", 0, "fe95"], + "properties":{ + "tempc":{ + "condition":["servicedata", 24, "0410"], + "decoder":["value_from_hex_data", "servicedata", 30, 4, true], + "post_proc":["/", 10] + }, + "moi":{ + "condition":["servicedata", 24, "0810"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false] + }, + "lux":{ + "condition":["servicedata", 24, "0710"], + "decoder":["value_from_hex_data", "servicedata", 30, 6, true] + }, + "fer":{ + "condition":["servicedata", 24, "0910"], + "decoder":["value_from_hex_data", "servicedata", 30, 4, true] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _HHCCJCY01HHCC_json_props = "{\"properties\":{\"lux\":{\"unit\":\"lx\",\"name\":\"illuminance\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"fer\":{\"unit\":\"µS/cm\",\"name\":\"fertility\"},\"moi\":{\"unit\":\"%\",\"name\":\"moisture\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "lux":{ + "unit":"lx", + "name":"illuminance" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "fer":{ + "unit":"µS/cm", + "name":"fertility" + }, + "moi":{ + "unit":"%", + "name":"moisture" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/HHCCJCY10_json.h b/lib/decoder/src/devices/HHCCJCY10_json.h new file mode 100644 index 00000000..bf303a6e --- /dev/null +++ b/lib/decoder/src/devices/HHCCJCY10_json.h @@ -0,0 +1,54 @@ +const char* _HHCCJCY10_json = "{\"brand\":\"Xiaomi/VegTrug\",\"model\":\"MiFlora\",\"model_id\":\"HHCCJCY10\",\"tag\":\"09\",\"condition\":[\"servicedata\",\"=\",18,\"&\",\"uuid\",\"index\",0,\"fd50\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",2,4,false,true],\"post_proc\":[\"/\",10]},\"moi\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,2,false,false]},\"lux\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,6,false,false]},\"fer\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,4,false,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false]}}}"; +/*R""""( +{ + "brand":"Xiaomi/VegTrug", + "model":"MiFlora", + "model_id":"HHCCJCY10", + "tag":"09", + "condition":["servicedata", "=", 18, "&", "uuid", "index", 0, "fd50"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 2, 4, false, true], + "post_proc":["/", 10] + }, + "moi":{ + "decoder":["value_from_hex_data", "servicedata", 0, 2, false, false] + }, + "lux":{ + "decoder":["value_from_hex_data", "servicedata", 6, 6, false, false] + }, + "fer":{ + "decoder":["value_from_hex_data", "servicedata", 14, 4, false, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false] + } + } +})"""";*/ + +const char* _HHCCJCY10_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"moi\":{\"unit\":\"%\",\"name\":\"moisture\"},\"lux\":{\"unit\":\"lx\",\"name\":\"illuminance\"},\"fer\":{\"unit\":\"µS/cm\",\"name\":\"fertility\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "moi":{ + "unit":"%", + "name":"moisture" + }, + "lux":{ + "unit":"lx", + "name":"illuminance" + }, + "fer":{ + "unit":"µS/cm", + "name":"fertility" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/HHCCPOT002_json.h b/lib/decoder/src/devices/HHCCPOT002_json.h new file mode 100644 index 00000000..298c808e --- /dev/null +++ b/lib/decoder/src/devices/HHCCPOT002_json.h @@ -0,0 +1,41 @@ +const char* _HHCCPOT002_json = "{\"brand\":\"Xiaomi\",\"model\":\"RoPot\",\"model_id\":\"HHCCPOT002\",\"tag\":\"09\",\"condition\":[\"servicedata\",\"index\",2,\"205d01\"],\"properties\":{\"moi\":{\"condition\":[\"servicedata\",25,\"8\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false]},\"fer\":{\"condition\":[\"servicedata\",25,\"9\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,4,true]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"RoPot", + "model_id":"HHCCPOT002", + "tag":"09", + "condition":["servicedata", "index", 2, "205d01"], + "properties":{ + "moi":{ + "condition":["servicedata", 25, "8"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false] + }, + "fer":{ + "condition":["servicedata", 25, "9"], + "decoder":["value_from_hex_data", "servicedata", 30, 4, true] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _HHCCPOT002_json_props = "{\"properties\":{\"moi\":{\"unit\":\"%\",\"name\":\"moisture\"},\"fer\":{\"unit\":\"µS/cm\",\"name\":\"fertility\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "moi":{ + "unit":"%", + "name":"moisture" + }, + "fer":{ + "unit":"µS/cm", + "name":"fertility" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/IBS_THBP01B_json.h b/lib/decoder/src/devices/IBS_THBP01B_json.h new file mode 100644 index 00000000..eeb8c4b9 --- /dev/null +++ b/lib/decoder/src/devices/IBS_THBP01B_json.h @@ -0,0 +1,51 @@ +const char* _IBS_THBP01B_json = "{\"brand\":\"Inkbird\",\"model\":\"T(H) Sensor\",\"model_id\":\"IBS-TH1/TH2/P01B/ITH-12S\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"sps\",\"|\",\"name\",\"index\",0,\"tps\",\"&\",\"manufacturerdata\",\"=\",18],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",0,4,true],\"post_proc\":[\"/\",100]},\"extprobe\":{\"condition\":[\"manufacturerdata\",9,\"!\",\"0\"],\"decoder\":[\"static_value\",true]},\"hum\":{\"condition\":[\"manufacturerdata\",4,\"!\",\"ffff\",\"&\",\"manufacturerdata\",4,\"!\",\"0000\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",4,4,true,false],\"post_proc\":[\"/\",100]},\"batt\":{\"condition\":[\"manufacturerdata\",14,\"!\",\"f\",\"&\",\"manufacturerdata\",14,\"!\",\"e\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,2,false,false]}}}"; +/*R""""( +{ + "brand":"Inkbird", + "model":"T(H) Sensor", + "model_id":"IBS-TH1/TH2/P01B/ITH-12S", + "tag":"0103", + "condition":["name", "index", 0, "sps", "|", "name", "index", 0, "tps", "&", "manufacturerdata", "=", 18], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 0, 4, true], + "post_proc":["/", 100] + }, + "extprobe":{ + "condition":["manufacturerdata", 9, "!", "0"], + "decoder":["static_value", true] + }, + "hum":{ + "condition":["manufacturerdata", 4, "!", "ffff", "&", "manufacturerdata", 4, "!", "0000"], + "decoder":["value_from_hex_data", "manufacturerdata", 4, 4, true, false], + "post_proc":["/", 100] + }, + "batt":{ + "condition":["manufacturerdata", 14, "!", "f", "&", "manufacturerdata", 14, "!", "e"], + "decoder":["value_from_hex_data", "manufacturerdata", 14, 2, false, false] + } + } +})"""";*/ + +const char* _IBS_THBP01B_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"extprobe\":{\"unit\":\"status\",\"name\":\"external probe connected\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "extprobe":{ + "unit":"status", + "name":"external probe connected" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/IBT_2X_json.h b/lib/decoder/src/devices/IBT_2X_json.h new file mode 100644 index 00000000..04a2c3dc --- /dev/null +++ b/lib/decoder/src/devices/IBT_2X_json.h @@ -0,0 +1,70 @@ +const char* _IBT_2X_json_2XS = "{\"brand\":\"Inkbird\",\"model\":\"iBBQ\",\"model_id\":\"IBT-2X(S)\",\"tag\":\"0301\",\"condition\":[\"manufacturerdata\",\"=\",28,\"index\",0,\"00000000\",\"&\",\"manufacturerdata\",\"mac@index\",8],\"conditionnomac\":[\"name\",\"index\",0,\"iBBQ\",\"|\",\"name\",\"index\",0,\"xBBQ\",\"&\",\"manufacturerdata\",\"=\",28,\"index\",0,\"00000000\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",22,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false],\"post_proc\":[\"/\",10]},\"tempc2\":{\"condition\":[\"manufacturerdata\",26,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Inkbird", + "model":"iBBQ", + "model_id":"IBT-2X(S)", + "tag":"0301", + "condition":["manufacturerdata", "=", 28, "index", 0, "00000000", "&", "manufacturerdata", "mac@index", 8], + "conditionnomac":["name", "index", 0, "iBBQ", "|", "name", "index", 0, "xBBQ", "&","manufacturerdata", "=", 28, "index", 0, "00000000"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 22, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false], + "post_proc":["/", 10] + }, + "tempc2":{ + "condition":["manufacturerdata", 26, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _IBT_2X_json_2X = "{\"brand\":\"Inkbird\",\"model\":\"iBBQ\",\"model_id\":\"IBT-2X(S)\",\"tag\":\"0301\",\"condition\":[\"manufacturerdata\",\"=\",28,\"index\",0,\"01000000\",\"&\",\"manufacturerdata\",\"revmac@index\",8],\"conditionnomac\":[\"name\",\"index\",0,\"iBBQ\",\"|\",\"name\",\"index\",0,\"xBBQ\",\"&\",\"manufacturerdata\",\"=\",28,\"index\",0,\"01000000\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",22,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false],\"post_proc\":[\"/\",10]},\"tempc2\":{\"condition\":[\"manufacturerdata\",26,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Inkbird", + "model":"iBBQ", + "model_id":"IBT-2X(S)", + "tag":"0301", + "condition":["manufacturerdata", "=", 28, "index", 0, "01000000", "&", "manufacturerdata", "revmac@index", 8], + "conditionnomac":["name", "index", 0, "iBBQ", "|", "name", "index", 0, "xBBQ", "&", "manufacturerdata", "=", 28, "index", 0, "01000000"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 22, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false], + "post_proc":["/", 10] + }, + "tempc2":{ + "condition":["manufacturerdata", 26, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _IBT_2X_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc2\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "tempc2":{ + "unit":"°C", + "name":"temperature" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/IBT_4XS_json.h b/lib/decoder/src/devices/IBT_4XS_json.h new file mode 100644 index 00000000..3305ce71 --- /dev/null +++ b/lib/decoder/src/devices/IBT_4XS_json.h @@ -0,0 +1,62 @@ +const char* _IBT_4XS_json = "{\"brand\":\"Inkbird\",\"model\":\"iBBQ\",\"model_id\":\"IBT-4X(S/C)\",\"tag\":\"0301\",\"condition\":[\"manufacturerdata\",\"=\",36,\"index\",0,\"00000000\",\"&\",\"manufacturerdata\",\"mac@index\",8],\"conditionnomac\":[\"name\",\"index\",0,\"iBBQ\",\"&\",\"manufacturerdata\",\"=\",36,\"index\",0,\"00000000\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",22,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false],\"post_proc\":[\"/\",10]},\"tempc2\":{\"condition\":[\"manufacturerdata\",26,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"tempc3\":{\"condition\":[\"manufacturerdata\",30,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,false],\"post_proc\":[\"/\",10]},\"tempc4\":{\"condition\":[\"manufacturerdata\",34,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Inkbird", + "model":"iBBQ", + "model_id":"IBT-4X(S/C)", + "tag":"0301", + "condition":["manufacturerdata", "=" ,36 ,"index", 0, "00000000", "&", "manufacturerdata", "mac@index", 8], + "conditionnomac":["name", "index", 0, "iBBQ","&","manufacturerdata", "=" ,36 ,"index", 0, "00000000"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 22, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false], + "post_proc":["/", 10] + }, + "tempc2":{ + "condition":["manufacturerdata", 26, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "tempc3":{ + "condition":["manufacturerdata", 30, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, false], + "post_proc":["/", 10] + }, + "tempc4":{ + "condition":["manufacturerdata", 34, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 32, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _IBT_4XS_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc2\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc3\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc4\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "tempc2":{ + "unit":"°C", + "name":"temperature" + }, + "tempc3":{ + "unit":"°C", + "name":"temperature" + }, + "tempc4":{ + "unit":"°C", + "name":"temperature" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/IBT_6XS_SOLIS6_json.h b/lib/decoder/src/devices/IBT_6XS_SOLIS6_json.h new file mode 100644 index 00000000..b84efc07 --- /dev/null +++ b/lib/decoder/src/devices/IBT_6XS_SOLIS6_json.h @@ -0,0 +1,80 @@ +const char* _IBT_6XS_SOLIS6_json = "{\"brand\":\"Inkbird/Tenergy\",\"model\":\"iBBQ/SOLIS6\",\"model_id\":\"IBT-6XS/SOLIS-6\",\"tag\":\"0301\",\"condition\":[\"manufacturerdata\",\"=\",44,\"index\",0,\"00000000\",\"&\",\"manufacturerdata\",\"mac@index\",8],\"conditionnomac\":[\"name\",\"index\",0,\"iBBQ\",\"&\",\"manufacturerdata\",\"=\",44,\"index\",0,\"00000000\"],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",22,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false],\"post_proc\":[\"/\",10]},\"tempc2\":{\"condition\":[\"manufacturerdata\",26,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true,false],\"post_proc\":[\"/\",10]},\"tempc3\":{\"condition\":[\"manufacturerdata\",30,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true,false],\"post_proc\":[\"/\",10]},\"tempc4\":{\"condition\":[\"manufacturerdata\",34,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,4,true,false],\"post_proc\":[\"/\",10]},\"tempc5\":{\"condition\":[\"manufacturerdata\",38,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",36,4,true,false],\"post_proc\":[\"/\",10]},\"tempc6\":{\"condition\":[\"manufacturerdata\",42,\"!\",\"ff\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",40,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Inkbird/Tenergy", + "model":"iBBQ/SOLIS6", + "model_id":"IBT-6XS/SOLIS-6", + "tag":"0301", + "condition":["manufacturerdata", "=", 44, "index", 0, "00000000", "&", "manufacturerdata", "mac@index", 8], + "conditionnomac":["name", "index", 0, "iBBQ", "&", "manufacturerdata", "=", 44, "index", 0, "00000000"], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", 22, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false], + "post_proc":["/", 10] + }, + "tempc2":{ + "condition":["manufacturerdata", 26, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true, false], + "post_proc":["/", 10] + }, + "tempc3":{ + "condition":["manufacturerdata", 30, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true, false], + "post_proc":["/", 10] + }, + "tempc4":{ + "condition":["manufacturerdata", 34, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 32, 4, true, false], + "post_proc":["/", 10] + }, + "tempc5":{ + "condition":["manufacturerdata", 38, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 36, 4, true, false], + "post_proc":["/", 10] + }, + "tempc6":{ + "condition":["manufacturerdata", 42, "!", "ff"], + "decoder":["value_from_hex_data", "manufacturerdata", 40, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _IBT_6XS_SOLIS6_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc2\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc3\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc4\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc5\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"tempc6\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "tempc2":{ + "unit":"°C", + "name":"temperature" + }, + "tempc3":{ + "unit":"°C", + "name":"temperature" + }, + "tempc4":{ + "unit":"°C", + "name":"temperature" + }, + "tempc5":{ + "unit":"°C", + "name":"temperature" + }, + "tempc6":{ + "unit":"°C", + "name":"temperature" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/JHT_F525_json.h b/lib/decoder/src/devices/JHT_F525_json.h new file mode 100644 index 00000000..5122d67b --- /dev/null +++ b/lib/decoder/src/devices/JHT_F525_json.h @@ -0,0 +1,26 @@ +#include "common_props.h" + +const char* _JHT_F525_json = "{\"brand\":\"Jaalee\",\"model\":\"TH sensor\",\"model_id\":\"F525\",\"tag\":\"0102\",\"condition\":[\"manufacturerdata\",\"=\",52,\"&\",\"uuid\",\"contain\",\"f525\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",40,4,false],\"post_proc\":[\"*\",175.72,\"/\",65536,\"-\",46.85]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",44,4,false,false],\"post_proc\":[\"*\",125.0,\"/\",65536,\"-\",6]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",50,2,false,false]}}}"; +/* R""""( +{ + "brand":"Jaalee", + "model":"TH sensor", + "model_id":"F525", + "tag":"0102", + "condition":["manufacturerdata", "=", 52, "&", "uuid", "contain", "f525"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 40, 4, false], + "post_proc":["*", 175.72, "/", 65536, "-", 46.85] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 44, 4, false, false], + "post_proc":["*", 125.0, "/", 65536, "-", 6] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 50, 2, false, false] + } + } +})"""";*/ + +const char* _JHT_F525_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/JQJCY01YM_json.h b/lib/decoder/src/devices/JQJCY01YM_json.h new file mode 100644 index 00000000..7bd2f9d0 --- /dev/null +++ b/lib/decoder/src/devices/JQJCY01YM_json.h @@ -0,0 +1,60 @@ +const char* _JQJCY01YM_json = "{\"brand\":\"Xiaomi\",\"model\":\"Formaldehyde detector\",\"model_id\":\"JQJCY01YM\",\"tag\":\"0f\",\"condition\":[\"servicedata\",\"index\",2,\"20df02\"],\"properties\":{\"for\":{\"condition\":[\"servicedata\",23,\"0\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true],\"post_proc\":[\"/\",100]},\"hum\":{\"condition\":[\"servicedata\",23,\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true,false],\"post_proc\":[\"/\",10]},\"tempc\":{\"condition\":[\"servicedata\",23,\"4\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true,false],\"post_proc\":[\"/\",10]},\"batt\":{\"condition\":[\"servicedata\",23,\"a\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,2,false,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Formaldehyde detector", + "model_id":"JQJCY01YM", + "tag":"0f", + "condition":["servicedata", "index", 2, "20df02"], + "properties":{ + "for":{ + "condition":["servicedata", 23, "0"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true], + "post_proc":["/", 100] + }, + "hum":{ + "condition":["servicedata", 23, "6"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true, false], + "post_proc":["/", 10] + }, + "tempc":{ + "condition":["servicedata", 23, "4"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true, false], + "post_proc":["/", 10] + }, + "batt":{ + "condition":["servicedata", 23, "a"], + "decoder":["value_from_hex_data", "servicedata", 28, 2, false, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _JQJCY01YM_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"for\":{\"unit\":\"mg/m³\",\"name\":\"formaldehyde\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "for":{ + "unit":"mg/m³", + "name":"formaldehyde" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ \ No newline at end of file diff --git a/lib/decoder/src/devices/KKM_K6P_json.h b/lib/decoder/src/devices/KKM_K6P_json.h new file mode 100644 index 00000000..ec4bba1a --- /dev/null +++ b/lib/decoder/src/devices/KKM_K6P_json.h @@ -0,0 +1,50 @@ +const char* _KKM_K6P_json = "{\"brand\":\"KKM\",\"model\":\"Long Range K6P\",\"model_id\":\"K6P\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",18,\"index\",0,\"210107\",\"&\",\"uuid\",\"index\",0,\"feaa\"],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false],\"post_proc\":[\"/\",256,\"*\",100,\">\",0,\"/\",100]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",10,2,false,true],\"post_proc\":[\"+\",\".cal\"]},\"_.cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false,false],\"post_proc\":[\"/\",256,\"*\",100,\">\",0,\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"+\",\".cal\"]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,4,false,false],\"post_proc\":[\"/\",1000]}}}"; +/*R""""( +{ + "brand":"KKM", + "model":"Long Range K6P", + "model_id":"K6P", + "tag":"01", + "condition":["servicedata", "=", 18, "index", 0, "210107", "&", "uuid", "index", 0, "feaa"], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false], + "post_proc":["/", 256, "*", 100, ">", 0, "/", 100] + }, + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 10, 2, false, true], + "post_proc":["+", ".cal"] + }, + "_.cal":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false, false], + "post_proc":["/", 256, "*", 100, ">", 0, "/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["+", ".cal"] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 6, 4, false, false], + "post_proc":["/", 1000] + } + } +})"""";*/ + +const char* _KKM_K6P_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "volt":{ + "unit":"V", + "name":"voltage" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/KKM_K9_json.h b/lib/decoder/src/devices/KKM_K9_json.h new file mode 100644 index 00000000..cbd3762e --- /dev/null +++ b/lib/decoder/src/devices/KKM_K9_json.h @@ -0,0 +1,74 @@ +const char* _KKM_K9_json = "{\"brand\":\"KKM\",\"model\":\"Tracking K9\",\"model_id\":\"K9\",\"tag\":\"0708\",\"condition\":[\"servicedata\",\"=\",30,\"index\",0,\"21010f\",\"&\",\"uuid\",\"index\",0,\"feaa\"],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false],\"post_proc\":[\"/\",256,\"*\",100,\">\",0,\"/\",100]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",10,2,false,true],\"post_proc\":[\"+\",\".cal\"]},\"_.cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false,false],\"post_proc\":[\"/\",256,\"*\",100,\">\",0,\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"+\",\".cal\"]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,4,false,false],\"post_proc\":[\"/\",1000]},\"accx\":{\"condition\":[\"servicedata\",0,\"21010f\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,false,true]},\"accy\":{\"condition\":[\"servicedata\",0,\"21010f\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,false,true]},\"accz\":{\"condition\":[\"servicedata\",0,\"21010f\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",26,4,false,true]}}}"; +/*R""""( +{ + "brand":"KKM", + "model":"Tracking K9", + "model_id":"K9", + "tag":"0708", + "condition":["servicedata", "=", 30, "index", 0, "21010f", "&", "uuid", "index", 0, "feaa"], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false], + "post_proc":["/", 256, "*", 100, ">", 0, "/", 100] + }, + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 10, 2, false, true], + "post_proc":["+", ".cal"] + }, + "_.cal":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false, false], + "post_proc":["/", 256, "*", 100, ">", 0, "/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["+", ".cal"] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 6, 4, false, false], + "post_proc":["/", 1000] + }, + "accx":{ + "condition":["servicedata", 0, "21010f"], + "decoder":["value_from_hex_data", "servicedata", 18, 4, false, true] + }, + "accy":{ + "condition":["servicedata", 0, "21010f"], + "decoder":["value_from_hex_data", "servicedata", 22, 4, false, true] + }, + "accz":{ + "condition":["servicedata", 0, "21010f"], + "decoder":["value_from_hex_data", "servicedata", 26, 4, false, true] + } + } +})"""";*/ + +const char* _KKM_K9_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"},\"accz\":{\"unit\":\"m/s²\",\"name\":\"acceleration z\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + }, + "accz":{ + "unit":"m/s²", + "name":"acceleration z" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/LYWSD02_json.h b/lib/decoder/src/devices/LYWSD02_json.h new file mode 100644 index 00000000..402c5840 --- /dev/null +++ b/lib/decoder/src/devices/LYWSD02_json.h @@ -0,0 +1,32 @@ +#include "common_props.h" + +const char* _LYWSD02_json = "{\"brand\":\"Xiaomi/Mijia\",\"model\":\"e-ink Clock\",\"model_id\":\"LYWSD02\",\"tag\":\"01\",\"condition\":[\"uuid\",\"index\",0,\"fe95\",\"&\",\"servicedata\",\"index\",4,\"5b04\"],\"properties\":{\"tempc\":{\"condition\":[\"servicedata\",24,\"0410\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"condition\":[\"servicedata\",24,\"0610\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,4,true,false],\"post_proc\":[\"/\",10]},\"batt\":{\"condition\":[\"servicedata\",24,\"0a10\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/* R""""( +{ + "brand":"Xiaomi/Mijia", + "model":"e-ink Clock", + "model_id":"LYWSD02", + "tag":"01", + "condition":["uuid", "index", 0, "fe95", "&", "servicedata", "index", 4, "5b04"], + "properties":{ + "tempc":{ + "condition":["servicedata", 24, "0410"], + "decoder":["value_from_hex_data", "servicedata", 30, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "condition":["servicedata", 24, "0610"], + "decoder":["value_from_hex_data", "servicedata", 30, 4, true, false], + "post_proc":["/", 10] + }, + "batt":{ + "condition":["servicedata", 24, "0a10"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _LYWSD02_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/LYWSD03MMC_ENCR_json.h b/lib/decoder/src/devices/LYWSD03MMC_ENCR_json.h new file mode 100644 index 00000000..91f2491f --- /dev/null +++ b/lib/decoder/src/devices/LYWSD03MMC_ENCR_json.h @@ -0,0 +1,39 @@ +const char* _LYWSD03MMC_ENCR_json_PVVX = "{\"brand\":\"Xiaomi\",\"model\":\"TH Sensor\",\"model_id\":\"LYWSD03MMC/MJWSD05MMC_PVVX_ENCR\",\"tag\":\"010001\",\"condition\":[\"servicedata\",\"=\",22,\"&\",\"uuid\",\"index\",0,\"181a\"],\"properties\":{\"cipher\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",2,12]},\"ctr\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",0,2]},\"mic\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",14,8]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"TH Sensor", + "model_id":"LYWSD03MMC/MJWSD05MMC_PVVX_ENCR", + "tag":"010001", + "condition":["servicedata", "=", 22, "&", "uuid", "index", 0, "181a"], + "properties":{ + "cipher":{ + "decoder":["string_from_hex_data", "servicedata", 2, 12] + }, + "ctr":{ + "decoder":["string_from_hex_data", "servicedata", 0, 2] + }, + "mic":{ + "decoder":["string_from_hex_data", "servicedata", 14, 8] + } + } +})"""";*/ + +const char* _LYWSD03MMC_ENCR_json_props = "{\"properties\":{\"cipher\":{\"unit\":\"hex\",\"name\":\"ciphertext\"},\"ctr\":{\"unit\":\"hex\",\"name\":\"counter\"},\"mic\":{\"unit\":\"hex\",\"name\":\"message integrity check\"}}}"; +/*R""""( +{ + "properties":{ + "cipher":{ + "unit":"hex", + "name":"ciphertext" + }, + "ctr":{ + "unit":"hex", + "name":"counter" + }, + "mic":{ + "unit":"hex", + "name":"message integrity check" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/LYWSD03MMC_json.h b/lib/decoder/src/devices/LYWSD03MMC_json.h new file mode 100644 index 00000000..91948ed5 --- /dev/null +++ b/lib/decoder/src/devices/LYWSD03MMC_json.h @@ -0,0 +1,85 @@ +#include "common_props.h" + +const char* _LYWSD03MMC_json_ATC = "{\"brand\":\"Xiaomi\",\"model\":\"TH Sensor\",\"model_id\":\"LYWSD03MMC/MJWSD05MMC_ATC\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",26,\"index\",0,\"a4c138\",\"&\",\"uuid\",\"index\",0,\"181a\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,false,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,2,false,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,2,false,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,false,false],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"Xiaomi", + "model":"TH Sensor", + "model_id":"LYWSD03MMC/MJWSD05MMC_ATC", + "tag":"01", + "condition":["servicedata", "=", 26, "index", 0 , "a4c138", "&", "uuid", "index", 0, "181a"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, false, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 2, false, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 18, 2, false, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, false, false], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["mac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +const char* _LYWSD03MMC_json_PVVX = "{\"brand\":\"Xiaomi\",\"model\":\"TH Sensor\",\"model_id\":\"LYWSD03MMC/MJWSD05MMC_PVVX\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",30,\"index\",6,\"38c1a4\",\"&\",\"uuid\",\"index\",0,\"181a\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,true,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,4,true,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,2,false,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,true,false],\"post_proc\":[\"/\",1000]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",0]}}}"; +/* R""""( +{ + "brand":"Xiaomi", + "model":"TH Sensor", + "model_id":"LYWSD03MMC/MJWSD05MMC_PVVX", + "tag":"01", + "condition":["servicedata", "=", 30, "index", 6 , "38c1a4", "&", "uuid", "index", 0, "181a"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 12, 4, true, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 16, 4, true, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 24, 2, false, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "servicedata", 20, 4, true, false], + "post_proc":["/", 1000] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 0] + } + } +})"""";*/ + +const char* _LYWSD03MMC_json_PVVX_DECR = "{\"brand\":\"Xiaomi\",\"model\":\"TH Sensor\",\"model_id\":\"LYWSD03MMC/MJWSD05MMC_PVVX_DECR\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"=\",12,\"&\",\"uuid\",\"index\",0,\"181a\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,4,true,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,4,true,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,false,false]}}}"; +/* R""""( +{ + "brand":"Xiaomi", + "model":"TH Sensor", + "model_id":"LYWSD03MMC/MJWSD05MMC_PVVX_DECR", + "tag":"01", + "condition":["servicedata", "=", 12, "&", "uuid", "index", 0, "181a"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 0, 4, true, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 4, 4, true, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 8, 2, false, false] + } + } +})"""";*/ + +const char* _LYWSD03MMC_json_props = _common_BVTH_props; diff --git a/lib/decoder/src/devices/LYWSDCGQ_json.h b/lib/decoder/src/devices/LYWSDCGQ_json.h new file mode 100644 index 00000000..392210c7 --- /dev/null +++ b/lib/decoder/src/devices/LYWSDCGQ_json.h @@ -0,0 +1,37 @@ +#include "common_props.h" + +const char* _LYWSDCGQ_json = "{\"brand\":\"Xiaomi\",\"model\":\"Mi Jia round\",\"model_id\":\"LYWSDCGQ\",\"tag\":\"01\",\"condition\":[\"servicedata\",\"index\",2,\"20aa01\"],\"properties\":{\"batt\":{\"condition\":[\"servicedata\",23,\"a\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,2,true,false]},\"tempc\":{\"condition\":[\"servicedata\",23,\"d\",\"|\",\"servicedata\",23,\"4\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true],\"post_proc\":[\"/\",10]},\"hum\":{\"condition\":[\"servicedata\",23,\"d\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",32,4,true,false],\"post_proc\":[\"/\",10]},\"_hum\":{\"condition\":[\"servicedata\",23,\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,4,true,false],\"post_proc\":[\"/\",10]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"servicedata\",10]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Mi Jia round", + "model_id":"LYWSDCGQ", + "tag":"01", + "condition":["servicedata", "index", 2, "20aa01"], + "properties":{ + "batt":{ + "condition":["servicedata", 23, "a"], + "decoder":["value_from_hex_data", "servicedata", 28, 2, true, false] + }, + "tempc":{ + "condition":["servicedata", 23, "d", "|", "servicedata", 23, "4"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true], + "post_proc":["/", 10] + }, + "hum":{ + "condition":["servicedata", 23, "d"], + "decoder":["value_from_hex_data", "servicedata", 32, 4, true, false], + "post_proc":["/", 10] + }, + "_hum":{ + "condition":["servicedata", 23, "6"], + "decoder":["value_from_hex_data", "servicedata", 28, 4, true, false], + "post_proc":["/", 10] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "servicedata", 10] + } + } +})"""";*/ + +const char* _LYWSDCGQ_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/MBXPRO_json.h b/lib/decoder/src/devices/MBXPRO_json.h new file mode 100644 index 00000000..d5e92a6b --- /dev/null +++ b/lib/decoder/src/devices/MBXPRO_json.h @@ -0,0 +1,82 @@ +const char* _MBXPRO_json = "{\"brand\":\"Mokosmart\",\"model\":\"BeaconX Pro\",\"model_id\":\"MBXPRO\",\"tag\":\"0708\",\"condition\":[\"uuid\",\"index\",0,\"feab\"],\"properties\":{\"volt\":{\"condition\":[\"servicedata\",0,\"40\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,4,false],\"post_proc\":[\"/\",1000]},\"x_axis\":{\"condition\":[\"servicedata\",0,\"60\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"y_axis\":{\"condition\":[\"servicedata\",0,\"60\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"z_axis\":{\"condition\":[\"servicedata\",0,\"60\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"_volt\":{\"condition\":[\"servicedata\",0,\"60\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,false],\"post_proc\":[\"/\",1000]},\"tempc\":{\"condition\":[\"servicedata\",0,\"70\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,4,false],\"post_proc\":[\"/\",10]},\"hum\":{\"condition\":[\"servicedata\",0,\"70\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",10,4,false,false],\"post_proc\":[\"/\",10]},\"__volt\":{\"condition\":[\"servicedata\",0,\"70\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,4,false],\"post_proc\":[\"/\",1000]}}}"; +/*R""""( +{ + "brand":"Mokosmart", + "model":"BeaconX Pro", + "model_id":"MBXPRO", + "tag":"0708", + "condition":["uuid", "index", 0, "feab"], + "properties":{ + "volt":{ + "condition":["servicedata", 0, "40"], + "decoder":["value_from_hex_data", "servicedata", 6, 4, false], + "post_proc":["/", 1000] + }, + "x_axis":{ + "condition":["servicedata", 0, "60"], + "decoder":["value_from_hex_data", "servicedata", 12, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + }, + "y_axis":{ + "condition":["servicedata", 0, "60"], + "decoder":["value_from_hex_data", "servicedata", 16, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + }, + "z_axis":{ + "condition":["servicedata", 0, "60"], + "decoder":["value_from_hex_data", "servicedata", 20, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + }, + "_volt":{ + "condition":["servicedata", 0, "60"], + "decoder":["value_from_hex_data", "servicedata", 24, 4, false], + "post_proc":["/", 1000] + }, + "tempc":{ + "condition":["servicedata", 0, "70"], + "decoder":["value_from_hex_data", "servicedata", 6, 4, false], + "post_proc":["/", 10] + }, + "hum":{ + "condition":["servicedata", 0, "70"], + "decoder":["value_from_hex_data", "servicedata", 10, 4, false, false], + "post_proc":["/", 10] + }, + "__volt":{ + "condition":["servicedata", 0, "70"], + "decoder":["value_from_hex_data", "servicedata", 14, 4, false], + "post_proc":["/", 1000] + } + } +})"""";*/ + +const char* _MBXPRO_json_props = "{\"properties\":{\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"x_axis\":{\"unit\":\"m/s²\",\"name\":\"x_axis\"},\"y_axis\":{\"unit\":\"m/s²\",\"name\":\"y_axis\"},\"z_axis\":{\"unit\":\"m/s²\",\"name\":\"z_axis\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"}}}"; +/*R""""( +{ + "properties":{ + "volt":{ + "unit":"V", + "name":"voltage" + }, + "x_axis":{ + "unit":"m/s²", + "name":"x_axis" + }, + "y_axis":{ + "unit":"m/s²", + "name":"y_axis" + }, + "z_axis":{ + "unit":"m/s²", + "name":"z_axis" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + } + } +})"""";*/ \ No newline at end of file diff --git a/lib/decoder/src/devices/MS_CDP_json.h b/lib/decoder/src/devices/MS_CDP_json.h new file mode 100644 index 00000000..c1fc2586 --- /dev/null +++ b/lib/decoder/src/devices/MS_CDP_json.h @@ -0,0 +1,25 @@ +const char* _MS_CDP_json = "{\"brand\":\"GENERIC\",\"model\":\"MS-CDP\",\"model_id\":\"MS-CDP\",\"tag\":\"fe\",\"condition\":[\"manufacturerdata\",\"index\",0,\"060001\"],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Microsoft advertising beacon\"]}}}"; +/*R""""( +{ + "brand":"GENERIC", + "model":"MS-CDP", + "model_id":"MS-CDP", + "tag":"fe", + "condition":["manufacturerdata", "index", 0, "060001"], + "properties":{ + "device":{ + "decoder":["static_value", "Microsoft advertising beacon"] + } + } +})"""";*/ + +const char* _MS_CDP_json_props = "{\"properties\":{\"device\":{\"unit\":\"string\",\"name\":\"device type\"}}}"; +/*R""""( +{ + "properties":{ + "device":{ + "unit":"string", + "name":"device type" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/MUE4094RT_json.h b/lib/decoder/src/devices/MUE4094RT_json.h new file mode 100644 index 00000000..fd5252b3 --- /dev/null +++ b/lib/decoder/src/devices/MUE4094RT_json.h @@ -0,0 +1,34 @@ +const char* _MUE4094RT_json = "{\"brand\":\"Xiaomi\",\"model\":\"MiLamp\",\"model_id\":\"MUE4094RT\",\"tag\":\"0404\",\"condition\":[\"servicedata\",\"index\",0,\"4030dd\"],\"properties\":{\"motion\":{\"decoder\":[\"static_value\",true],\"is_bool\":1},\"darkness\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,true]}}}"; +/* +R""""( +{ + "brand":"Xiaomi", + "model":"MiLamp", + "model_id":"MUE4094RT", + "tag":"0404", + "condition":["servicedata", "index", 0, "4030dd"], + "properties":{ + "motion":{ + "decoder":["static_value", true], + "is_bool":1 + }, + "darkness":{ + "decoder":["value_from_hex_data", "servicedata", 8, 2, true] + } + } +})"""";*/ + +const char* _MUE4094RT_json_props = "{\"properties\":{\"motion\":{\"unit\":\"status\",\"name\":\"motion\"},\"darkness\":{\"unit\":\"lx\",\"name\":\"illuminance\"}}}"; +/*R""""( +{ + "properties":{ + "motion":{ + "unit":"status", + "name":"motion" + }, + "darkness":{ + "unit":"lx", + "name":"illuminance" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/Miband_json.h b/lib/decoder/src/devices/Miband_json.h new file mode 100644 index 00000000..59f26d54 --- /dev/null +++ b/lib/decoder/src/devices/Miband_json.h @@ -0,0 +1,49 @@ +const char* _Miband_json = "{\"brand\":\"Xiaomi/Amazfit\",\"model\":\"Mi Band/Smart Watch\",\"model_id\":\"MB/SW\",\"tag\":\"0b0a\",\"condition\":[\"manufacturerdata\",\"=\",52,\"index\",0,\"5701\",\"&\",\"manufacturerdata\",\"mac@index\",40],\"conditionnomac\":[\"uuid\",\"contain\",\"fee0\"],\"properties\":{\"steps\":{\"condition\":[\"servicedata\",\"=\",8],\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,4,true,false]},\"act_bpm\":{\"condition\":[\"manufacturerdata\",0,\"570102\",\"&\",\"manufacturerdata\",10,\"!\",\"f\"],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,2,false,false]},\"device\":{\"decoder\":[\"static_value\",\"Xiaomi/Amazfit Tracker\"]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",40]}}}"; +/*R""""( +{ + "brand":"Xiaomi/Amazfit", + "model":"Mi Band/Smart Watch", + "model_id":"MB/SW", + "tag":"0b0a", + "condition":["manufacturerdata", "=", 52, "index", 0, "5701", "&", "manufacturerdata", "mac@index", 40], + "conditionnomac":["uuid", "contain", "fee0"], + "properties":{ + "steps":{ + "condition":["servicedata", "=", 8], + "decoder":["value_from_hex_data", "servicedata", 0, 4, true, false] + }, + "act_bpm":{ + "condition":["manufacturerdata", 0, "570102", "&", "manufacturerdata", 10, "!", "f"], + "decoder":["value_from_hex_data", "manufacturerdata", 10, 2, false, false] + }, + "device":{ + "decoder":["static_value", "Xiaomi/Amazfit Tracker"] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 40] + } + } +})"""";*/ + +const char* _Miband_json_props = "{\"properties\":{\"steps\":{\"unit\":\"int\",\"name\":\"step-count\"},\"act_bpm\":{\"unit\":\"bpm\",\"name\":\"activity heart rate\"},\"device\":{\"unit\":\"string\",\"name\":\"tracker device\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "steps":{ + "unit":"int", + "name":"step-count" + }, + "act_bpm":{ + "unit":"bpm", + "name":"activity heart rate" + }, + "device":{ + "unit":"string", + "name":"tracker device" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/Mokobeacon_json.h b/lib/decoder/src/devices/Mokobeacon_json.h new file mode 100644 index 00000000..81d32f9f --- /dev/null +++ b/lib/decoder/src/devices/Mokobeacon_json.h @@ -0,0 +1,49 @@ +const char* _Mokobeacon_json = "{\"brand\":\"Mokosmart\",\"model\":\"Beacon\",\"model_id\":\"Mokobeacon\",\"tag\":\"0708\",\"condition\":[\"uuid\",\"index\",0,\"ff01\"],\"properties\":{\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,2,false]},\"x_axis\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"y_axis\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"z_axis\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,false],\"post_proc\":[\"/\",10000,\"*\",9.80665]}}}"; +/*R""""( +{ + "brand":"Mokosmart", + "model":"Beacon", + "model_id":"Mokobeacon", + "tag":"0708", + "condition":["uuid", "index", 0, "ff01"], + "properties":{ + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 0, 2, false] + }, + "x_axis":{ + "decoder":["value_from_hex_data", "servicedata", 14, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + }, + "y_axis":{ + "decoder":["value_from_hex_data", "servicedata", 18, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + }, + "z_axis":{ + "decoder":["value_from_hex_data", "servicedata", 22, 4, false], + "post_proc":["/", 10000, "*", 9.80665] + } + } +})"""";*/ + +const char* _Mokobeacon_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"x_axis\":{\"unit\":\"m/s²\",\"name\":\"x_axis\"},\"y_axis\":{\"unit\":\"m/s²\",\"name\":\"y_axis\"},\"z_axis\":{\"unit\":\"m/s²\",\"name\":\"z_axis\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + }, + "x_axis":{ + "unit":"m/s²", + "name":"x_axis" + }, + "y_axis":{ + "unit":"m/s²", + "name":"y_axis" + }, + "z_axis":{ + "unit":"m/s²", + "name":"z_axis" + } + } +})"""";*/ \ No newline at end of file diff --git a/lib/decoder/src/devices/Mopeka_json.h b/lib/decoder/src/devices/Mopeka_json.h new file mode 100644 index 00000000..50b4d5fa --- /dev/null +++ b/lib/decoder/src/devices/Mopeka_json.h @@ -0,0 +1,91 @@ +const char* _Mopeka_json = "{\"brand\":\"Mopeka/Lippert\",\"model\":\"Pro Check (Universal)/BottleCheck Sensor\",\"model_id\":\"M1017\",\"tag\":\"ff01\",\"condition\":[\"manufacturerdata\",\"=\",24,\"index\",0,\"590003\",\"|\",\"manufacturerdata\",\"=\",24,\"index\",0,\"590006\",\"|\",\"manufacturerdata\",\"=\",24,\"index\",0,\"59000c\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,2,false,true],\"post_proc\":[\"&\",127,\"-\",40,\"min\",-40]},\".cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,2,false,true],\"post_proc\":[\"&\",127]},\"_.cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,2,false,true],\"post_proc\":[\"&\",127,\"*\",\".cal\",\"*\",-0.00000535]},\"__.cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,2,false,true],\"post_proc\":[\"&\",127,\"*\",-0.002822,\"+\",0.573045,\"+\",\".cal\"]},\"lvl_cm\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,4,true,false],\"post_proc\":[\"&\",16383,\"*\",\".cal\",\"/\",10]},\"sync\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",8,3,false,true]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false],\"post_proc\":[\"&\",127,\"/\",32]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false],\"post_proc\":[\"&\",127,\"/\",32,\"-\",2.2,\"/\",0.65,\"*\",100,\"max\",100,\"min\",0]},\"quality\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,2,false,false],\"post_proc\":[\">\",6,\"max\",3,\"min\",0]},\"accx\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,2,false,true]},\"accy\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",22,2,false,true]}}}"; +/* R""""( +{ + "brand":"Mopeka/Lippert", + "model":"Pro Check (Universal)/BottleCheck Sensor", + "model_id":"M1017", + "tag":"ff01", + "condition":["manufacturerdata", "=", 24, "index", 0, "590003", "|", "manufacturerdata", "=", 24, "index", 0, "590006", "|", "manufacturerdata", "=", 24, "index", 0, "59000c"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 2, false, true], + "post_proc":["&", 127, "-", 40, "min", -40] + }, + ".cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 2, false, true], + "post_proc":["&", 127] + }, + "_.cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 2, false, true], + "post_proc":["&", 127, "*", ".cal", "*", -0.00000535] + }, + "__.cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 2, false, true], + "post_proc":["&", 127, "*", -0.002822, "+", 0.573045, "+", ".cal"] + }, + "lvl_cm":{ + "decoder":["value_from_hex_data", "manufacturerdata", 10, 4, true, false], + "post_proc":["&", 16383, "*", ".cal", "/", 10] + }, + "sync":{ + "decoder":["bit_static_value", "manufacturerdata", 8, 3, false, true] + }, + "volt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false], + "post_proc":["&", 127, "/", 32] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false], + "post_proc":["&", 127, "/", 32, "-", 2.2, "/", 0.65, "*", 100, "max", 100, "min", 0] + }, + "quality":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 2, false, false], + "post_proc":[">", 6, "max", 3, "min", 0] + }, + "accx":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 2, false, true] + }, + "accy":{ + "decoder":["value_from_hex_data", "manufacturerdata", 22, 2, false, true] + } + } +})"""";*/ + +const char* _Mopeka_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"lvl_cm\":{\"unit\":\"cm\",\"name\":\"level in cm\"},\"sync\":{\"unit\":\"status\",\"name\":\"sync pressed\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"quality\":{\"unit\":\"status\",\"name\":\"reading quality\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "lvl_cm":{ + "unit":"cm", + "name":"level in cm" + }, + "sync":{ + "unit":"status", + "name":"sync pressed" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "quality":{ + "unit":"status", + "name":"reading quality" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/OralB_json.h b/lib/decoder/src/devices/OralB_json.h new file mode 100644 index 00000000..a7eda7a4 --- /dev/null +++ b/lib/decoder/src/devices/OralB_json.h @@ -0,0 +1,78 @@ +const char* _OralB_json = "{\"brand\":\"Oral-B\",\"model\":\"BT Toothbrush\",\"model_id\":\"ORALB_BT\",\"tag\":\"0b\",\"condition\":[\"manufacturerdata\",\">=\",22,\"index\",0,\"dc00\"],\"properties\":{\"state\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",10,2],\"lookup\":[\"01\",\"initialising\",\"02\",\"idle\",\"03\",\"running\",\"04\",\"charging\",\"73\",\"sleeping\"]},\"mode\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",18,2],\"lookup\":[\"00\",\"off\",\"01\",\"daily clean\",\"02\",\"sensitive\",\"03\",\"massage\",\"04\",\"whitening\",\"05\",\"deep clean\",\"06\",\"tongue cleaning\",\"07\",\"turbo\"]},\"sector\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",20,2],\"lookup\":[\"01\",\"sector 1\",\"02\",\"sector 2\",\"03\",\"sector 3\",\"04\",\"sector 4\",\"05\",\"sector 5\",\"06\",\"sector 6\",\"07\",\"sector 7\",\"08\",\"sector 8\"]},\"pressure\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,2,false,false]},\".cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,2,false,false]},\"time\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,2,false,false],\"post_proc\":[\"*\",60,\"+\",\".cal\"]}}}"; +/*R""""( +{ + "brand":"Oral-B", + "model":"BT Toothbrush", + "model_id":"ORALB_BT", + "tag":"0b", + "condition":["manufacturerdata", ">=", 22, "index", 0, "dc00"], + "properties":{ + "state":{ + "decoder":["string_from_hex_data", "manufacturerdata", 10, 2], + "lookup":["01", "initialising", + "02", "idle", + "03", "running", + "04", "charging", + "73", "sleeping"] + }, + "mode":{ + "decoder":["string_from_hex_data", "manufacturerdata", 18, 2], + "lookup":["00", "off", + "01", "daily clean", + "02", "sensitive", + "03", "massage", + "04", "whitening", + "05", "deep clean", + "06", "tongue cleaning", + "07", "turbo"] + }, + "sector":{ + "decoder":["string_from_hex_data", "manufacturerdata", 20, 2], + "lookup":["01", "sector 1", + "02", "sector 2", + "03", "sector 3", + "04", "sector 4", + "05", "sector 5", + "06", "sector 6", + "07", "sector 7", + "08", "sector 8"] + }, + "pressure":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 2, false, false] + }, + ".cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 2, false, false] + }, + "time":{ + "decoder":["value_from_hex_data", "manufacturerdata", 14, 2, false, false], + "post_proc":["*", 60, "+", ".cal"] + } + } +})"""";*/ + +const char* _OralB_json_props = "{\"properties\":{\"state\":{\"unit\":\"string\",\"name\":\"state\"},\"mode\":{\"unit\":\"string\",\"name\":\"mode\"},\"sector\":{\"unit\":\"string\",\"name\":\"sector\"},\"pressure\":{\"unit\":\"int\",\"name\":\"pressure\"},\"time\":{\"unit\":\"int\",\"name\":\"time\"}}}"; +/*R""""( +{ + "properties":{ + "state":{ + "unit":"string", + "name":"state" + }, + "mode":{ + "unit":"string", + "name":"mode" + }, + "sector":{ + "unit":"string", + "name":"sector" + }, + "pressure":{ + "unit":"int", + "name":"pressure" + }, + "time":{ + "unit":"int", + "name":"time" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/PH10_json.h b/lib/decoder/src/devices/PH10_json.h new file mode 100644 index 00000000..1a9cd196 --- /dev/null +++ b/lib/decoder/src/devices/PH10_json.h @@ -0,0 +1,25 @@ +const char* _PH10_json = "{\"brand\":\"Polar\",\"model\":\"Heart Rate Sensor\",\"model_id\":\"H10\",\"tag\":\"0b00\",\"condition\":[\"manufacturerdata\",\"=\",12,\"index\",0,\"6b00\"],\"properties\":{\"bpm\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,2,false,false]}}}"; +/*R""""( +{ + "brand":"Polar", + "model":"Heart Rate Sensor", + "model_id":"H10", + "tag":"0b00", + "condition":["manufacturerdata", "=", 12, "index", 0, "6b00"], + "properties":{ + "bpm":{ + "decoder":["value_from_hex_data", "manufacturerdata", 10, 2, false, false] + } + } +})"""";*/ + +const char* _PH10_json_props = "{\"properties\":{\"bpm\":{\"unit\":\"bpm\",\"name\":\"heart rate\"}}}"; +/*R""""( +{ + "properties":{ + "bpm":{ + "unit":"bpm", + "name":"heart rate" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/RDL52832_json.h b/lib/decoder/src/devices/RDL52832_json.h new file mode 100644 index 00000000..3c7a59f2 --- /dev/null +++ b/lib/decoder/src/devices/RDL52832_json.h @@ -0,0 +1,154 @@ +const char* _RDL52832_json = "{\"brand\":\"Radioland\",\"model\":\"RDL52832\",\"model_id\":\"RDL52832\",\"tag\":\"070a\",\"condition\":[\"manufacturerdata\",\"=\",50,\"&\",\"name\",\"index\",0,\"RDL52832\"],\"properties\":{\"mfid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",0,4]},\"uuid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",8,32]},\"major\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",40,4,false]},\"minor\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",44,4,false]},\"txpower\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",48,2,false]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,4,false,true],\"post_proc\":[\"/\",256]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,4,false,true],\"post_proc\":[\"/\",256]},\".cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false],\"post_proc\":[\"/\",10]},\"accx\":{\"condition\":[\"servicedata\",8,\"0000\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",9.80665]},\"_accx\":{\"condition\":[\"servicedata\",8,\"0001\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",9.80665]},\"__accx\":{\"condition\":[\"servicedata\",8,\"0100\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",-1,\"*\",9.80665]},\"___accx\":{\"condition\":[\"servicedata\",8,\"0101\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",14,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",-1,\"*\",9.80665]},\"_.cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",20,2,false,false],\"post_proc\":[\"/\",10]},\"accy\":{\"condition\":[\"servicedata\",16,\"0000\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",9.80665]},\"_accy\":{\"condition\":[\"servicedata\",16,\"0001\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",9.80665]},\"__accy\":{\"condition\":[\"servicedata\",16,\"0100\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",-1,\"*\",9.80665]},\"___accy\":{\"condition\":[\"servicedata\",16,\"0101\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",-1,\"*\",9.80665]},\"__.cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",28,2,false,false],\"post_proc\":[\"/\",10]},\"accz\":{\"condition\":[\"servicedata\",24,\"0000\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",9.80665]},\"_accz\":{\"condition\":[\"servicedata\",24,\"0001\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",9.80665]},\"__accz\":{\"condition\":[\"servicedata\",24,\"0100\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"*\",-1,\"*\",9.80665]},\"___accz\":{\"condition\":[\"servicedata\",24,\"0101\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",30,2,false,false],\"post_proc\":[\"/\",100,\"+\",\".cal\",\"+\",1,\"*\",-1,\"*\",9.80665]}}}"; + +/*R""""( +{ + "brand":"Radioland", + "model":"RDL52832", + "model_id":"RDL52832", + "tag":"070a", + "condition":["manufacturerdata", "=", 50, "&", "name", "index", 0, "RDL52832"], + "properties":{ + "mfid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 0, 4] + }, + "uuid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 8, 32] + }, + "major":{ + "decoder":["value_from_hex_data", "manufacturerdata", 40, 4, false] + }, + "minor":{ + "decoder":["value_from_hex_data", "manufacturerdata", 44, 4, false] + }, + "txpower":{ + "decoder":["value_from_hex_data","manufacturerdata", 48, 2, false] + }, + "tempc":{ + "decoder":["value_from_hex_data", "servicedata", 0, 4, false, true], + "post_proc":["/", 256] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 4, 4, false, true], + "post_proc":["/", 256] + }, + ".cal":{ + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false], + "post_proc":["/", 10] + }, + "accx":{ + "condition":["servicedata", 8, "0000"], + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", 9.80665] + }, + "_accx":{ + "condition":["servicedata", 8, "0001"], + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", 9.80665] + }, + "__accx":{ + "condition":["servicedata", 8, "0100"], + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", -1, "*", 9.80665] + }, + "___accx":{ + "condition":["servicedata", 8, "0101"], + "decoder":["value_from_hex_data", "servicedata", 14, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", -1, "*", 9.80665] + }, + "_.cal":{ + "decoder":["value_from_hex_data", "servicedata", 20, 2, false, false], + "post_proc":["/", 10] + }, + "accy":{ + "condition":["servicedata", 16, "0000"], + "decoder":["value_from_hex_data", "servicedata", 22, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", 9.80665] + }, + "_accy":{ + "condition":["servicedata", 16, "0001"], + "decoder":["value_from_hex_data", "servicedata", 22, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", 9.80665] + }, + "__accy":{ + "condition":["servicedata", 16, "0100"], + "decoder":["value_from_hex_data", "servicedata", 22, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", -1, "*", 9.80665] + }, + "___accy":{ + "condition":["servicedata", 16, "0101"], + "decoder":["value_from_hex_data", "servicedata", 22, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", -1, "*", 9.80665] + }, + "__.cal":{ + "decoder":["value_from_hex_data", "servicedata", 28, 2, false, false], + "post_proc":["/", 10] + }, + "accz":{ + "condition":["servicedata", 24, "0000"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", 9.80665] + }, + "_accz":{ + "condition":["servicedata", 24, "0001"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", 9.80665] + }, + "__accz":{ + "condition":["servicedata", 24, "0100"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "*", -1, "*", 9.80665] + }, + "___accz":{ + "condition":["servicedata", 24, "0101"], + "decoder":["value_from_hex_data", "servicedata", 30, 2, false, false], + "post_proc":["/", 100, "+", ".cal", "+", 1, "*", -1, "*", 9.80665] + } + } +})"""";*/ + +const char* _RDL52832_json_props = "{\"properties\":{\"mfid\":{\"unit\":\"hex\",\"name\":\"manufacturer id\"},\"uuid\":{\"unit\":\"hex\",\"name\":\"service uuid\"},\"major\":{\"unit\":\"hex\",\"name\":\"major value\"},\"minor\":{\"unit\":\"hex\",\"name\":\"minor value\"},\"txpower\":{\"unit\":\"dBm\",\"name\":\"tx power @ 1 m\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"},\"accz\":{\"unit\":\"m/s²\",\"name\":\"acceleration z\"}}}"; +/*R""""( +{ + "properties":{ + "mfid":{ + "unit":"hex", + "name":"manufacturer id" + }, + "uuid":{ + "unit":"hex", + "name":"service uuid" + }, + "major":{ + "unit":"hex", + "name":"major value" + }, + "minor":{ + "unit":"hex", + "name":"minor value" + }, + "txpower":{ + "unit":"dBm", + "name":"tx power @ 1 m" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + }, + "accz":{ + "unit":"m/s²", + "name":"acceleration z" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/RuuviTag_RAWv1_json.h b/lib/decoder/src/devices/RuuviTag_RAWv1_json.h new file mode 100644 index 00000000..37c1660d --- /dev/null +++ b/lib/decoder/src/devices/RuuviTag_RAWv1_json.h @@ -0,0 +1,73 @@ +const char* _RuuviTag_RAWv1_json = "{\"brand\":\"Ruuvi\",\"model\":\"RuuviTag\",\"model_id\":\"RuuviTag_RAWv1\",\"tag\":\"0708\",\"condition\":[\"manufacturerdata\",\"=\",32,\"index\",0,\"990403\"],\"properties\":{\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false],\"post_proc\":[\"/\",2]},\"tempc\":{\"decoder\":[\"bf_value_from_hex_data\",\"manufacturerdata\",8,4,false,true]},\"pres\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,4,false,false],\"post_proc\":[\"+\",50000,\"/\",100]},\"accx\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"accy\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"accz\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,false,false],\"post_proc\":[\"/\",1000]}}}"; +/*R""""( +{ + "brand":"Ruuvi", + "model":"RuuviTag", + "model_id":"RuuviTag_RAWv1", + "tag":"0708", + "condition":["manufacturerdata", "=", 32, "index", 0, "990403"], + "properties":{ + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false], + "post_proc":["/", 2] + }, + "tempc":{ + "decoder":["bf_value_from_hex_data", "manufacturerdata", 8, 4, false, true] + }, + "pres":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 4, false, false], + "post_proc":["+", 50000, "/", 100] + }, + "accx":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "accy":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "accz":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "volt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, false, false], + "post_proc":["/", 1000] + } + } +})"""";*/ + +const char* _RuuviTag_RAWv1_json_props = "{\"properties\":{\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"pres\":{\"unit\":\"hPa\",\"name\":\"pressure\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"},\"accz\":{\"unit\":\"m/s²\",\"name\":\"acceleration z\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"}}}"; +/*R""""( +{ + "properties":{ + "hum":{ + "unit":"%", + "name":"humidity" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "pres":{ + "unit":"hPa", + "name":"pressure" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + }, + "accz":{ + "unit":"m/s²", + "name":"acceleration z" + }, + "volt":{ + "unit":"V", + "name":"voltage" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/RuuviTag_RAWv2_json.h b/lib/decoder/src/devices/RuuviTag_RAWv2_json.h new file mode 100644 index 00000000..5e9fa272 --- /dev/null +++ b/lib/decoder/src/devices/RuuviTag_RAWv2_json.h @@ -0,0 +1,103 @@ +const char* _RuuviTag_RAWv2_json = "{\"brand\":\"Ruuvi\",\"model\":\"RuuviTag\",\"model_id\":\"RuuviTag_RAWv2\",\"tag\":\"0708\",\"condition\":[\"manufacturerdata\",\"=\",52,\"index\",0,\"990405\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,4,false,true],\"post_proc\":[\"/\",200]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",10,4,false,false],\"post_proc\":[\"/\",400]},\"pres\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",14,4,false,false],\"post_proc\":[\"+\",50000,\"/\",100]},\"accx\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",18,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"accy\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",22,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"accz\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",26,4,false,true],\"post_proc\":[\"/\",10000,\"*\",9.80665]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",30,4,false,false],\"post_proc\":[\">\",5,\"+\",1600,\"/\",1000]},\"tx\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",30,4,false,false],\"post_proc\":[\"%\",32,\"*\",2,\"-\",40]},\"mov\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",34,2,false,false]},\"seq\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",36,4,false,false]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",40]}}}"; +/*R""""( +{ + "brand":"Ruuvi", + "model":"RuuviTag", + "model_id":"RuuviTag_RAWv2", + "tag":"0708", + "condition":["manufacturerdata", "=", 52, "index", 0, "990405"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 4, false, true], + "post_proc":["/", 200] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 10, 4, false, false], + "post_proc":["/", 400] + }, + "pres":{ + "decoder":["value_from_hex_data", "manufacturerdata", 14, 4, false, false], + "post_proc":["+", 50000, "/", 100] + }, + "accx":{ + "decoder":["value_from_hex_data", "manufacturerdata", 18, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "accy":{ + "decoder":["value_from_hex_data", "manufacturerdata", 22, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "accz":{ + "decoder":["value_from_hex_data", "manufacturerdata", 26, 4, false, true], + "post_proc":["/", 10000, "*", 9.80665] + }, + "volt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 30, 4, false, false], + "post_proc":[">", 5, "+", 1600, "/", 1000] + }, + "tx":{ + "decoder":["value_from_hex_data", "manufacturerdata", 30, 4, false, false], + "post_proc":["%", 32, "*", 2, "-", 40] + }, + "mov":{ + "decoder":["value_from_hex_data", "manufacturerdata", 34, 2, false, false] + }, + "seq":{ + "decoder":["value_from_hex_data", "manufacturerdata", 36, 4, false, false] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 40] + } + } +})"""";*/ + +const char* _RuuviTag_RAWv2_json_props = "{\"properties\":{\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"pres\":{\"unit\":\"hPa\",\"name\":\"pressure\"},\"accx\":{\"unit\":\"m/s²\",\"name\":\"acceleration x\"},\"accy\":{\"unit\":\"m/s²\",\"name\":\"acceleration y\"},\"accz\":{\"unit\":\"m/s²\",\"name\":\"acceleration z\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"tx\":{\"unit\":\"dBm\",\"name\":\"tx power\"},\"mov\":{\"unit\":\"int\",\"name\":\"movement counter\"},\"seq\":{\"unit\":\"int\",\"name\":\"measurement sequence number\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "hum":{ + "unit":"%", + "name":"humidity" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "pres":{ + "unit":"hPa", + "name":"pressure" + }, + "accx":{ + "unit":"m/s²", + "name":"acceleration x" + }, + "accy":{ + "unit":"m/s²", + "name":"acceleration y" + }, + "accz":{ + "unit":"m/s²", + "name":"acceleration z" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "tx":{ + "unit":"dBm", + "name":"tx power" + }, + "mov":{ + "unit":"int", + "name":"movement counter" + }, + "seq":{ + "unit":"int", + "name":"measurement sequence number" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBBT_002C_ENCR_json.h b/lib/decoder/src/devices/SBBT_002C_ENCR_json.h new file mode 100644 index 00000000..c431a9b0 --- /dev/null +++ b/lib/decoder/src/devices/SBBT_002C_ENCR_json.h @@ -0,0 +1,47 @@ +const char* _SBBT_002C_ENCR_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Button1 encrypted\",\"model_id\":\"SBBT_002C_ENCR\",\"tag\":\"110602\",\"condition\":[\"servicedata\",\"index\",0,\"41\",\"|\",\"servicedata\",\"index\",0,\"45\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBBT-002C\"],\"properties\":{\"cipher\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",2,12]},\"ctr\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",14,8]},\"mic\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",22,8]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Button1 encrypted", + "model_id":"SBBT_002C_ENCR", + "tag":"110602", + "condition":["servicedata", "index", 0, "41", "|", "servicedata", "index", 0, "45", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBBT-002C"], + "properties":{ + "cipher":{ + "decoder":["string_from_hex_data", "servicedata", 2, 12] + }, + "ctr":{ + "decoder":["string_from_hex_data", "servicedata", 14, 8] + }, + "mic":{ + "decoder":["string_from_hex_data", "servicedata", 22, 8] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBBT_002C_ENCR_json_props = "{\"properties\":{\"cipher\":{\"unit\":\"hex\",\"name\":\"ciphertext\"},\"ctr\":{\"unit\":\"hex\",\"name\":\"counter\"},\"mic\":{\"unit\":\"hex\",\"name\":\"message integrity check\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "cipher":{ + "unit":"hex", + "name":"ciphertext" + }, + "ctr":{ + "unit":"hex", + "name":"counter" + }, + "mic":{ + "unit":"hex", + "name":"message integrity check" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBBT_002C_json.h b/lib/decoder/src/devices/SBBT_002C_json.h new file mode 100644 index 00000000..21c2296d --- /dev/null +++ b/lib/decoder/src/devices/SBBT_002C_json.h @@ -0,0 +1,50 @@ +const char* _SBBT_002C_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Button1\",\"model_id\":\"SBBT-002C\",\"tag\":\"1106\",\"condition\":[\"servicedata\",\"=\",14,\"index\",0,\"40\",\"|\",\"servicedata\",\"=\",14,\"index\",0,\"44\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBBT-002C\"],\"properties\":{\"packet\":{\"condition\":[\"servicedata\",2,\"00\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false]},\"batt\":{\"condition\":[\"servicedata\",6,\"01\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,false,false]},\"press\":{\"condition\":[\"servicedata\",10,\"3a\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,2,false,false]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Button1", + "model_id":"SBBT-002C", + "tag":"1106", + "condition":["servicedata", "=", 14, "index", 0, "40", "|", "servicedata", "=", 14, "index", 0, "44", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBBT-002C"], + "properties":{ + "packet":{ + "condition":["servicedata", 2, "00"], + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false] + }, + "batt":{ + "condition":["servicedata", 6, "01"], + "decoder":["value_from_hex_data", "servicedata", 8, 2, false, false] + }, + "press":{ + "condition":["servicedata", 10, "3a"], + "decoder":["value_from_hex_data", "servicedata", 12, 2, false, false] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBBT_002C_json_props = "{\"properties\":{\"packet\":{\"unit\":\"int\",\"name\":\"packet id\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"press\":{\"unit\":\"int\",\"name\":\"press type\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "packet":{ + "unit":"int", + "name":"packet id" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "press":{ + "unit":"int", + "name":"press type" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBCS_json.h b/lib/decoder/src/devices/SBCS_json.h new file mode 100644 index 00000000..aa52a316 --- /dev/null +++ b/lib/decoder/src/devices/SBCS_json.h @@ -0,0 +1,83 @@ +const char* _SBCS_json = "{\"brand\":\"SwitchBot\",\"model\":\"Contact Sensor\",\"model_id\":\"W120150X\",\"tag\":\"0406\",\"condition\":[\"uuid\",\"index\",0,\"0d00\",\"|\",\"uuid\",\"index\",0,\"fd3d\",\"&\",\"servicedata\",\"=\",18,\"index\",0,\"64\"],\"properties\":{\"contact\":{\"condition\":[\"servicedata\",7,\"bit\",2,0],\"decoder\":[\"bit_static_value\",\"servicedata\",7,1,\"closed\",\"open\"]},\"_contact\":{\"condition\":[\"servicedata\",7,\"bit\",2,1],\"decoder\":[\"static_value\",\"timeout not closed\"]},\"motion\":{\"decoder\":[\"bit_static_value\",\"servicedata\",2,2,false,true]},\"lightlevel\":{\"decoder\":[\"bit_static_value\",\"servicedata\",7,0,\"dark\",\"bright\"]},\"scopetested\":{\"condition\":[\"servicedata\",2,\"bit\",3,0],\"decoder\":[\"static_value\",false]},\"in_ct\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,1,false,false],\"post_proc\":[\">\",2]},\"out_ct\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",16,1,false,false],\"post_proc\":[\"&\",3]},\"push_ct\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",17,1,false,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Contact Sensor", + "model_id":"W120150X", + "tag":"0406", + "condition":["uuid", "index", 0, "0d00", "|", "uuid", "index", 0, "fd3d", "&", "servicedata", "=", 18, "index", 0, "64"], + "properties":{ + "contact":{ + "condition":["servicedata", 7, "bit", 2, 0], + "decoder":["bit_static_value", "servicedata", 7, 1, "closed", "open"] + }, + "_contact":{ + "condition":["servicedata", 7, "bit", 2, 1], + "decoder":["static_value", "timeout not closed"] + }, + "motion":{ + "decoder":["bit_static_value", "servicedata", 2, 2, false, true] + }, + "lightlevel":{ + "decoder":["bit_static_value", "servicedata", 7, 0, "dark", "bright"] + }, + "scopetested":{ + "condition":["servicedata", 2, "bit", 3, 0], + "decoder":["static_value", false] + }, + "in_ct":{ + "decoder":["value_from_hex_data", "servicedata", 16, 1, false, false], + "post_proc":[">", 2] + }, + "out_ct":{ + "decoder":["value_from_hex_data", "servicedata", 16, 1, false, false], + "post_proc":["&", 3] + }, + "push_ct":{ + "decoder":["value_from_hex_data", "servicedata", 17, 1, false, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + } + } +})"""";*/ + +const char* _SBCS_json_props = "{\"properties\":{\"contact\":{\"unit\":\"string\",\"name\":\"contact\"},\"motion\":{\"unit\":\"status\",\"name\":\"motion\"},\"lightlevel\":{\"unit\":\"string\",\"name\":\"light level\"},\"scopetested\":{\"unit\":\"status\",\"name\":\"scope tested\"},\"in_ct\":{\"unit\":\"int\",\"name\":\"in count\"},\"out_ct\":{\"unit\":\"int\",\"name\":\"out count\"},\"push_ct\":{\"unit\":\"int\",\"name\":\"push count\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "contact":{ + "unit":"string", + "name":"contact" + }, + "motion":{ + "unit":"status", + "name":"motion" + }, + "lightlevel":{ + "unit":"string", + "name":"light level" + }, + "scopetested":{ + "unit":"status", + "name":"scope tested" + }, + "in_ct":{ + "unit":"int", + "name":"in count" + }, + "out_ct":{ + "unit":"int", + "name":"out count" + }, + "push_ct":{ + "unit":"int", + "name":"push count" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBCU_json.h b/lib/decoder/src/devices/SBCU_json.h new file mode 100644 index 00000000..25c70313 --- /dev/null +++ b/lib/decoder/src/devices/SBCU_json.h @@ -0,0 +1,55 @@ +const char* _SBCU_json = "{\"brand\":\"SwitchBot\",\"model\":\"Curtain\",\"model_id\":\"W070160X\",\"tag\":\"0d02\",\"condition\":[\"servicedata\",\"=\",10,\"index\",0,\"63\",\"|\",\"servicedata\",\"=\",12,\"index\",0,\"63\",\"&\",[\"uuid\",\"index\",0,\"0d00\",\"|\",\"uuid\",\"index\",0,\"fd3d\"]],\"properties\":{\"moving\":{\"decoder\":[\"bit_static_value\",\"servicedata\",6,3,false,true]},\"position\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",6,2,false,false],\"post_proc\":[\"&\",127]},\"calibrated\":{\"decoder\":[\"bit_static_value\",\"servicedata\",2,2,false,true]},\"lightlevel\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,1,false,false]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Curtain", + "model_id":"W070160X", + "tag":"0d02", + "condition":["servicedata", "=", 10, "index", 0, "63", "|", "servicedata", "=", 12, "index", 0, "63", "&", ["uuid", "index", 0, "0d00", "|", "uuid", "index", 0, "fd3d"]], + "properties":{ + "moving":{ + "decoder":["bit_static_value", "servicedata", 6, 3, false, true] + }, + "position":{ + "decoder":["value_from_hex_data", "servicedata", 6, 2, false, false], + "post_proc":["&", 127] + }, + "calibrated":{ + "decoder":["bit_static_value", "servicedata", 2, 2, false, true] + }, + "lightlevel":{ + "decoder":["value_from_hex_data", "servicedata", 8, 1, false, false] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + } + } +})"""";*/ + +const char* _SBCU_json_props = "{\"properties\":{\"moving\":{\"unit\":\"status\",\"name\":\"moving\"},\"position\":{\"unit\":\"%\",\"name\":\"position\"},\"calibrated\":{\"unit\":\"status\",\"name\":\"calibrated\"},\"lightlevel\":{\"unit\":\"int\",\"name\":\"light level\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "moving":{ + "unit":"status", + "name":"moving" + }, + "position":{ + "unit":"%", + "name":"position" + }, + "calibrated":{ + "unit":"status", + "name":"calibrated" + }, + "lightlevel":{ + "unit":"int", + "name":"light level" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBDW_002C_ENCR_json.h b/lib/decoder/src/devices/SBDW_002C_ENCR_json.h new file mode 100644 index 00000000..9670077a --- /dev/null +++ b/lib/decoder/src/devices/SBDW_002C_ENCR_json.h @@ -0,0 +1,47 @@ +const char* _SBDW_002C_ENCR_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Door/Window encrypted\",\"model_id\":\"SBDW_002C_ENCR\",\"tag\":\"040602\",\"condition\":[\"servicedata\",\"index\",0,\"45\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBDW-002C\"],\"properties\":{\"cipher\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",2,26]},\"ctr\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",28,8]},\"mic\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",36,8]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Door/Window encrypted", + "model_id":"SBDW_002C_ENCR", + "tag":"040602", + "condition":["servicedata", "index", 0, "45", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBDW-002C"], + "properties":{ + "cipher":{ + "decoder":["string_from_hex_data", "servicedata", 2, 26] + }, + "ctr":{ + "decoder":["string_from_hex_data", "servicedata", 28, 8] + }, + "mic":{ + "decoder":["string_from_hex_data", "servicedata", 36, 8] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBDW_002C_ENCR_json_props = "{\"properties\":{\"cipher\":{\"unit\":\"hex\",\"name\":\"ciphertext\"},\"ctr\":{\"unit\":\"hex\",\"name\":\"counter\"},\"mic\":{\"unit\":\"hex\",\"name\":\"message integrity check\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "cipher":{ + "unit":"hex", + "name":"ciphertext" + }, + "ctr":{ + "unit":"hex", + "name":"counter" + }, + "mic":{ + "unit":"hex", + "name":"message integrity check" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBDW_002C_json.h b/lib/decoder/src/devices/SBDW_002C_json.h new file mode 100644 index 00000000..e7a8fdbd --- /dev/null +++ b/lib/decoder/src/devices/SBDW_002C_json.h @@ -0,0 +1,68 @@ +const char* _SBDW_002C_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Door/Window\",\"model_id\":\"SBDW-002C\",\"tag\":\"0406\",\"condition\":[\"servicedata\",\"=\",28,\"index\",0,\"44\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBDW-002C\"],\"properties\":{\"packet\":{\"condition\":[\"servicedata\",2,\"00\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false]},\"batt\":{\"condition\":[\"servicedata\",6,\"01\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,false,false]},\"lux\":{\"condition\":[\"servicedata\",10,\"05\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,6,true,false],\"post_proc\":[\"/\",100]},\"open\":{\"condition\":[\"servicedata\",18,\"2d\"],\"decoder\":[\"bit_static_value\",\"servicedata\",21,0,false,true]},\"rot\":{\"condition\":[\"servicedata\",22,\"3f\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",24,4,true,true],\"post_proc\":[\"/\",10]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Door/Window", + "model_id":"SBDW-002C", + "tag":"0406", + "condition":["servicedata", "=", 28, "index", 0, "44", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBDW-002C"], + "properties":{ + "packet":{ + "condition":["servicedata", 2, "00"], + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false] + }, + "batt":{ + "condition":["servicedata", 6, "01"], + "decoder":["value_from_hex_data", "servicedata", 8, 2, false, false] + }, + "lux":{ + "condition":["servicedata", 10, "05"], + "decoder":["value_from_hex_data", "servicedata", 12, 6, true, false], + "post_proc":["/", 100] + }, + "open":{ + "condition":["servicedata", 18, "2d"], + "decoder":["bit_static_value", "servicedata", 21, 0, false, true] + }, + "rot":{ + "condition":["servicedata", 22, "3f"], + "decoder":["value_from_hex_data", "servicedata", 24, 4, true, true], + "post_proc":["/", 10] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBDW_002C_json_props = "{\"properties\":{\"packet\":{\"unit\":\"int\",\"name\":\"packet id\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"lux\":{\"unit\":\"lux\",\"name\":\"illuminance\"},\"open\":{\"unit\":\"status\",\"name\":\"door\"},\"rot\":{\"unit\":\"0\",\"name\":\"rotation\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "packet":{ + "unit":"int", + "name":"packet id" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "lux":{ + "unit":"lux", + "name":"illuminance" + }, + "open":{ + "unit":"status", + "name":"door" + }, + "rot":{ + "unit":"0", + "name":"rotation" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBMO_003Z_ENCR_json.h b/lib/decoder/src/devices/SBMO_003Z_ENCR_json.h new file mode 100644 index 00000000..57e546bd --- /dev/null +++ b/lib/decoder/src/devices/SBMO_003Z_ENCR_json.h @@ -0,0 +1,47 @@ +const char* _SBMO_003Z_ENCR_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Motion encrypted\",\"model_id\":\"SBMO_003Z_ENCR\",\"tag\":\"040602\",\"condition\":[\"servicedata\",\"index\",0,\"45\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBMO-003Z\"],\"properties\":{\"cipher\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",2,20]},\"ctr\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",22,8]},\"mic\":{\"decoder\":[\"string_from_hex_data\",\"servicedata\",30,8]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Motion encrypted", + "model_id":"SBMO_003Z_ENCR", + "tag":"040602", + "condition":["servicedata", "index", 0, "45", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBMO-003Z"], + "properties":{ + "cipher":{ + "decoder":["string_from_hex_data", "servicedata", 2, 20] + }, + "ctr":{ + "decoder":["string_from_hex_data", "servicedata", 22, 8] + }, + "mic":{ + "decoder":["string_from_hex_data", "servicedata", 30, 8] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBMO_003Z_ENCR_json_props = "{\"properties\":{\"cipher\":{\"unit\":\"hex\",\"name\":\"ciphertext\"},\"ctr\":{\"unit\":\"hex\",\"name\":\"counter\"},\"mic\":{\"unit\":\"hex\",\"name\":\"message integrity check\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "cipher":{ + "unit":"hex", + "name":"ciphertext" + }, + "ctr":{ + "unit":"hex", + "name":"counter" + }, + "mic":{ + "unit":"hex", + "name":"message integrity check" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBMO_003Z_json.h b/lib/decoder/src/devices/SBMO_003Z_json.h new file mode 100644 index 00000000..6fe7c16c --- /dev/null +++ b/lib/decoder/src/devices/SBMO_003Z_json.h @@ -0,0 +1,59 @@ +const char* _SBMO_003Z_json = "{\"brand\":\"Shelly\",\"model\":\"ShellyBLU Motion\",\"model_id\":\"SBMO-003Z\",\"tag\":\"0406\",\"condition\":[\"servicedata\",\"=\",22,\"index\",0,\"44\",\"&\",\"uuid\",\"index\",0,\"fcd2\",\"&\",\"name\",\"index\",0,\"SBMO-003Z\"],\"properties\":{\"packet\":{\"condition\":[\"servicedata\",2,\"00\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false]},\"batt\":{\"condition\":[\"servicedata\",6,\"01\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,false,false]},\"lux\":{\"condition\":[\"servicedata\",10,\"05\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",12,6,true,false],\"post_proc\":[\"/\",100]},\"motion\":{\"condition\":[\"servicedata\",18,\"21\"],\"decoder\":[\"bit_static_value\",\"servicedata\",21,0,false,true]},\"mac\":{\"condition\":[\"manufacturerdata\",\"=\",30],\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",18]}}}"; +/*R""""( +{ + "brand":"Shelly", + "model":"ShellyBLU Motion", + "model_id":"SBMO-003Z", + "tag":"0406", + "condition":["servicedata", "=", 22, "index", 0, "44", "&", "uuid", "index", 0, "fcd2", "&", "name", "index", 0, "SBMO-003Z"], + "properties":{ + "packet":{ + "condition":["servicedata", 2, "00"], + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false] + }, + "batt":{ + "condition":["servicedata", 6, "01"], + "decoder":["value_from_hex_data", "servicedata", 8, 2, false, false] + }, + "lux":{ + "condition":["servicedata", 10, "05"], + "decoder":["value_from_hex_data", "servicedata", 12, 6, true, false], + "post_proc":["/", 100] + }, + "motion":{ + "condition":["servicedata", 18, "21"], + "decoder":["bit_static_value", "servicedata", 21, 0, false, true] + }, + "mac":{ + "condition":["manufacturerdata", "=", 30], + "decoder":["revmac_from_hex_data", "manufacturerdata", 18] + } + } +})"""";*/ + +const char* _SBMO_003Z_json_props = "{\"properties\":{\"packet\":{\"unit\":\"int\",\"name\":\"packet id\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"lux\":{\"unit\":\"lux\",\"name\":\"illuminance\"},\"motion\":{\"unit\":\"status\",\"name\":\"motion\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "packet":{ + "unit":"int", + "name":"packet id" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "lux":{ + "unit":"lux", + "name":"illuminance" + }, + "motion":{ + "unit":"status", + "name":"motion" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBMS_json.h b/lib/decoder/src/devices/SBMS_json.h new file mode 100644 index 00000000..f3ca4391 --- /dev/null +++ b/lib/decoder/src/devices/SBMS_json.h @@ -0,0 +1,71 @@ +const char* _SBMS_json = "{\"brand\":\"SwitchBot\",\"model\":\"Motion Sensor\",\"model_id\":\"W110150X\",\"tag\":\"0406\",\"condition\":[\"uuid\",\"index\",0,\"0d00\",\"|\",\"uuid\",\"index\",0,\"fd3d\",\"&\",\"servicedata\",\"=\",12,\"index\",0,\"73\"],\"properties\":{\"motion\":{\"decoder\":[\"bit_static_value\",\"servicedata\",2,2,false,true]},\"led\":{\"decoder\":[\"bit_static_value\",\"servicedata\",10,1,false,true]},\"scopetested\":{\"condition\":[\"servicedata\",2,\"bit\",3,0],\"decoder\":[\"static_value\",false]},\"sensingdistance\":{\"condition\":[\"servicedata\",11,\"bit\",3,0,\"&\",\"servicedata\",11,\"bit\",2,0],\"decoder\":[\"static_value\",\"long\"]},\"_sensingdistance\":{\"condition\":[\"servicedata\",11,\"bit\",3,0,\"&\",\"servicedata\",11,\"bit\",2,1],\"decoder\":[\"static_value\",\"middle\"]},\"__sensingdistance\":{\"condition\":[\"servicedata\",11,\"bit\",3,1,\"&\",\"servicedata\",11,\"bit\",2,0],\"decoder\":[\"static_value\",\"short\"]},\"lightlevel\":{\"decoder\":[\"bit_static_value\",\"servicedata\",11,1,\"dark\",\"bright\"]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Motion Sensor", + "model_id":"W110150X", + "tag":"0406", + "condition":["uuid", "index", 0, "0d00", "|", "uuid", "index", 0, "fd3d", "&", "servicedata", "=", 12, "index", 0, "73"], + "properties":{ + "motion":{ + "decoder":["bit_static_value", "servicedata", 2, 2, false, true] + }, + "led":{ + "decoder":["bit_static_value", "servicedata", 10, 1, false, true] + }, + "scopetested":{ + "condition":["servicedata", 2, "bit", 3, 0], + "decoder":["static_value", false] + }, + "sensingdistance":{ + "condition":["servicedata", 11, "bit", 3, 0, "&","servicedata", 11, "bit", 2, 0], + "decoder":["static_value", "long"] + }, + "_sensingdistance":{ + "condition":["servicedata", 11, "bit", 3, 0, "&","servicedata", 11, "bit", 2, 1], + "decoder":["static_value", "middle"] + }, + "__sensingdistance":{ + "condition":["servicedata", 11, "bit", 3, 1, "&","servicedata", 11, "bit", 2, 0], + "decoder":["static_value", "short"] + }, + "lightlevel":{ + "decoder":["bit_static_value", "servicedata", 11, 1, "dark", "bright"] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + } + } +})"""";*/ + +const char* _SBMS_json_props = "{\"properties\":{\"motion\":{\"unit\":\"status\",\"name\":\"motion\"},\"led\":{\"unit\":\"status\",\"name\":\"LED\"},\"scopetested\":{\"unit\":\"status\",\"name\":\"scope tested\"},\"sensingdistance\":{\"unit\":\"string\",\"name\":\"sensing distance\"},\"lightlevel\":{\"unit\":\"string\",\"name\":\"light level\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "motion":{ + "unit":"status", + "name":"motion" + }, + "led":{ + "unit":"status", + "name":"LED" + }, + "scopetested":{ + "unit":"status", + "name":"scope tested" + }, + "sensingdistance":{ + "unit":"string", + "name":"sensing distance" + }, + "lightlevel":{ + "unit":"string", + "name":"light level" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SBMT_json.h b/lib/decoder/src/devices/SBMT_json.h new file mode 100644 index 00000000..440375f4 --- /dev/null +++ b/lib/decoder/src/devices/SBMT_json.h @@ -0,0 +1,37 @@ +#include "common_props.h" + +const char* _SBMT_json = "{\"brand\":\"SwitchBot\",\"model\":\"Meter (Plus)\",\"model_id\":\"THX1/W230150X\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",12,\"index\",0,\"54\",\"|\",\"servicedata\",\"=\",12,\"index\",0,\"69\",\"&\",[\"uuid\",\"index\",0,\"0d00\",\"|\",\"uuid\",\"index\",0,\"fd3d\"]],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",7,1,false,false],\"post_proc\":[\"/\",10]},\"tempc\":{\"condition\":[\"servicedata\",8,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,true,false],\"post_proc\":[\"+\",\".cal\",\"*\",-1]},\"_tempc\":{\"condition\":[\"servicedata\",8,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"servicedata\",8,2,true,false],\"post_proc\":[\"+\",\".cal\",\"-\",128]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",10,2,false,false],\"post_proc\":[\"&\",127]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Meter (Plus)", + "model_id":"THX1/W230150X", + "tag":"0102", + "condition":["servicedata", "=", 12, "index", 0, "54", "|", "servicedata", "=", 12, "index", 0, "69", "&", ["uuid", "index", 0, "0d00", "|", "uuid", "index", 0, "fd3d"]], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "servicedata", 7, 1, false, false], + "post_proc":["/", 10] + }, + "tempc":{ + "condition":["servicedata", 8, "bit", 3, 0], + "decoder":["value_from_hex_data", "servicedata", 8, 2, true, false], + "post_proc":["+", ".cal", "*", -1] + }, + "_tempc":{ + "condition":["servicedata", 8, "bit", 3, 1], + "decoder":["value_from_hex_data", "servicedata", 8, 2, true, false], + "post_proc":["+", ".cal", "-", 128] + }, + "hum":{ + "decoder":["value_from_hex_data", "servicedata", 10, 2, false, false], + "post_proc":["&", 127] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + } + } +})"""";*/ + +const char* _SBMT_json_props = _common_BTH_props; diff --git a/lib/decoder/src/devices/SBOT_json.h b/lib/decoder/src/devices/SBOT_json.h new file mode 100644 index 00000000..6f15e7b5 --- /dev/null +++ b/lib/decoder/src/devices/SBOT_json.h @@ -0,0 +1,40 @@ +#include "common_props.h" + +const char* _SBOT_json = "{\"brand\":\"SwitchBot\",\"model\":\"Outdoor Meter\",\"model_id\":\"W340001X\",\"tag\":\"0102\",\"condition\":[\"servicedata\",\"=\",6,\"index\",0,\"77\",\"&\",\"uuid\",\"index\",0,\"fd3d\",\"&\",\"manufacturerdata\",\"=\",28],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",21,1,false,false],\"post_proc\":[\"/\",10]},\"tempc\":{\"condition\":[\"manufacturerdata\",22,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",22,2,true,false],\"post_proc\":[\"+\",\".cal\",\"*\",-1]},\"_tempc\":{\"condition\":[\"manufacturerdata\",22,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",22,2,true,false],\"post_proc\":[\"+\",\".cal\",\"-\",128]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,2,false,false],\"post_proc\":[\"&\",127]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",4]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Outdoor Meter", + "model_id":"W340001X", + "tag":"0102", + "condition":["servicedata", "=", 6, "index", 0, "77", "&", "uuid", "index", 0, "fd3d", "&", "manufacturerdata", "=", 28], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 21, 1, false, false], + "post_proc":["/", 10] + }, + "tempc":{ + "condition":["manufacturerdata", 22, "bit", 3, 0], + "decoder":["value_from_hex_data", "manufacturerdata", 22, 2, true, false], + "post_proc":["+", ".cal", "*", -1] + }, + "_tempc":{ + "condition":["manufacturerdata", 22, "bit", 3, 1], + "decoder":["value_from_hex_data", "manufacturerdata", 22, 2, true, false], + "post_proc":["+", ".cal", "-", 128] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 2, false, false], + "post_proc":["&", 127] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 4] + } + } +})"""";*/ + +const char* _SBOT_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/SBS1_json.h b/lib/decoder/src/devices/SBS1_json.h new file mode 100644 index 00000000..fadeac46 --- /dev/null +++ b/lib/decoder/src/devices/SBS1_json.h @@ -0,0 +1,40 @@ +const char* _SBS1_json = "{\"brand\":\"SwitchBot\",\"model\":\"Bot\",\"model_id\":\"X1\",\"tag\":\"0e02\",\"condition\":[\"uuid\",\"index\",0,\"0d00\",\"|\",\"uuid\",\"index\",0,\"fd3d\",\"&\",\"servicedata\",\"=\",6,\"index\",0,\"48\"],\"properties\":{\"mode\":{\"decoder\":[\"bit_static_value\",\"servicedata\",2,3,\"onestate\",\"on/off\"]},\"state\":{\"decoder\":[\"bit_static_value\",\"servicedata\",2,2,\"on\",\"off\"]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",4,2,false,false],\"post_proc\":[\"&\",127]}}}"; +/*R""""( +{ + "brand":"SwitchBot", + "model":"Bot", + "model_id":"X1", + "tag":"0e02", + "condition":["uuid", "index", 0, "0d00", "|", "uuid", "index", 0, "fd3d", "&", "servicedata", "=", 6, "index", 0, "48"], + "properties":{ + "mode":{ + "decoder":["bit_static_value", "servicedata", 2, 3, "onestate", "on/off"] + }, + "state":{ + "decoder":["bit_static_value", "servicedata", 2, 2, "on", "off"] + }, + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 4, 2, false, false], + "post_proc":["&", 127] + } + } +})"""";*/ + +const char* _SBS1_json_props = "{\"properties\":{\"mode\":{\"unit\":\"string\",\"name\":\"mode\"},\"state\":{\"unit\":\"string\",\"name\":\"state\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "mode":{ + "unit":"string", + "name":"mode" + }, + "state":{ + "unit":"string", + "name":"state" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ \ No newline at end of file diff --git a/lib/decoder/src/devices/SCD4X_json.h b/lib/decoder/src/devices/SCD4X_json.h new file mode 100644 index 00000000..d1921a0b --- /dev/null +++ b/lib/decoder/src/devices/SCD4X_json.h @@ -0,0 +1,43 @@ +const char* _SCD4X_json = "{\"brand\":\"Sensirion\",\"model\":\"MyCO₂/CO₂ Gadget\",\"model_id\":\"SCD4X\",\"tag\":\"0f\",\"condition\":[\"manufacturerdata\",\">=\",24,\"index\",0,\"d5060008\",\"|\",\"manufacturerdata\",\">=\",24,\"index\",0,\"d506000a\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,4,true,true],\"post_proc\":[\"*\",175,\"/\",65535,\"-\",45]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,true,false],\"post_proc\":[\"*\",100,\"/\",65535]},\"co2\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false]}}}"; + +/* R""""( +{ + "brand":"Sensirion", + "model":"MyCO₂/CO₂ Gadget", + "model_id":"SCD4X", + "tag":"0f", + "condition":["manufacturerdata", ">=", 24, "index", 0, "d5060008", "|", "manufacturerdata", ">=", 24, "index", 0, "d506000a"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 4, true, true], + "post_proc":["*", 175, "/", 65535, "-", 45] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, true, false], + "post_proc":["*", 100, "/", 65535] + }, + "co2":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false] + } + } +})"""";*/ + +const char* _SCD4X_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"co2\":{\"unit\":\"ppm\",\"name\":\"carbon dioxide\"}}}"; + +/* R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "co2":{ + "unit":"ppm", + "name":"carbon dioxide" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SHT4X_json.h b/lib/decoder/src/devices/SHT4X_json.h new file mode 100644 index 00000000..c6eed65a --- /dev/null +++ b/lib/decoder/src/devices/SHT4X_json.h @@ -0,0 +1,24 @@ +#include "common_props.h" + +const char* _SHT4X_json = "{\"brand\":\"Sensirion\",\"model\":\"TH Sensor\",\"model_id\":\"SHT4X\",\"tag\":\"01\",\"condition\":[\"manufacturerdata\",\">=\",20,\"index\",0,\"d5060006\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,4,true,true],\"post_proc\":[\"*\",175,\"/\",65535,\"-\",45]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,true,false],\"post_proc\":[\"*\",125,\"/\",65535,\"-\",6]}}}"; + +/* R""""( +{ + "brand":"Sensirion", + "model":"TH Sensor", + "model_id":"SHT4X", + "tag":"01", + "condition":["manufacturerdata", ">=", 20, "index", 0, "d5060006"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 4, true, true], + "post_proc":["*", 175, "/", 65535, "-", 45] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, true, false], + "post_proc":["*", 125, "/", 65535, "-", 6] + } + } +})"""";*/ + +const char* _SHT4X_json_props = _common_TH_props; diff --git a/lib/decoder/src/devices/ServiceData_json.h b/lib/decoder/src/devices/ServiceData_json.h new file mode 100644 index 00000000..a2cd434c --- /dev/null +++ b/lib/decoder/src/devices/ServiceData_json.h @@ -0,0 +1,25 @@ +const char* _ServiceData_json = "{\"brand\":\"GENERIC\",\"model\":\"Service data\",\"model_id\":\"ServiceData\",\"tag\":\"08\",\"condition\":[\"uuid\",\"index\",0,\"180f\"],\"properties\":{\"batt\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",0,2,false,false]}}}"; +/*R""""( +{ + "brand":"GENERIC", + "model":"Service data", + "model_id":"ServiceData", + "tag":"08", + "condition":["uuid", "index", 0, "180f"], + "properties":{ + "batt":{ + "decoder":["value_from_hex_data", "servicedata", 0, 2, false, false] + } + } +})"""";*/ + +const char* _ServiceData_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/Skale_json.h b/lib/decoder/src/devices/Skale_json.h new file mode 100644 index 00000000..5603f42e --- /dev/null +++ b/lib/decoder/src/devices/Skale_json.h @@ -0,0 +1,26 @@ +const char* _Skale_json = "{\"brand\":\"Atomax\",\"model\":\"Skale I/II\",\"model_id\":\"SKALE\",\"tag\":\"0501\",\"condition\":[\"manufacturerdata\",\"=\",12,\"index\",0,\"ef81\"],\"properties\":{\"weight\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",4,4,true,true],\"post_proc\":[\"/\",10]}}}"; +/*R""""( +{ + "brand":"Atomax", + "model":"Skale I/II", + "model_id":"SKALE", + "tag":"0501", + "condition":["manufacturerdata", "=", 12, "index", 0, "ef81"], + "properties":{ + "weight":{ + "decoder":["value_from_hex_data", "manufacturerdata", 4, 4, true, true], + "post_proc":["/", 10] + } + } +})"""";*/ + +const char* _Skale_json_props = "{\"properties\":{\"weight\":{\"unit\":\"g\",\"name\":\"weight\"}}}"; +/*R""""( +{ + "properties":{ + "weight":{ + "unit":"g", + "name":"weight" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/SmartDry_json.h b/lib/decoder/src/devices/SmartDry_json.h new file mode 100644 index 00000000..ed3a4e70 --- /dev/null +++ b/lib/decoder/src/devices/SmartDry_json.h @@ -0,0 +1,54 @@ +const char* _SmartDry_json = "{\"brand\":\"SmartDry\",\"model\":\"Laundry Sensor\",\"model_id\":\"SDLS\",\"tag\":\"ff01\",\"condition\":[\"manufacturerdata\",\"=\",28,\"index\",0,\"ae01\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",4,8,true,false,true]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",12,8,true,false,true]},\"shake\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true,false]},\"volt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,2,false,false],\"post_proc\":[\"+\",\"2847\",\"/\",1000]},\"wake\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",27,0,false,true]}}}"; +/* R""""( +{ + "brand":"SmartDry", + "model":"Laundry Sensor", + "model_id":"SDLS", + "tag":"ff01", + "condition":["manufacturerdata", "=", 28, "index", 0, "ae01"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 4, 8, true, false, true] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 12, 8, true, false, true] + }, + "shake":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true, false] + }, + "volt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 2, false, false], + "post_proc":["+", "2847", "/", 1000] + }, + "wake":{ + "decoder":["bit_static_value", "manufacturerdata", 27, 0, false, true] + } + } +})"""";*/ + +const char* _SmartDry_json_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"shake\":{\"unit\":\"int\",\"name\":\"shake\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"wake\":{\"unit\":\"status\",\"name\":\"wake\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "shake":{ + "unit":"int", + "name":"shake" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "wake":{ + "unit":"status", + "name":"wake" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/T201_json.h b/lib/decoder/src/devices/T201_json.h new file mode 100644 index 00000000..08fbb747 --- /dev/null +++ b/lib/decoder/src/devices/T201_json.h @@ -0,0 +1,29 @@ +#include "common_props.h" + +const char* _T201_json = "{\"brand\":\"Oria\",\"model\":\"TH Sensor\",\"model_id\":\"T201\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"T201\",\"&\",\"manufacturerdata\",\">=\",38],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,false,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,false,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,2,false,false]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Oria", + "model":"TH Sensor", + "model_id":"T201", + "tag":"0103", + "condition":["name", "index", 0, "T201", "&", "manufacturerdata", ">=", 38], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, false, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, false, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 32, 2, false, false] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _T201_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/T301_json.h b/lib/decoder/src/devices/T301_json.h new file mode 100644 index 00000000..10539a23 --- /dev/null +++ b/lib/decoder/src/devices/T301_json.h @@ -0,0 +1,29 @@ +#include "common_props.h" + +const char* _T301_json = "{\"brand\":\"Oria\",\"model\":\"TH Sensor\",\"model_id\":\"T301\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"T301\",\"&\",\"manufacturerdata\",\"=\",38],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,false,true],\"post_proc\":[\"/\",100]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,false,false],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,2,false,false]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"Oria", + "model":"TH Sensor", + "model_id":"T301", + "tag":"0103", + "condition":["name", "index", 0, "T301", "&", "manufacturerdata", "=", 38], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, false, true], + "post_proc":["/", 100] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, false, false], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 32, 2, false, false] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _T301_json_props = _common_BTHM_props; diff --git a/lib/decoder/src/devices/TPMS_json.h b/lib/decoder/src/devices/TPMS_json.h new file mode 100644 index 00000000..b177b819 --- /dev/null +++ b/lib/decoder/src/devices/TPMS_json.h @@ -0,0 +1,64 @@ +const char* _TPMS_json = "{\"brand\":\"GENERIC\",\"model\":\"TPMS\",\"model_id\":\"TPMS\",\"tag\":\"0a01\",\"condition\":[\"manufacturerdata\",\"=\",36,\"index\",0,\"000\",\"&\",\"manufacturerdata\",\"mac@index\",4],\"conditionnomac\":[\"manufacturerdata\",\"=\",36,\"&\",\"name\",\"index\",0,\"TPMS\"],\"properties\":{\"count\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",5,1,false],\"post_proc\":[\"+\",1]},\"pres\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,8,true],\"post_proc\":[\"/\",100000]},\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,8,true],\"post_proc\":[\"/\",100]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,2,true]},\"alarm\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",35,0,false,true]},\"mac\":{\"decoder\":[\"mac_from_hex_data\",\"manufacturerdata\",4]}}}"; +/*R""""( +{ + "brand":"GENERIC", + "model":"TPMS", + "model_id":"TPMS", + "tag":"0a01", + "condition":["manufacturerdata", "=", 36, "index", 0, "000", "&", "manufacturerdata", "mac@index", 4], + "conditionnomac":["manufacturerdata", "=", 36, "&", "name", "index", 0, "TPMS"], + "properties":{ + "count":{ + "decoder":["value_from_hex_data", "manufacturerdata", 5, 1, false], + "post_proc":["+", 1] + }, + "pres":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 8, true], + "post_proc":["/", 100000] + }, + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 24, 8, true], + "post_proc":["/", 100] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 32, 2, true] + }, + "alarm":{ + "decoder":["bit_static_value", "manufacturerdata", 35, 0, false, true] + }, + "mac":{ + "decoder":["mac_from_hex_data", "manufacturerdata", 4] + } + } +})"""";*/ + +const char* _TPMS_json_props = "{\"properties\":{\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"pres\":{\"unit\":\"bar\",\"name\":\"pressure\"},\"count\":{\"unit\":\"int\",\"name\":\"count\"},\"alarm\":{\"unit\":\"status\",\"name\":\"alarm\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "batt":{ + "unit":"%", + "name":"battery" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "pres":{ + "unit":"bar", + "name":"pressure" + }, + "count":{ + "unit":"int", + "name":"count" + }, + "alarm":{ + "unit":"status", + "name":"alarm" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/TPTH_json.h b/lib/decoder/src/devices/TPTH_json.h new file mode 100644 index 00000000..8bc38c24 --- /dev/null +++ b/lib/decoder/src/devices/TPTH_json.h @@ -0,0 +1,22 @@ +#include "common_props.h" + +const char* _TPTH_json = "{\"brand\":\"ThermoPro\",\"model\":\"TH Sensor\",\"model_id\":\"TP35X/393\",\"tag\":\"0103\",\"condition\":[\"name\",\"index\",0,\"TP357\",\"|\",\"name\",\"index\",0,\"TP358\",\"|\",\"name\",\"index\",0,\"TP359\",\"|\",\"name\",\"index\",0,\"TP393\",\"&\",\"manufacturerdata\",\">=\",12,\"index\",0,\"c2\"],\"properties\":{\"tempc\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",2,4,true,true],\"post_proc\":[\"/\",10]},\"hum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",6,2,false,false]}}}"; +/*R""""( +{ + "brand":"ThermoPro", + "model":"TH Sensor", + "model_id":"TP35X/393", + "tag":"0103", + "condition":["name", "index", 0, "TP357", "|", "name", "index", 0, "TP358", "|", "name", "index", 0, "TP359", "|", "name", "index", 0, "TP393", "&", "manufacturerdata", ">=", 12, "index", 0, "c2"], + "properties":{ + "tempc":{ + "decoder":["value_from_hex_data", "manufacturerdata", 2, 4, true, true], + "post_proc":["/", 10] + }, + "hum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 6, 2, false, false] + } + } +})"""";*/ + +const char* _TPTH_json_props = _common_TH_props; diff --git a/lib/decoder/src/devices/ThermoBeacon_json.h b/lib/decoder/src/devices/ThermoBeacon_json.h new file mode 100644 index 00000000..b060dcd9 --- /dev/null +++ b/lib/decoder/src/devices/ThermoBeacon_json.h @@ -0,0 +1,94 @@ +const char* _ThermoBeacon_json = "{\"brand\":\"GENERIC\",\"model\":\"ThermoBeacon\",\"model_id\":\"WS02/WS08\",\"tag\":\"0101\",\"condition\":[\"manufacturerdata\",\"index\",0,\"1000\",\"|\",\"manufacturerdata\",\"index\",0,\"1100\",\"|\",\"manufacturerdata\",\"index\",0,\"1500\",\"|\",\"manufacturerdata\",\"index\",0,\"1800\",\"|\",\"manufacturerdata\",\"index\",0,\"1b00\",\"&\",\"manufacturerdata\",\">=\",40],\"properties\":{\"tempc\":{\"condition\":[\"manufacturerdata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,4,true],\"post_proc\":[\"/\",16]},\"hum\":{\"condition\":[\"manufacturerdata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",28,4,true],\"post_proc\":[\"/\",16]},\"volt\":{\"condition\":[\"manufacturerdata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true],\"post_proc\":[\"/\",1000]},\"time\":{\"condition\":[\"manufacturerdata\",\"=\",40],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,8,true,false]},\"tempc_max\":{\"condition\":[\"manufacturerdata\",\"=\",44],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,4,true],\"post_proc\":[\"/\",16]},\"time_max\":{\"condition\":[\"manufacturerdata\",\"=\",44],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",24,8,true,false]},\"tempc_min\":{\"condition\":[\"manufacturerdata\",\"=\",44],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",32,4,true],\"post_proc\":[\"/\",16]},\"time_min\":{\"condition\":[\"manufacturerdata\",\"=\",44],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",36,8,true,false]},\"mac\":{\"decoder\":[\"revmac_from_hex_data\",\"manufacturerdata\",8]}}}"; +/*R""""( +{ + "brand":"GENERIC", + "model":"ThermoBeacon", + "model_id":"WS02/WS08", + "tag":"0101", + "condition":["manufacturerdata", "index", 0, "1000", "|", "manufacturerdata", "index", 0, "1100", "|", "manufacturerdata", "index", 0, "1500", "|", "manufacturerdata", "index", 0, "1800", "|", "manufacturerdata", "index", 0, "1b00", "&", "manufacturerdata", ">=", 40], + "properties":{ + "tempc":{ + "condition":["manufacturerdata", "=", 40], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 4, true], + "post_proc":["/", 16] + }, + "hum":{ + "condition":["manufacturerdata", "=", 40], + "decoder":["value_from_hex_data", "manufacturerdata", 28, 4, true], + "post_proc":["/", 16] + }, + "volt":{ + "condition":["manufacturerdata", "=", 40], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true], + "post_proc":["/", 1000] + }, + "time":{ + "condition":["manufacturerdata", "=", 40], + "decoder":["value_from_hex_data", "manufacturerdata", 32, 8, true, false] + }, + "tempc_max":{ + "condition":["manufacturerdata", "=", 44], + "decoder":["value_from_hex_data", "manufacturerdata", 20, 4, true], + "post_proc":["/", 16] + }, + "time_max":{ + "condition":["manufacturerdata", "=", 44], + "decoder":["value_from_hex_data", "manufacturerdata", 24, 8, true, false] + }, + "tempc_min":{ + "condition":["manufacturerdata", "=", 44], + "decoder":["value_from_hex_data", "manufacturerdata", 32, 4, true], + "post_proc":["/", 16] + }, + "time_min":{ + "condition":["manufacturerdata", "=", 44], + "decoder":["value_from_hex_data", "manufacturerdata", 36, 8, true, false] + }, + "mac":{ + "decoder":["revmac_from_hex_data", "manufacturerdata", 8] + } + } +})"""";*/ + +const char* _ThermoBeacon_json_props = "{\"properties\":{\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"time\":{\"unit\":\"int\",\"name\":\"timestamp\"},\"tempc_max\":{\"unit\":\"°C\",\"name\":\"maximum temperature\"},\"time_max\":{\"unit\":\"int\",\"name\":\"maximum temperature timestamp\"},\"tempc_min\":{\"unit\":\"°C\",\"name\":\"minimum temperature\"},\"time_min\":{\"unit\":\"int\",\"name\":\"minimum temperature timestamp\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "volt":{ + "unit":"V", + "name":"voltage" + }, + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "time":{ + "unit":"int", + "name":"timestamp" + }, + "tempc_max":{ + "unit":"°C", + "name":"maximum temperature" + }, + "time_max":{ + "unit":"int", + "name":"maximum temperature timestamp" + }, + "tempc_min":{ + "unit":"°C", + "name":"minimum temperature" + }, + "time_min":{ + "unit":"int", + "name":"minimum temperature timestamp" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/XMTZC04HMKG_json.h b/lib/decoder/src/devices/XMTZC04HMKG_json.h new file mode 100644 index 00000000..2a200212 --- /dev/null +++ b/lib/decoder/src/devices/XMTZC04HMKG_json.h @@ -0,0 +1,40 @@ +const char* _XMTZC04HMKG_json = "{\"brand\":\"Xiaomi\",\"model\":\"Mi Smart Scale\",\"model_id\":\"XMTZC01HM/XMTZC04HM\",\"tag\":\"05\",\"condition\":[\"servicedata\",\"index\",0,\"22\",\"|\",\"servicedata\",\"index\",0,\"a2\",\"|\",\"servicedata\",\"index\",0,\"62\",\"|\",\"servicedata\",\"index\",0,\"e2\",\"&\",\"servicedata\",\"=\",20,\"&\",\"uuid\",\"contain\",\"181d\"],\"properties\":{\"weighing_mode\":{\"decoder\":[\"bit_static_value\",\"servicedata\",0,2,\"person\",\"object\"]},\"unit\":{\"decoder\":[\"static_value\",\"kg\"]},\"weight\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",2,4,true,false],\"post_proc\":[\"/\",200]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Mi Smart Scale", + "model_id":"XMTZC01HM/XMTZC04HM", + "tag":"05", + "condition":["servicedata", "index", 0, "22", "|", "servicedata", "index", 0, "a2", "|", "servicedata", "index", 0, "62", "|", "servicedata", "index", 0, "e2", "&", "servicedata", "=", 20, "&", "uuid", "contain", "181d"], + "properties":{ + "weighing_mode":{ + "decoder":["bit_static_value", "servicedata", 0, 2, "person", "object"] + }, + "unit":{ + "decoder":["static_value", "kg"] + }, + "weight":{ + "decoder":["value_from_hex_data", "servicedata", 2, 4, true, false], + "post_proc":["/", 200] + } + } +})"""";*/ + +const char* _XMTZC04HMKG_json_props = "{\"properties\":{\"weighing_mode\":{\"unit\":\"string\",\"name\":\"weighing_mode\"},\"unit\":{\"unit\":\"string\",\"name\":\"unit\"},\"weight\":{\"unit\":\"kg\",\"name\":\"weight\"}}}"; +/*R""""( +{ + "properties":{ + "weighing_mode":{ + "unit":"string", + "name":"weighing_mode" + }, + "unit":{ + "unit":"string", + "name":"unit" + }, + "weight":{ + "unit":"kg", + "name":"weight" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/XMTZC04HMLB_json.h b/lib/decoder/src/devices/XMTZC04HMLB_json.h new file mode 100644 index 00000000..f45f78d8 --- /dev/null +++ b/lib/decoder/src/devices/XMTZC04HMLB_json.h @@ -0,0 +1,40 @@ +const char* _XMTZC04HMLB_json = "{\"brand\":\"Xiaomi\",\"model\":\"Mi Smart Scale\",\"model_id\":\"XMTZC01HM/XMTZC04HM\",\"tag\":\"05\",\"condition\":[\"servicedata\",\"index\",0,\"23\",\"|\",\"servicedata\",\"index\",0,\"a3\",\"|\",\"servicedata\",\"index\",0,\"63\",\"|\",\"servicedata\",\"index\",0,\"e3\",\"&\",\"servicedata\",\"=\",20,\"&\",\"uuid\",\"contain\",\"181d\"],\"properties\":{\"weighing_mode\":{\"decoder\":[\"bit_static_value\",\"servicedata\",0,2,\"person\",\"object\"]},\"unit\":{\"decoder\":[\"static_value\",\"lb\"]},\"weight\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",2,4,true,false],\"post_proc\":[\"/\",100]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Mi Smart Scale", + "model_id":"XMTZC01HM/XMTZC04HM", + "tag":"05", + "condition":["servicedata", "index", 0, "23", "|", "servicedata", "index", 0, "a3", "|", "servicedata", "index", 0, "63", "|", "servicedata", "index", 0, "e3", "&", "servicedata", "=", 20, "&", "uuid", "contain", "181d"], + "properties":{ + "weighing_mode":{ + "decoder":["bit_static_value", "servicedata", 0, 2, "person", "object"] + }, + "unit":{ + "decoder":["static_value", "lb"] + }, + "weight":{ + "decoder":["value_from_hex_data", "servicedata", 2, 4, true, false], + "post_proc":["/", 100] + } + } +})"""";*/ + +const char* _XMTZC04HMLB_json_props = "{\"properties\":{\"weighing_mode\":{\"unit\":\"string\",\"name\":\"weighing_mode\"},\"unit\":{\"unit\":\"string\",\"name\":\"unit\"},\"weight\":{\"unit\":\"lb\",\"name\":\"weight\"}}}"; +/*R""""( +{ + "properties":{ + "weighing_mode":{ + "unit":"string", + "name":"weighing_mode" + }, + "unit":{ + "unit":"string", + "name":"unit" + }, + "weight":{ + "unit":"lb", + "name":"weight" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/XMTZC05HMKG_json.h b/lib/decoder/src/devices/XMTZC05HMKG_json.h new file mode 100644 index 00000000..45c2bd3c --- /dev/null +++ b/lib/decoder/src/devices/XMTZC05HMKG_json.h @@ -0,0 +1,48 @@ +const char* _XMTZC05HMKG_json = "{\"brand\":\"Xiaomi\",\"model\":\"Mi Body Composition Scale\",\"model_id\":\"XMTZC02HM/XMTZC05HM\",\"tag\":\"05\",\"condition\":[\"servicedata\",\"index\",1,\"22\",\"|\",\"servicedata\",\"index\",1,\"2a\",\"|\",\"servicedata\",\"index\",1,\"62\",\"|\",\"servicedata\",\"index\",1,\"6a\",\"&\",\"servicedata\",\"=\",26,\"&\",\"uuid\",\"contain\",\"181b\"],\"properties\":{\"weighing_mode\":{\"decoder\":[\"bit_static_value\",\"servicedata\",1,2,\"person\",\"object\"]},\"unit\":{\"decoder\":[\"static_value\",\"kg\"]},\"weight\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,true,false],\"post_proc\":[\"/\",200]},\"impedance\":{\"condition\":[\"servicedata\",3,\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,true,false]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Mi Body Composition Scale", + "model_id":"XMTZC02HM/XMTZC05HM", + "tag":"05", + "condition":["servicedata", "index", 1, "22", "|", "servicedata", "index", 1, "2a", "|", "servicedata", "index", 1, "62", "|", "servicedata", "index", 1, "6a", "&", "servicedata", "=", 26, "&", "uuid", "contain", "181b"], + "properties":{ + "weighing_mode":{ + "decoder":["bit_static_value", "servicedata", 1, 2, "person", "object"] + }, + "unit":{ + "decoder":["static_value", "kg"] + }, + "weight":{ + "decoder":["value_from_hex_data", "servicedata", 22, 4, true, false], + "post_proc":["/", 200] + }, + "impedance":{ + "condition":["servicedata", 3, "6"], + "decoder":["value_from_hex_data", "servicedata", 18, 4, true, false] + } + } +})"""";*/ + +const char* _XMTZC05HMKG_json_props = "{\"properties\":{\"weighing_mode\":{\"unit\":\"string\",\"name\":\"weighing_mode\"},\"unit\":{\"unit\":\"string\",\"name\":\"unit\"},\"weight\":{\"unit\":\"kg\",\"name\":\"weight\"},\"impedance\":{\"unit\":\"Ω\",\"name\":\"impedance\"}}}"; +/*R""""( +{ + "properties":{ + "weighing_mode":{ + "unit":"string", + "name":"weighing_mode" + }, + "unit":{ + "unit":"string", + "name":"unit" + }, + "weight":{ + "unit":"kg", + "name":"weight" + }, + "impedance":{ + "unit":"Ω", + "name":"impedance" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/XMTZC05HMLB_json.h b/lib/decoder/src/devices/XMTZC05HMLB_json.h new file mode 100644 index 00000000..e53fade5 --- /dev/null +++ b/lib/decoder/src/devices/XMTZC05HMLB_json.h @@ -0,0 +1,48 @@ +const char* _XMTZC05HMLB_json = "{\"brand\":\"Xiaomi\",\"model\":\"Mi Body Composition Scale\",\"model_id\":\"XMTZC02HM/XMTZC05HM\",\"tag\":\"05\",\"condition\":[\"servicedata\",\"index\",1,\"32\",\"|\",\"servicedata\",\"index\",1,\"3a\",\"|\",\"servicedata\",\"index\",1,\"72\",\"|\",\"servicedata\",\"index\",1,\"7a\",\"&\",\"servicedata\",\"=\",26,\"&\",\"uuid\",\"contain\",\"181b\"],\"properties\":{\"weighing_mode\":{\"decoder\":[\"bit_static_value\",\"servicedata\",1,2,\"person\",\"object\"]},\"unit\":{\"decoder\":[\"static_value\",\"lb\"]},\"weight\":{\"decoder\":[\"value_from_hex_data\",\"servicedata\",22,4,true,false],\"post_proc\":[\"/\",100]},\"impedance\":{\"condition\":[\"servicedata\",3,\"6\"],\"decoder\":[\"value_from_hex_data\",\"servicedata\",18,4,true,false]}}}"; +/*R""""( +{ + "brand":"Xiaomi", + "model":"Mi Body Composition Scale", + "model_id":"XMTZC02HM/XMTZC05HM", + "tag":"05", + "condition":["servicedata", "index", 1, "32", "|", "servicedata", "index", 1, "3a", "|", "servicedata", "index", 1, "72", "|", "servicedata", "index", 1, "7a", "&", "servicedata", "=", 26, "&", "uuid", "contain", "181b"], + "properties":{ + "weighing_mode":{ + "decoder":["bit_static_value", "servicedata", 1, 2, "person", "object"] + }, + "unit":{ + "decoder":["static_value", "lb"] + }, + "weight":{ + "decoder":["value_from_hex_data", "servicedata", 22, 4, true, false], + "post_proc":["/", 100] + }, + "impedance":{ + "condition":["servicedata", 3, "6"], + "decoder":["value_from_hex_data", "servicedata", 18, 4, true, false] + } + } +})"""";*/ + +const char* _XMTZC05HMLB_json_props = "{\"properties\":{\"weighing_mode\":{\"unit\":\"string\",\"name\":\"weighing_mode\"},\"unit\":{\"unit\":\"string\",\"name\":\"unit\"},\"weight\":{\"unit\":\"lb\",\"name\":\"weight\"},\"impedance\":{\"unit\":\"Ω\",\"name\":\"impedance\"}}}"; +/*R""""( +{ + "properties":{ + "weighing_mode":{ + "unit":"string", + "name":"weighing_mode" + }, + "unit":{ + "unit":"string", + "name":"unit" + }, + "weight":{ + "unit":"lb", + "name":"weight" + }, + "impedance":{ + "unit":"Ω", + "name":"impedance" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/common_props.h b/lib/decoder/src/devices/common_props.h new file mode 100644 index 00000000..509bd33c --- /dev/null +++ b/lib/decoder/src/devices/common_props.h @@ -0,0 +1,90 @@ +#ifndef _DECODER_COMMON_PROPS +#define _DECODER_COMMON_PROPS + +const char* _common_TH_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + } + } +})"""";*/ + +const char* _common_BTH_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"}}}"; +/* +R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "batt":{ + "unit":"%", + "name":"battery" + } + } +})"""";*/ + +const char* _common_BTHM_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/* +R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ + +const char* _common_BVTH_props = "{\"properties\":{\"tempc\":{\"unit\":\"°C\",\"name\":\"temperature\"},\"hum\":{\"unit\":\"%\",\"name\":\"humidity\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"},\"mac\":{\"unit\":\"string\",\"name\":\"MAC address\"}}}"; +/*R""""( +{ + "properties":{ + "tempc":{ + "unit":"°C", + "name":"temperature" + }, + "hum":{ + "unit":"%", + "name":"humidity" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "volt":{ + "unit":"V", + "name":"voltage" + }, + "mac":{ + "unit":"string", + "name":"MAC address" + } + } +})"""";*/ + +#endif diff --git a/lib/decoder/src/devices/iBeacon_json.h b/lib/decoder/src/devices/iBeacon_json.h new file mode 100644 index 00000000..a97f80f4 --- /dev/null +++ b/lib/decoder/src/devices/iBeacon_json.h @@ -0,0 +1,64 @@ +const char* _ibeacon_json = "{\"brand\":\"GENERIC\",\"model\":\"iBeacon\",\"model_id\":\"IBEACON\",\"tag\":\"06\",\"condition\":[\"manufacturerdata\",\"=\",50,\"index\",0,\"4c00\"],\"properties\":{\"mfid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",0,4]},\"uuid\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",8,32]},\"major\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",40,4,false]},\"minor\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",44,4,false]},\"txpower\":{\"condition\":[\"manufacturerdata\",48,\"bit\",3,1],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",48,2,false]},\"volt\":{\"condition\":[\"manufacturerdata\",48,\"bit\",3,0],\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",48,2,false],\"post_proc\":[\"/\",10]}}}"; + +/*R""""( +{ + "brand":"GENERIC", + "model":"iBeacon", + "model_id":"IBEACON", + "tag":"06", + "condition":["manufacturerdata", "=", 50, "index", 0, "4c00"], + "properties":{ + "mfid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 0, 4] + }, + "uuid":{ + "decoder":["string_from_hex_data", "manufacturerdata", 8, 32] + }, + "major":{ + "decoder":["value_from_hex_data", "manufacturerdata", 40, 4, false] + }, + "minor":{ + "decoder":["value_from_hex_data", "manufacturerdata", 44, 4, false] + }, + "txpower":{ + "condition":["manufacturerdata", 48, "bit", 3, 1], + "decoder":["value_from_hex_data","manufacturerdata", 48, 2, false] + }, + "volt":{ + "condition":["manufacturerdata", 48, "bit", 3, 0], + "decoder":["value_from_hex_data","manufacturerdata", 48, 2, false], + "post_proc":["/", 10] + } + } +})"""";*/ + +const char* _ibeacon_json_props = "{\"properties\":{\"mfid\":{\"unit\":\"hex\",\"name\":\"manufacturer id\"},\"uuid\":{\"unit\":\"hex\",\"name\":\"service uuid\"},\"major\":{\"unit\":\"hex\",\"name\":\"major value\"},\"minor\":{\"unit\":\"hex\",\"name\":\"minor value\"},\"txpower\":{\"unit\":\"dBm\",\"name\":\"tx power @ 1 m\"},\"volt\":{\"unit\":\"V\",\"name\":\"voltage\"}}}"; +/*R""""( +{ + "properties":{ + "mfid":{ + "unit":"hex", + "name":"manufacturer id" + }, + "uuid":{ + "unit":"hex", + "name":"service uuid" + }, + "major":{ + "unit":"hex", + "name":"major value" + }, + "minor":{ + "unit":"hex", + "name":"minor value" + }, + "txpower":{ + "unit":"dBm", + "name":"tx power @ 1 m" + }, + "volt":{ + "unit":"V", + "name":"voltage" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/iNodeEM_json.h b/lib/decoder/src/devices/iNodeEM_json.h new file mode 100644 index 00000000..dc033ae6 --- /dev/null +++ b/lib/decoder/src/devices/iNodeEM_json.h @@ -0,0 +1,72 @@ +const char* _iNodeEM_json = "{\"brand\":\"iNode\",\"model\":\"Energy Meter\",\"model_id\":\"INEM\",\"tag\":\"0c01\",\"condition\":[\"manufacturerdata\",\"index\",0,\"90\",\"|\",\"manufacturerdata\",\"index\",0,\"92\",\"|\",\"manufacturerdata\",\"index\",0,\"94\",\"|\",\"manufacturerdata\",\"index\",0,\"96\",\"&\",\"manufacturerdata\",\"=\",26,\"index\",2,\"82\"],\"properties\":{\".cal\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,4,true,false],\"post_proc\":[\"&\",16383]},\"avg\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",4,4,true,false],\"post_proc\":[\"*\",60,\"/\",\".cal\"]},\"avgu\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",18,0,\"kW\",\"m³\"]},\"sum\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",8,4,true,false],\"post_proc\":[\"/\",\".cal\"]},\"sumu\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",18,0,\"kWh\",\"m³\"]},\"batt\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",20,1,false,false],\"post_proc\":[\"-\",1,\"*\",10]},\"_batt\":{\"condition\":[\"manufacturerdata\",20,\"1\",\"|\",\"manufacturerdata\",20,\"c\",\"|\",\"manufacturerdata\",20,\"d\",\"|\",\"manufacturerdata\",20,\"e\",\"|\",\"manufacturerdata\",20,\"f\"],\"decoder\":[\"static_value\",\"100\"]},\"lowbatt\":{\"condition\":[\"manufacturerdata\",1,\"bit\",2,1],\"decoder\":[\"static_value\",true]}}}"; +/*R""""( +{ + "brand":"iNode", + "model":"Energy Meter", + "model_id":"INEM", + "tag":"0c01", + "condition":["manufacturerdata", "index", 0, "90", "|", "manufacturerdata", "index", 0, "92", "|", "manufacturerdata", "index", 0, "94", "|", "manufacturerdata", "index", 0, "96", "&", "manufacturerdata", "=", 26, "index", 2, "82"], + "properties":{ + ".cal":{ + "decoder":["value_from_hex_data", "manufacturerdata", 16, 4, true, false], + "post_proc":["&", 16383] + }, + "avg":{ + "decoder":["value_from_hex_data", "manufacturerdata", 4, 4, true, false], + "post_proc":[ "*", 60, "/", ".cal"] + }, + "avgu":{ + "decoder":["bit_static_value", "manufacturerdata", 18, 0, "kW", "m³"] + }, + "sum":{ + "decoder":["value_from_hex_data", "manufacturerdata", 8, 4, true, false], + "post_proc":["/", ".cal"] + }, + "sumu":{ + "decoder":["bit_static_value", "manufacturerdata", 18, 0, "kWh", "m³"] + }, + "batt":{ + "decoder":["value_from_hex_data", "manufacturerdata", 20, 1, false, false], + "post_proc":["-", 1, "*", 10] + }, + "_batt":{ + "condition":["manufacturerdata", 20, "1", "|", "manufacturerdata", 20, "c", "|", "manufacturerdata", 20, "d", "|", "manufacturerdata", 20, "e", "|", "manufacturerdata", 20, "f"], + "decoder":["static_value", "100"] + }, + "lowbatt":{ + "condition":["manufacturerdata", 1, "bit", 2, 1], + "decoder":["static_value", true] + } + } +})"""";*/ + +const char* _iNodeEM_json_props = "{\"properties\":{\"avg\":{\"unit\":\"kW/m³\",\"name\":\"average\"},\"avgu\":{\"unit\":\"string\",\"name\":\"average unit\"},\"sum\":{\"unit\":\"kWh/m³\",\"name\":\"sum\"},\"sumu\":{\"unit\":\"string\",\"name\":\"sum unit\"},\"batt\":{\"unit\":\"%\",\"name\":\"battery\"},\"lowbatt\":{\"unit\":\"status\",\"name\":\"low battery\"}}}"; +/*R""""( +{ + "properties":{ + "avg":{ + "unit":"kW/m³", + "name":"average" + }, + "avgu":{ + "unit":"string", + "name":"average unit" + }, + "sum":{ + "unit":"kWh/m³", + "name":"sum" + }, + "sumu":{ + "unit":"string", + "name":"sum unit" + }, + "batt":{ + "unit":"%", + "name":"battery" + }, + "lowbatt":{ + "unit":"status", + "name":"low battery" + } + } +})"""";*/ diff --git a/lib/decoder/src/devices/tracker_json.h b/lib/decoder/src/devices/tracker_json.h new file mode 100644 index 00000000..afe30667 --- /dev/null +++ b/lib/decoder/src/devices/tracker_json.h @@ -0,0 +1,85 @@ +const char* _tracker_json_nut = "{\"brand\":\"nut\",\"model\":\"Smart Tracker\",\"model_id\":\"NUT\",\"tag\":\"100f\",\"condition\":[\"name\",\"index\",0,\"nut\",\"&\",\"manufacturerdata\",\"=\",8,\"&\",\"uuid\",\"index\",0,\"180a\"],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"nut Tracker\"]}}}"; +/*R""""( +{ + "brand":"nut", + "model":"Smart Tracker", + "model_id":"NUT", + "tag":"100f", + "condition":["name", "index", 0, "nut", "&", "manufacturerdata", "=", 8, "&", "uuid", "index", 0, "180a"], + "properties":{ + "device":{ + "decoder":["static_value", "nut Tracker"] + } + } +})"""";*/ + +const char* _tracker_json_itag = "{\"brand\":\"iTAG\",\"model\":\"Smart Tracker\",\"model_id\":\"ITAG\",\"tag\":\"100f\",\"condition\":[\"name\",\"index\",0,\"iTAG\",\"&\",\"manufacturerdata\",\"=\",8],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"iTAG Tracker\"]}}}"; +/*R""""( +{ + "brand":"iTAG", + "model":"Smart Tracker", + "model_id":"ITAG", + "tag":"100f", + "condition":["name", "index", 0, "iTAG", "&", "manufacturerdata", "=", 8], + "properties":{ + "device":{ + "decoder":["static_value", "iTAG Tracker"] + } + } +})"""";*/ + +const char* _tracker_json_tagit = "{\"brand\":\"Tag-It\",\"model\":\"Smart Tracker\",\"model_id\":\"TAGIT\",\"tag\":\"100f\",\"condition\":[\"name\",\"index\",0,\"Tag-It\",\"&\",\"manufacturerdata\",\"=\",26],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Tag-It Tracker\"]}}}"; +/*R""""( +{ + "brand":"Tag-It", + "model":"Smart Tracker", + "model_id":"TAGIT", + "tag":"100f", + "condition":["name", "index", 0, "Tag-It", "&", "manufacturerdata", "=", 26], + "properties":{ + "device":{ + "decoder":["static_value", "Tag-It Tracker"] + } + } +})"""";*/ + +const char* _tracker_json_tile = "{\"brand\":\"Tile\",\"model\":\"Smart Tracker\",\"model_id\":\"TILE\",\"tag\":\"100f\",\"condition\":[\"uuid\",\"index\",0,\"feed\",\"|\",\"uuid\",\"index\",0,\"feec\",\"|\",\"uuid\",\"index\",0,\"fd84\"],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Tile Tracker\"]}}}"; +/*R""""( +{ + "brand":"Tile", + "model":"Smart Tracker", + "model_id":"TILE", + "tag":"100f", + "condition":["uuid", "index", 0, "feed", "|", "uuid", "index", 0, "feec", "|", "uuid", "index", 0, "fd84"], + "properties":{ + "device":{ + "decoder":["static_value", "Tile Tracker"] + } + } +})"""";*/ + +const char* _tracker_json_tilename = "{\"brand\":\"Tile\",\"model\":\"Smart Tracker\",\"model_id\":\"TILE\",\"tag\":\"100f\",\"condition\":[\"name\",\"index\",0,\"Tile\"],\"properties\":{\"device\":{\"decoder\":[\"static_value\",\"Tile Tracker\"]}}}"; +/*R""""( +{ + "brand":"Tile", + "model":"Smart Tracker", + "model_id":"TILE", + "tag":"100f", + "condition":["name", "index", 0, "Tile"], + "properties":{ + "device":{ + "decoder":["static_value", "Tile Tracker"] + } + } +})"""";*/ + +const char* _tracker_json_props = "{\"properties\":{\"device\":{\"unit\":\"string\",\"name\":\"tracker device\"}}}"; +/*R""""( +{ + "properties":{ + "device":{ + "unit":"string", + "name":"tracker device" + } + } +})"""";*/ diff --git a/lib/iarduino_RTC/.gitattributes b/lib/iarduino_RTC/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/lib/iarduino_RTC/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/lib/iarduino_RTC/LICENSE b/lib/iarduino_RTC/LICENSE new file mode 100644 index 00000000..8e99b7e9 --- /dev/null +++ b/lib/iarduino_RTC/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2019 iarduino.ru + +Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование и/или продажу копий Программного Обеспечения, а также лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий: + +Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения. + +ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/iarduino_RTC/README.md b/lib/iarduino_RTC/README.md new file mode 100644 index 00000000..c3d7253b --- /dev/null +++ b/lib/iarduino_RTC/README.md @@ -0,0 +1,104 @@ + +[![](https://iarduino.ru/img/logo.svg)](https://iarduino.ru)[![](https://wiki.iarduino.ru/img/git-shop.svg?3)](https://iarduino.ru) [![](https://wiki.iarduino.ru/img/git-wiki.svg?2)](https://wiki.iarduino.ru) [![](https://wiki.iarduino.ru/img/git-lesson.svg?2)](https://lesson.iarduino.ru)[![](https://wiki.iarduino.ru/img/git-forum.svg?2)](http://forum.trema.ru) + +# iarduino_RTC [СКАЧАТЬ](https://github.com/tremaru/iarduino_RTC/archive/1.2.1.zip) +Библиотека позволяет читать и записывать время RTC модулей на базе чипов: DS1302, [DS1307](http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html), [DS3231](http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul-v2-0.html), ... +Преимуществом данной библиотеки является удобная реализация получения времени. +> Подробнее про установку библиотеки читайте в нашей [инструкции](https://wiki.iarduino.ru/page/Installing_libraries/). + +Пример подключения к [Arduino](https://iarduino.ru/shop/boards/arduino-uno-r3.html)/[Piranha UNO](https://iarduino.ru/shop/boards/piranha-uno-r3.html) с помощью [Trema Shield](https://iarduino.ru/shop/Expansion-payments/trema-shield.html) +Подробнее о [подключении модуля](https://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/) +![enter image description here](https://iarduino.ru/img/upload/6a83fe7f3deb2f48d1408daac49284ba.png) + +| Модель | Ссылка на магазин| +|--|--| +| DS1307 ![enter image description here](https://wiki.iarduino.ru/img/resources/643/643.svg) | https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html| +| DS3231 ![enter image description here](https://wiki.iarduino.ru/img/resources/877/877.svg) | https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul-v2-0.html | + + + +## Описание библиотеки: +Библиотека позволяет читать и записывать время RTC модулей на базе чипов: DS1302, [DS1307](http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html), [DS3231](http://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul-v2-0.html), … +Преимуществом данной библиотеки является удобная реализация получения времени. +**[ Подробное описание, подключение и примеры работы ](https://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/)** + +## Назначение функций и переменных: +Подробное описание работы с библиотекой, находится в разделе https://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + +**Подключаем библиотеку.** + + #include // Подключаем библиотеку. + + **Создаём объект.** + + + iarduino_RTC ОБЪЕКТ ( НАЗВАНИЕ [, ВЫВОД_RST [, ВЫВОД_CLK [, ВЫВОД_DAT ]]] ); // Создаём объект. + +**Инициализация работы RTC модуля.** + + Функция begin(); // Инициализация работы RTC модуля. + + **Установка времени.** + + Функция settime( СЕК [, МИН [, ЧАС [, ДЕНЬ [, МЕС [, ГОД [, ДН ]]]]]] ); // Установка времени. + + **Чтение времени.** + + Функция gettime( [ СТРОКА ] ); // Чтение времени. + +**Заставляет функцию gettime «мигать» указанным параметром времени.** + + функция blinktime( ПАРАМЕТР [ ЧАСТОТА ] ); // Заставляет функцию gettime «мигать» указанным параметром времени. + +**Указывает минимальный период обращения к модулю в минутах.** + + функция period( МИНУТЫ ); // Указывает минимальный период обращения к модулю в минутах. + +**Возвращает секунды от 0 до 59.** + + Переменная seconds // Возвращает секунды от 0 до 59. + + **Возвращает минуты от 0 до 59.** + + Переменная minutes // Возвращает минуты от 0 до 59. + +**Возвращает часы от 1 до 12.** + + Переменная hours // Возвращает часы от 1 до 12. + +**Возвращает часы от 0 до 23.** + + Переменная Hours // Возвращает часы от 0 до 23. + +**Возвращает полдень 0 или 1 (0-am, 1-pm).** + + Переменная midday // Возвращает полдень 0 или 1 (0-am, 1-pm). + +**Возвращает день месяца от 1 до 31.** + + Переменная day // Возвращает день месяца от 1 до 31. + +**Возвращает день недели от 0 до 6 (0-воскресенье, 6-суббота).** + + Переменная weekday // Возвращает день недели от 0 до 6 (0-воскресенье, 6-суббота). + +**Возвращает месяц от 1 до 12.** + + Переменная month // Возвращает месяц от 1 до 12. + +**Возвращает год от 0 до 99.** + + Переменная year // Возвращает год от 0 до 99. + + + + + + + + + + + + + diff --git a/lib/iarduino_RTC/examples/gettime/gettime.ino b/lib/iarduino_RTC/examples/gettime/gettime.ino new file mode 100644 index 00000000..066b7ec9 --- /dev/null +++ b/lib/iarduino_RTC/examples/gettime/gettime.ino @@ -0,0 +1,128 @@ +// Пример считывания текущего времени: // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. +} // +void loop(){ // + if(millis()%1000==0){ // Если прошла 1 секунда. + Serial.println(watch.gettime("d-m-Y, H:i:s, D")); // Выводим время. + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ====================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/examples/gettimeUnix/gettimeUnix.ino b/lib/iarduino_RTC/examples/gettimeUnix/gettimeUnix.ino new file mode 100644 index 00000000..a11b0449 --- /dev/null +++ b/lib/iarduino_RTC/examples/gettimeUnix/gettimeUnix.ino @@ -0,0 +1,128 @@ +// Пример считывания секунд прошедших с начала эпохи Unix: // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. +} // +void loop(){ // + if (millis()%1000==0){ // Если прошла 1 секунда. + Serial.println( watch.gettimeUnix() ); // Выводим количество секунд прошедших с начала эпохи Unix (01.01.1970 00:00:00 GMT). + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ====================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/examples/getvartime/getvartime.ino b/lib/iarduino_RTC/examples/getvartime/getvartime.ino new file mode 100644 index 00000000..5653d213 --- /dev/null +++ b/lib/iarduino_RTC/examples/getvartime/getvartime.ino @@ -0,0 +1,138 @@ +// Пример считывания текущего времени в переменные: // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +uint8_t D, M, Y, h, m, s, W; // Объявляем переменные для получения следующих значений: D-день, M-месяц, Y-год, h-часы, m-минуты, s-секунды, W-день недели. + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. +} // +void loop(){ // + if(millis()%1000==0){ // Если прошла 1 секунда. + watch.gettime(); // Считываем текущее время из модуля в буфер библиотеки. + D = watch.day; // Получаем из буфера библиотеки текущий день месяца 1-31. + M = watch.month; // Получаем из буфера библиотеки текущий месяц 1-12. + Y = watch.year; // Получаем из буфера библиотеки текущий год 0-99. + h = watch.Hours; // Получаем из буфера библиотеки текущие часы 0-23. + m = watch.minutes; // Получаем из буфера библиотеки текущие минуты 0-59. + s = watch.seconds; // Получаем из буфера библиотеки текущие секунды 0-59. + W = watch.weekday; // Получаем из буфера библиотеки текущий день недели 0-6. + Serial.println((String) D+"-"+M+"-"+Y+", "+h+":"+m+":"+s+", "+W); // Выводим время в монитор, одной строкой. + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ================================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/examples/setsystime/setsystime.ino b/lib/iarduino_RTC/examples/setsystime/setsystime.ino new file mode 100644 index 00000000..9c393afc --- /dev/null +++ b/lib/iarduino_RTC/examples/setsystime/setsystime.ino @@ -0,0 +1,137 @@ +// Пример установки системного времени (вашего компьютера) // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +// Определяем системное время: // Время загрузки скетча. +const char* strM="JanFebMarAprMayJunJulAugSepOctNovDec"; // Определяем массив всех вариантов текстового представления текущего месяца находящегося в предопределенном макросе __DATE__. +const char* sysT=__TIME__; // Получаем время компиляции скетча в формате "SS:MM:HH". +const char* sysD=__DATE__; // Получаем дату компиляции скетча в формате "MMM:DD:YYYY", где МММ - текстовое представление текущего месяца, например: Jul. +// Парсим полученные значения в массив: // Определяем массив «i» из 6 элементов типа int, содержащий следующие значения: секунды, минуты, часы, день, месяц и год компиляции скетча. +const int i[6] {(sysT[6]-'0')*10+(sysT[7]-'0'), (sysT[3]-'0')*10+(sysT[4]-'0'), (sysT[0]-'0')*10+(sysT[1]-'0'), (sysD[4]-'0')*10+(sysD[5]-'0'), ((int)memmem(strM,36,sysD,3)+3-(int)&strM[0])/3, (sysD[9]-'0')*10+(sysD[10]-'0')}; + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. + watch.settime(i[0],i[1],i[2],i[3],i[4],i[5]); // Устанавливаем время в модуль: i[0] сек, i[1] мин, i[2] час, i[3] день, i[4] месяц, i[5] год, без указания дня недели. +// watch.settime(i[0],i[1],i[2],i[3],i[4],i[5], 2); // Можно установить время с указанием дня недели, где последний параметр, это день недели (указывается вручную) в формате: 0-воскресенье, 1-понедельник, ... , 6-суббота. +} // +void loop(){ // + if(millis()%1000==0){ // Если прошла 1 секунда. + Serial.println(watch.gettime("d-m-Y, H:i:s, D")); // Выводим время. + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ====================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/examples/settime/settime.ino b/lib/iarduino_RTC/examples/settime/settime.ino new file mode 100644 index 00000000..fcf4c118 --- /dev/null +++ b/lib/iarduino_RTC/examples/settime/settime.ino @@ -0,0 +1,129 @@ +// Пример установки времени модуля: // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. + watch.settime(0,51,21,27,10,15,2); // Записываем время в модуль: 0 сек, 51 мин, 21 час, 27, октября, 2015 года, вторник. +} // +void loop(){ // + if(millis()%1000==0){ // Если прошла 1 секунда. + Serial.println(watch.gettime("d-m-Y, H:i:s, D")); // Выводим время. + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ====================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/examples/settimeUnix/settimeUnix.ino b/lib/iarduino_RTC/examples/settimeUnix/settimeUnix.ino new file mode 100644 index 00000000..0c6e9b8d --- /dev/null +++ b/lib/iarduino_RTC/examples/settimeUnix/settimeUnix.ino @@ -0,0 +1,129 @@ +// Пример установки времени модуля значением Unix time: // + // +// Раскомментируйте для программной реализации шины I2C: // +// #define pin_SW_SDA 3 // Назначение любого вывода Arduino для работы в качестве линии SDA программной шины I2C. +// #define pin_SW_SCL 9 // Назначение любого вывода Arduino для работы в качестве линии SCL программной шины I2C. +// Раскомментируйте для совместимости с большинством плат: // +// #include // Библиотека iarduino_RTC будет использовать методы и функции библиотеки Wire. +// Ссылки для ознакомления: // +// Подробная информация о подключении модуля к шине I2C: // http://wiki.iarduino.ru/page/i2c_connection/ +// Подробная информация о функциях и методах библиотеки: // http://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ + // +#include // Подключаем библиотеку iarduino_RTC для работы с модулями реального времени. +// iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. +// iarduino_RTC watch(RTC_DS1307); // Объявляем объект watch для работы с RTC модулем на базе чипа DS1307, используется шина I2C. + iarduino_RTC watch(RTC_DS3231); // Объявляем объект watch для работы с RTC модулем на базе чипа DS3231, используется шина I2C. +// iarduino_RTC watch(RTC_RX8025); // Объявляем объект watch для работы с RTC модулем на базе чипа RX8025, используется шина I2C. + // +void setup(){ // + delay(300); // Ждем готовности модуля отвечать на запросы. + Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта на скорости 9600 бод. + watch.begin(); // Инициируем работу с модулем. + watch.settimeUnix(1577836800); // Записываем время в модуль: 01.01.1970 00:00:00 (дата начала эпохи Unix) + 1577836800 сек = 01.01.2020 00:00:00. +} // +void loop(){ // + if(millis()%1000==0){ // Если прошла 1 секунда. + Serial.println(watch.gettime("d-m-Y, H:i:s, D")); // Выводим время. + delay(1); // Приостанавливаем скетч на 1 мс, чтоб не выводить время несколько раз за 1мс. + } // +} // + // +// ====================================================== // +// ОПИСАНИЯ ПАРАМЕТРОВ ФУНКЦИЙ: +// +// • Подключение библиотеки: +// #include +// iarduino_RTC watch(название модуля [, вывод SS/RST [, вывод CLK [, вывод DAT]]]); +// - Если модуль работает на шине I2C или SPI, то достаточно указать 1 параметр, например: iarduino_RTC watch(RTC_DS3231); +// - Если модуль работает на шине SPI, а аппаратный вывод SS занят, то номер назначенного вывода SS для модуля указывается вторым параметром, например: iarduino_RTC watch(RTC_DS1305,22); +// - Если модуль работает на трехпроводной шине, то указываются номера всех выводов, например: iarduino_RTC watch(RTC_DS1302, 2, 3, 4); // RST, CLK, DAT. +// +// • Для работы с модулями, в библиотеке реализованы 7 функции: +// - Инициировать модуль begin(); +// - Указать время settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]); +// - Получить время gettime(["строка с параметрами"]); +// - Мигать времем blinktime(0-не_мигать / 1-мигают_сек / 2-мигают_мин / 3-мигают_час / 4-мигают_дни / 5-мигают_мес / 6-мигает_год / 7-мигают_дни_недели / 8-мигает_полдень); +// - Разгрузить шину period(минуты); +// - Получить Unix время gettimeUnix(); +// - Указать Unix время settimeUnix(секунды); +// +// • Функция begin(): +// - Функция инициирует модуль: проверяет регистры модуля, запускает генератор модуля и т.д. +// +// • Функция settime(секунды [, минуты [, часы [, день [, месяц [, год [, день недели]]]]]]): +// - Функция записывает время в модуль. +// - Год указывается без учёта века, в формате 0-99. +// - Часы указываются в 24-часовом формате, от 0 до 23. +// - День недели указывается в виде числа: 0-воскресенье, 1-понедельник, 2-вторник ..., 6-суббота. +// - Если предыдущий параметр надо оставить без изменений, то можно указать отрицательное или заведомо большее значение. +// - Все параметры, кроме секунд, являются необязательными. Если параметр не указан, значит он не будет изменён. +// - Пример: watch.settime(-1, 10); // Установить 10 минут, а секунды, часы и дату, оставить без изменений. +// - Пример: watch.settime(0, 5, 13); // Установить 13 часов, 5 минут, 0 секунд, а дату оставить без изменений. +// - Пример: watch.settime(-1, -1, -1, 9, 2, 17); // Установить дату 09.02.2017, а время и день недели оставить без изменений. +// +// • Функция gettime(["строка с параметрами"]): +// - Функция получает и возвращает строку заменяя описанные ниже символы на текущее время: +// - Пример: watch.gettime("d-m-Y, H:i:s, D"); // Вернуть строку с датой и временем, например: "01-10-2015, 14:00:05, Thu". +// - Пример: watch.gettime("s"); // Вернуть строку с секундами, например: "05". +// - Указанные символы идентичны символам для функции date() в PHP: +// s - Вернуть секунды от 00 до 59 (два знака). +// i - Вернуть минуты от 00 до 59 (два знака). +// h - Вернуть часы в 12-часовом формате от 01 до 12 (два знака). +// H - Вернуть часы в 24-часовом формате от 00 до 23 (два знака). +// d - Вернуть день месяца от 01 до 31 (два знака). +// w - Вернуть день недели от 0 до 6 (один знак: 0-воскресенье, 6-суббота). +// D - Вернуть день недели наименование от Mon до Sun (три знака: Mon Tue Wed Thu Fri Sat Sun). +// m - Вернуть месяц от 01 до 12 (два знака). +// M - Вернуть месяц наименование от Jan до Dec (три знака: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec). +// Y - Вернуть год от 2000 до 2099 (четыре знака). +// y - Вернуть год от 00 до 99 (два знака). +// a - Вернуть полдень am или pm (два знака, в нижнем регистре). +// A - Вернуть полдень AM или PM (два знака, в верхнем регистре). +// +// Если требуется получить время в виде цифр, то можно вызвать функцию gettime() без параметра, после чего получить время из переменных: +// - Пример: watch.gettime(); +// Serial.print(watch.Hours); Serial.print(":"); // Вывести часы. +// Serial.print(watch.minutes); Serial.print(":"); // Вывести минуты. +// Serial.print(watch.seconds); Serial.println(""); // Вывести секунды. +// seconds - Переменная содержит секунды 0-59. +// minutes - Переменная содержит минуты 0-59. +// hours - Переменная содержит часы 1-12. +// Hours - Переменная содержит часы 0-23. +// midday - Переменная содержит полдень 0-1 (0-am, 1-pm). +// day - Переменная содержит день месяца 1-31. +// weekday - Переменная содержит день недели 0-6 (0-воскресенье, 6-суббота). +// month - Переменная содержит месяц 1-12. +// year - Переменная содержит год 0-99. +// Unix - Переменная содержит секунды прошедшие с начала эпохи Unix. +// +// • Функция blinktime(параметр [, частота ]): +// - Данная функция указывает функции gettime("строка с параметрами"); мигать одним из параметров времени (заменять параметр пробелами). +// - Эта функция может быть полезна, для отображения на дисплее, устанавливаемого параметра времени. +// Например, при установке минут, они начинают мигать, и Вам понятно, что именно Вы устанавливаете. +// - Первым аргументом функции указывается параметр в виде числа от 0 до 8: +// 0 - не мигать. +// 1 - мигают сек. +// 2 - мигают мин. +// 3 - мигают час. +// 4 - мигают дни. +// 5 - мигают мес. +// 6 - мигает год. +// 7 - мигают дни недели. +// 8 - мигает полдень. +// - Второй аргумент функции является необязательным, он указвает частоту миганий в Гц, по умолчанию 1Гц. +// - Пример: watch.blinktime(6); // При выводе на дисплей будет мигать год с частотой по умолчанию 1Гц. +// - Пример: watch.blinktime(6, 2); // При выводе на дисплей будет мигать год с частотой 2Гц. +// +// • Функция period(минуты): +// - Устанавливает минимальный период обращения к модулю в минутах (от 0 до 255). +// - Данная функция указывает функции gettime() откуда брать текущее время: из модуля (не чаще заданного периода), или рассчитать в библиотеке (без обращения к модулю). +// - Пример: watch.period(10); // Теперь функция gettime() будет получать время от модуля только 1 раз в 10 минут. +// Ответом на все остальные запросы к функции gettime(), будет рассчитанное время: сумма времени полученного от модуля и времени прошедшего с момента его получения. +// +// • Функция gettimeUnix(): +// - Функция возвращает число равное количеству секунд прошедших с начала эпохи Unix (с полуночи 1 января 1970 года). +// +// • Функция settimeUnix(секунды): +// - Функция записывает время в модуль. +// - В качестве единственного параметра функции указывается количество секунд прошедших с начала эпохи Unix. +// - Пример: watch.settimeUnix(1577836800); // Установить время на 1577836800 сек больше даты 01.01.1970г 00:00:00, что соответствует дате 01.01.2020г 00:00:00. \ No newline at end of file diff --git a/lib/iarduino_RTC/extras/DS1302.pdf b/lib/iarduino_RTC/extras/DS1302.pdf new file mode 100644 index 00000000..fd672446 Binary files /dev/null and b/lib/iarduino_RTC/extras/DS1302.pdf differ diff --git a/lib/iarduino_RTC/extras/DS1307.pdf b/lib/iarduino_RTC/extras/DS1307.pdf new file mode 100644 index 00000000..276e7a16 Binary files /dev/null and b/lib/iarduino_RTC/extras/DS1307.pdf differ diff --git a/lib/iarduino_RTC/extras/DS3231.pdf b/lib/iarduino_RTC/extras/DS3231.pdf new file mode 100644 index 00000000..a445882a Binary files /dev/null and b/lib/iarduino_RTC/extras/DS3231.pdf differ diff --git a/lib/iarduino_RTC/extras/RX8025.txt b/lib/iarduino_RTC/extras/RX8025.txt new file mode 100644 index 00000000..efb28e28 --- /dev/null +++ b/lib/iarduino_RTC/extras/RX8025.txt @@ -0,0 +1,196 @@ +Модуль часов реального времени RX-8025 SA/NB: + +НАЗНАЧЕНИЕ ВЫВОДОВ: ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ +| Вывод | I/O | Назначение: | ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ +| SCL | I | Линия тактирования шины I2C. | +| SDA | I/O | Линия данных шины I2C. | ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ +| FOUT | O | Выход тактового сигнала (32,768 кГц), работает при наличии 1 на выводе FOE. Если на FOE уровень 0 или OPEN, то FOUT=0. | +| FOE | I | Вход управления выводом FOUT. Если FOE=1 то FOUT=32.768кГц. Если FOE=0 то FOUT=0. Вывод FOE толерантен к 5.5В, он внутрисхемно прижат к GND. | ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ +| /INTA | O | N-канальный выходом с открытым стоком, предназначен для вывода прерываний будильника Alarm_D и периодических прерываний. | +| /INTB | O | N-канальный выходом с открытым стоком, предназначен для вывода прерываний будильника Alarm_W. | ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ +| TEST | − | Вывод используется производителем для тестирования. Обязательно подключите к Vcc. | +| VDD | − | Vcc от +1.7В до +5.5В, номинально 3.0В. | +| GND | − | GND. | +| N.C. | − | На чипе RX-8025NB контакты N.C. соеденены друг с другом. Оставьте их неподключёнными или подключите все к Vcc или все к GND. | ++-------+-----+----------------------------------------------------------------------------------------------------------------------------------------------+ + +ЛОГИЧЕСКИЕ УРОВНИ: +Vin 0 = 0В...0,2Vcc +Vin 1 = 0,8Vcc...5,5В + +ФУНКЦИИ МОДУЛЯ: + +1) ТЕКУЩЕЕ ВРЕМЯ: + Модуль позволяет устанавливать, синхронизировать и отображать данные, включая год (последние две цифры), месяц, дату, день, час, минуту и ​​секунду. + Любой (двузначный) год, кратный 4, считается високосным и рассчитывается автоматически до 2099 года. +2) ФУНКЦИЯ РЕГУЛИРОВКИ ТОЧНОСТИ ЧАСОВ: + Точность часов можно отрегулировать вперед или назад изменив частоту с кварца 32,768 кГц с шагом ±3,05 × 10^–6. + Для регулировки требуется записать в регистр Digital Offset знаковое значение в 7-битном двоичном коде (от −62 до +63). + На предделитель часов поступит частота = 32768 Гц - DigitalOffset * 3,05 × 10^–6. + Примечание: Можно настроить только точность часов. Настройки не влияют на выходной сигнал 32,768 кГц с вывода FOUT. +3) ФУНКЦИЯ ПЕРИОДИЧЕСКОГО ПРЕРЫВАНИЯ: + В дополнение к функции будильника Alarm_D через вывод /INTA могут выводиться периодические прерывания. + Периодическое прерывание настраивается битами CT2-CT0 регистра Control-1. + При срабатывании прерывания устанавливается флаг CTFG регистра Control-2, а на выводе /INTA устанавливается уровень логического 0. + Сброс флага CTFG освобождает вывод /INTA до следующего прерывания. + Биты CT2-CT0 позволяют выбрать частоту прерываний: 2 Гц (пол секунды), 1 Гц (ежесекундно), 1/60 Гц (ежеминутно), ежечасно или ежемесячно. +4) ФУНКЦИИ БУДИЛЬНИКА: + Модуль оснащен двумя будильниками (Alarm_D и Alarm_W), которые выводят сигнал прерывания (на выводы /INTA, /INTB) при совпадении времени. + • Будильник Alarm_W на основе дня недели, часа и минут, выводит сигнал прерывания через вывод /INTB. Будильник позволяет выбрать несколько деней недели. + Будильник срабатывает если он включён битом WALE регистра Control-1, при совпадении времени будильника с текущим временем. + При срабатывании будильника устанавливается флаг WAFG регистра Control-2, а на выводе /INTB устанавливается уровень логического 0. + Сброс флага WAFG освобождает вывод /INTB до следующего прерывания. + Если для будильника установить текущее время, то он сработает не немедленно, а при следующем совпадении времени. + Для установки нового времени будильника рекомендуется его отключить WALE=0, установить время будильника Alarm_W Minute/Hour/Day, сбросить прерывание будильника WAFG=0, включить будильник WALE=1. + • Будильник Alarm_D использует только часы и минуты, выводит сигнал прерывания через вывод /INTA. + Будильник срабатывает если он включён битом DALE регистра Control-1, при совпадении времени будильника с текущим временем. + При срабатывании будильника устанавливается флаг DAFG регистра Control-2, в на выводе /INTA устанавливается уровень логического 0. + Сброс флага DAFG освобождает вывод /INTA до следующего прерывания. + Если для будильника установить текущее время, то он сработает не немедленно, а при следующем совпадении времени. + Для установки нового времени будильника рекомендуется его отключить DALE=0, установить время будильника Alarm_D Minute/Hour, сбросить прерывание будильника DAFG=0, включить будильник DALE=1. +5) ФУНКЦИЯ ОБНАРУЖЕНИЯ ОСТАНОВКИ КОЛЕБАНИЙ: + При обнаружении остановки колебаний устанавливается флаг /XST регистра Control-2 (активный уровень 0) и остаётся в таком состоянии до его сброва в 1 по шине I2C. + Это позволяет определить, что данные часов стали некорректными. +6) ФУНКЦИЯ ОБНАРУЖЕНИЯ ПАДЕНИЯ МОЩНОСТИ (КОНТРОЛЬ НАПРЯЖЕНИЯ): + При снижении Vcc ниже порогового уровня, устанавливается флаг VDET регистра Control-2 и остаётся в таком состоянии до его перезаписи в 0 по шине I2C. + Выборка напряжения выполняется один раз в секунду. + Пороговый уровень выбирается битом VDSL регистра Control-2, доступны значения 1.3В и 2.1 В. +7) ФУНКЦИЯ ОБНАРУЖЕНИЯ СБРОСА ПРИ ВКЛЮЧЕНИИ ПИТАНИЯ: + При подаче питания происходит сброс регистров в значение по умолчанию и установка флага PON регистра Control-2 в значение 1, это значение остается до его перезаписи в 0 по шине I2C. + Пока флаг PON установлен в 1, все биты регистров Digital-Offset, Control-1 и Control-2 (кроме PON и /XST) сбрасываются в «0». Это также приводит к остановке прерываний на выводах /INTA и /INTB. + Для начала работы необходимо сбросить флаг PON в 0, что перезапустит функцию обнаружения сброса при включении питания. +8) ТАКТОВЫЙ ВЫХОД 32,768 кГц + Тактовая частота 32,768 кГц (с точностью, равной точности встроенного кварцевого генератора) может выводиться через вывод с открытым стоком FOUT. + Вывод FOUT включается подачей 1 на вход FOE. Если на вход FOE подать 0, то и на выходе FOUT будет 0. +9) ИНТЕРФЕЙС + Данные считываются и записываются через интерфейс шины I2C. Максимальная тактовая частота 400 кГц (при Vcc ≥ 1,7 В). + +КАРТА РЕГИСТРОВ: ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +|Address| Function | 7 6 5 4 3 2 1 0 | ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +| 0 | Seconds | (0) S40 S20 S10 S8 S4 S2 S1 | +| 1 | Minutes | (0) M40 M20 M10 M8 M4 M2 M1 | +| 2 | Hours | (0) (0) H20 P,/A H10 H8 H4 H2 H1 | +| 3 | Days | (0) (0) (0) (0) (0) W4 W2 W1 | Так написано в datasheet +| | | (0) Сб Пт Чт Ср Вт Пн Вс | А так на самом деле. И модуль не определяет день недели, а тупо его увеличивает после 23:59:59 +| 4 | Days | (0) (0) D20 D10 D8 D4 D2 D1 | +| 5 | Months | «0» (0) (0) MO10 MO8 MO4 MO2 MO1 | +| 6 | Years | Y80 Y40 Y20 Y10 Y8 Y4 Y2 Y1 | ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +| 7 | Digital Offset | «0» F6 F5 F4 F3 F2 F1 F0 | Значения по умолчанию (с момента установки бита PON) 00000000 ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +| 8 | Alarm_W Minute | (0) WM40 WM20 WM10 WM8 WM4 WM2 WM1 | +| 9 | Alarm_W Hour | (0) (0) WH20 WP,/A WH10 WH8 WH4 WH2 WH1 | +| A | Alarm_W Day | (0) WW6 WW5 WW4 WW3 WW2 WW1 WW0 | +| B | Alarm_D Minute | (0) DM40 DM20 DM10 DM8 DM4 DM2 DM1 | +| C | Alarm_D Hour | (0) (0) DH20 DP,/A DH10 DH8 DH4 DH2 DH1 | ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +| D | Reserved | - - - - - - - - | ++-------+-------------------+---------------------------------------------------------------------------------------------------+ +| E | Control-1 | WALE DALE /12,24 • TEST CT2 CT1 CT0 | Значения по умолчанию (с момента установки бита PON) 00000000 +| F | Control-2 | VDSL VDET /XST PON • CTFG WAFG DAFG | Значения по умолчанию (с момента установки бита PON) 00?10000 ++-------+-------------------+---------------------------------------------------------------------------------------------------+ + +Осторожно: + Бит PON Является битом флага сброса при включении питания. Устанавливается в 1, когда происходит сброс, например, при первоначальном включении питания или при восстановлении после падения напряжения питания. + При этом все биты в регистрах Control-1 и Control-2, кроме битов PON и /XST, сбрасываются в 0. + Примечание: Пока бит PON не установится в 1 все остальные значения регистров не определены, поэтому обязательно выполните сброс перед использованием модуля. + Кроме того, избегайте ввода неверных данных о дате и времени, так как работа часов не гарантируется, если данные о времени неверны. + Бит TEST Используется производителем для тестирования. Убедитесь, что для этого бита установлено значение «0». + Reserved Регистр с адресом D используется для заводских настроек. Не читайте и не записывайте в этот регистр. + Биты «0» Должны быть установлены в 0 при записи. Их значение при чтении будет 0. + Биты (0) Доступны только для чтения. Их значение при чтении всегда равно 0. + Биты • Являются битами ОЗУ, которые могут содержать любое значение и доступны для чтения/записи. Однако эти биты сбрасываются в 0, когда значение бита PON равно 1. + +0) Seconds Регистр счётчика секунд в двоично-десятичном формате (0b X101 1001 = 59сек). +1) Minutes Регистр счётчика минут в двоично-десятичном формате (0b X101 1001 = 59мин). +2) Hours Регистр счётчика часов в двоично-десятичном формате (0b XX01 1001 = 19час). + H20 P,/A В 24-часовом формате бит функционирует как H20 + В 12-часовом формате бит функционирует как P,/A (0-AM, 1-PM). + Формат часов задаёт бит /12,24 регистра Control-1. +3) Days Регистр счётчика дней недели (1-Пн, 6-Сб, 0-Вс). Значение 7-недопустимо. Так написано в datasheet, типа у регистра активны только 3 младших бита формирующих число от 0 до 6. + А на самом деле регистр хранит 7 значащих бит. Установленный бит указывает на день недели: 0-бит Вс, 1-бит Пн, 2-бит Вт, ... 6-бит Сб, 7-бит всегда 0. +4) Days Регистр счётчика дней в двоично-десятичном формате (0b XX11 0001 = 31день). +5) Months Регистр счётчика месяцев в двоично-десятичном формате (0b XXX1 0010 = 12мес = декабрь). +6) Years Регистр счётчика лет в двоично-десятичном формате (0b 1001 1001 = 99год). +7) Digital Offset Регистр регулировки точности часов. Хранит знаковое значение в 7-битном двоичном коде (от −62 = 0bX1000010, до +63 = 0bX0111111) + Частота поступающая на предделитель = 32768 Гц - DigitalOffset * 3,05 × 10^–6. (32768Гц−62*3,05×10^–6 = 32768Гц+189.1×10^–6) + Функция цифрового смещения корректирует количество тактовых импульсов каждые 20 сек. (в 00сек, 20сек и 40сек каждой минуты) + Частота на выходе FOUT не изменяется. +8) Alarm_W Minute Регистр минут будильника Alarm_W. Формат хранения данных аналогичен регистру Minutes. +9) Alarm_W Hour Регистр часов будильника Alarm_W. Формат хранения данных аналогичен регистру Hours. + WH20 WP,/A В 24-часовом формате бит функционирует как WH20 + В 12-часовом формате бит функционирует как WP,/A (0-AM, 1-PM). + Формат часов задаёт бит /12,24 регистра Control-1. +A) Alarm_W Day Регистр дней недели будильника Alarm_W. Позволяет выбрать 1 до 7 дней недели. + WW6-WW0 WW1-Пн, WW2-Вт, WW3-ср, WW4-Чт, WW5-Пт, WW6-Сб, WW0-Вс. + Если все биты установлены в 0, то будильник Alarm_W выключен. +B) Alarm_D Minute Регистр минут будильника Alarm_D. Формат хранения данных аналогичен регистру Minutes. +C) Alarm_D Hour Регистр часов будильника Alarm_D. Формат хранения данных аналогичен регистру Hours. + DH20 DP,/A В 24-часовом формате бит функционирует как DH20 + В 12-часовом формате бит функционирует как DP,/A (0-AM, 1-PM). + Формат часов задаёт бит /12,24 регистра Control-1. +E) Control-1 Регистр управления 1 + WALE Бит разрешения работы будильника Alarm_W. + 0 - будильник Alarm_W отключён. + 1 - будильник Alarm_W включён, при совпадении времени на выводе /INTB появляется прерывание. + DALE Бит разрешения работы будильника Alarm_D. + 0 - будильник Alarm_D отключён. + 1 - будильник Alarm_D включён, при совпадении времени на выводе /INTA появляется прерывание. + /12,24 Бит выбора формата часов текущего времени и будильников. + 0 - Часы текущего времени и будильников хранятся в 12-часовом формате. + 1 - Часы текущего времени и будильников хранятся в 24-часовом формате. + • + TEST + CT2-CT0 Биты настройки периодического прерывания на выводе /INTA. + 000 − Периодическое прерывание отключено, вывод /INTA отключён. + 001 − Периодическое прерывание отключено, на выволе /INTA уровень 0. + 010 - Периодическое прерывание работает на частоте 2 Гц с коэффициентом заполнения 50%. + 011 - Периодическое прерывание работает на частоте 1 Гц с коэффициентом заполнения 50%. + 100 - Периодическое прерывание срабатывает в начале каждой секунды. + 101 - Периодическое прерывание срабатывает в начале 00 секунды каждой минуты. + 110 - Периодическое прерывание срабатывает в начале 00 секунды, 00 минуты каждого часа. + 111 - Периодическое прерывание срабатывает в начале 00 секунды, 00 минуты 00 часа каждого первого числа месяца. +F) Control-2 Регистр управления 2 + VDSL Выбор порогового значения напряжения для функции обнаружения падения мощности. + 0 - 2,1 В в качестве порога функции обнаружения падения мощности. + 1 - 1,3 В в качестве порога функции обнаружения падения мощности. + VDET Флаг функции обнаружения падения мощности. Устанавливается в 1 после падения Vcc ниже порога. + Чтение: 0 - Падение мощности не обнаружено. + 1 - Обнаружено падение Vcc ниже порогового. Флаг остаётся 1 до его перезаписи в 0 по шине I2C. + Запись: 0 - Сброс флага в 0 и перезапуск функции обнаружения падения мощности. + 1 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 1 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + /XST Флаг функции обнаружения остановки колебаний. + Чтение: 0 - Обнаружена остановка колебаний. Флаг остаётся 0 до его перезаписи в 1 по шине I2C. + 1 - Остановка колебаний не обнаружено. + Запись: 0 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 0 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + 1 - Сброс флага в 1 и перезапуск функции обнаружения остановки колебаний. + PON Флаг функции обнаружения сброса при включении питания. + Чтение: 0 - Сброс при включении питания не обнаружен. + 1 - Обнаружен сброс при включении питания. Флаг остаётся 1 до его перезаписи в 0 по шине I2C. + Пока флаг установлен в 1, все биты регистров Digital-Offset, Control-1 и Control-2 (кроме PON и /XST) сбрасываются в «0». Это также приводит к остановке прерываний на выводах /INTA и /INTB. + Запись: 0 - Сброс флага в 0 и перезапуск функции обнаружения сброса при включении питания. + 1 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 1 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + • Бит является битом ОЗУ, он может содержать любое значение и доступен для чтения/записи. Бит сбрасывается в 0 если обнаружен сброс при включении питания (PON=1). + CTFG Флаг наличия периодического прерывания на выводе /INTA. + Чтение: 0 - На выходе /INTA нет сигнала периодического прерывания. + 1 - На выходе /INTA установлен сигнал периодического прерывания. + Запись: 0 - Сброс флага в 0 и освобождение /INTA до следующего прерывания. Сброс возможен если не поступает очередное прерывание и не срабатывает будильник Alarm_D. + 1 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 1 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + WAFG Флаг будильника Alarm_W. Значение флага актуально если бит WALE регистра Control-1 равен 1. + Чтение: 0 - Будильник Alarm_W не сработал или бит WALE регистра Control-1 равен 0. + 1 - Сработал будильник Alarm_W. Флаг остаётся 1 до его перезаписи в 0 по шине I2C. + Запись: 0 - Сброс флага в 0 и отключение прерывания на выводе /INTB. + 1 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 1 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + DAFG Флаг будильника Alarm_D. Значение флага актуально если бит DALE регистра Control-1 равен 1. + Чтение: 0 - Будильник Alarm_D не сработал или бит DALE регистра Control-1 равен 0. + 1 - Сработал будильник Alarm_D. Флаг остаётся 1 до его перезаписи в 0 по шине I2C. + Запись: 0 - Сброс флага в 0 и отключение прерывания на выводе /INTA. + 1 - ЗАПРЕЩЕНО! НЕ ЗАПИСЫВАЙТЕ 1 ДАЖЕ ЕСЛИ ЭТО НЕ ИМЕЕТ ЭФФЕКТА. + diff --git a/lib/iarduino_RTC/keywords.txt b/lib/iarduino_RTC/keywords.txt new file mode 100644 index 00000000..5345352b --- /dev/null +++ b/lib/iarduino_RTC/keywords.txt @@ -0,0 +1,30 @@ +# #################################################### +# СИНТАКСИЧЕСКАЯ РАСКРАСКА ДЛЯ БИБИЛИОТЕКИ: +# iarduino_RTC +# #################################################### +# ТИПЫ ДАННЫХ: (KEYWORD1) + iarduino_RTC KEYWORD1 +# #################################################### +# МЕТОДЫ И ФУНКЦИИ: (KEYWORD2) + begin KEYWORD2 + settime KEYWORD2 + gettime KEYWORD2 + settimeUnix KEYWORD2 + gettimeUnix KEYWORD2 + blinktime KEYWORD2 + period KEYWORD2 + seconds KEYWORD2 + minutes KEYWORD2 + hours KEYWORD2 + Hours KEYWORD2 + midday KEYWORD2 + day KEYWORD2 + weekday KEYWORD2 + month KEYWORD2 + year KEYWORD2 + Unix KEYWORD2 +# #################################################### +# КОНСТАНТЫ: (LITERAL1) + RTC_DS1302 LITERAL1 + RTC_DS1307 LITERAL1 + RTC_DS3231 LITERAL1 \ No newline at end of file diff --git a/lib/iarduino_RTC/library.properties b/lib/iarduino_RTC/library.properties new file mode 100644 index 00000000..21f3cc8c --- /dev/null +++ b/lib/iarduino_RTC/library.properties @@ -0,0 +1,10 @@ +name = iarduino RTC (часы реального времени) +version = 2.0.0 +author = iarduino +maintainer = Панькин Павел +sentence = Библиотека для работы с часами реального времени. +paragraph = Поддерживает часы на чипах: DS1302, DS1307, DS3231, RX8025, есть возможность добавлять часы на других чипах. Библиотека позволяет устанавливать и считывать дату/время. Преимуществом данной библиотеки является удобная реализация получения времени. Считывание даты/времени можно осуществить как через переменные (year, month, day, hours, minutes, seconds и т.д.), так и одной строкой в которую попадут значения в соответствии с указанным Вами шаблоном. +category = Timing +url = http://iarduino.ru/file/235.html +architectures = avr,esp8266,esp32 +includes = iarduino_RTC.h \ No newline at end of file diff --git a/lib/iarduino_RTC/src/iarduino_RTC.cpp b/lib/iarduino_RTC/src/iarduino_RTC.cpp new file mode 100644 index 00000000..959c79bd --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC.cpp @@ -0,0 +1,134 @@ +#include "iarduino_RTC.h" + +// Вывод даты и времени +char* iarduino_RTC::gettime(String i){char j[i.length()+1]; i.toCharArray(j, i.length()); j[i.length()]=0; return gettime(j);} +char* iarduino_RTC::gettime(const char* i){ + uint8_t j, k, n; bool f; // Объявляем локальные переменные + if(valRequest > millis()){valRequest=0;} // Избавляемся от переполнения millis() +// Получаем текущее время: + if(valPeriod == 0) {funcReadTime();}else // Если минимальный период опроса модуля == 0 минут, то получаем время из регистров модуля, иначе ... + if(valRequest == 0 || (valPeriod+valRequest <= millis())) {funcReadTime();}else // Если время последнего опроса модуля превысило минимальный период опроса модуля, то получаем время из регистров модуля, иначе ... + {funcCalculationTime();} // Получаем время без обращения к модулю (время рассчитывая исходя из millis и valRequest) + funcSetMoreTime(); // Преобразуем переменные не читаемые из модуля +// Вычисляем размер блока памяти под строку с ответом: + n=strlen(i)+1; // Определяем размер равный введённой строке (i) + 1 символ конца строки + for(j=0; j0){n++;} if(k>9){n++;} if(k>11){n++;} // Увеличиваем размер (n) в соостветствии со значениём найденного служебного символа + }}} +// Выделяем блок памяти под строку с ответом: + free(charReturn); // Освобождаем ранее созданный блок памяти + charReturn = (char*) malloc(n); // Выделяем новый блок памяти размером n байт +// Заполняем выделенный блок памяти: + n=0; // Определяем позицию в блоке памяти для следующего подставляемого значения + for(j=0; j6 ){i=6; } charReturn[n]=f?32:i+48; break; + /* 2 знака */ case 1: if(i>99){i=99;} charReturn[n]=f?32:i/10+48; charReturn[n+1]=f?32:i%10+48; break; + /* AM / PM */ case 2: if(i>6 ){i=6; } charReturn[n]=f?32:charMidday[i]; charReturn[n+1]=f?32:charMidday[i+1]; break; + /* дн / мес */ case 3: if(i>54){i=54;} charReturn[n]=f?32:charDayMon[i]; charReturn[n+1]=f?32:charDayMon[i+1]; charReturn[n+2]=f?32:charDayMon[i+2]; break; + /* 4 знака */ case 4: if(i>99){i=99;} charReturn[n]=f?32:(valCentury-1)/10+48; charReturn[n+1]=f?32:(valCentury-1)%10+48; charReturn[n+2]=f?32:i/10+48; charReturn[n+3]=f?32:i%10+48; break; + } +} + +// Установка даты и времени +void iarduino_RTC::settime(int i1, int i2, int i3, int i4, int i5, int i6, int i7){ // (сек, мин, час, день, мес, год, день_недели) + funcWriteTime(i1, i2, i3, i4, i5, i6, i7); // Записываем дату и время в регистры модуля + funcReadTime(); // Читаем дату и время из регистров модуля + funcSetMoreTime(); // Корректируем переменные не читаемые из модуля (hours, midday) +} + +// Установка даты и времени от начала эпохи Unix +void iarduino_RTC::settimeUnix(uint32_t i){ // (сек) + uint32_t j; uint8_t k; bool f=true; // Объявляем временные переменные. + seconds = i % 60; // Получаем текущее значение секунд. (остаток от деления секунд прошедших с начала эпохи Unix на количество секунд в минуте) + i = (i-seconds) / 60; // Получаем количество минут прошедших с начала эпохи Unix. + minutes = i % 60; // Получаем текущее значение минут. (остаток от деления минут прошедших с начала эпохи Unix на количество минут в часе) + i = (i-minutes) / 60; // Получаем количество часов прошедших с начала эпохи Unix. + Hours = i % 24; // Получаем текущее значение часов. (остаток от деления часов прошедших с начала эпохи Unix на количество часов в дне) + i = (i-Hours) / 24; // Получаем количество дней прошедших с начала эпохи Unix. + j = 0; while((((j+1)*365)+((j+2)/4))<=i){j++;} // Получаем количество лет прошедших с начала эпохи Unix. + weekday = (i+4) % 7; // Получаем текущий день недели. (0-воскресенье, 1-понедельник, ... , 6-суббота) + i = i - (j*365) - ((j+1)/4); // Получаем количество дней прошедших в текущем году. + valCentury = ((1970+j)/100)+1; // Получаем текущий век. + year = (1970+j)%100; // Получаем две последние цифры текущего года. + k = ((1970+j)%4)==0?29:28; // Получаем количество дней в феврале текущего года. + month = 0; while(f){month++; j=month; j=(((j+1)%2)^(j<8))?31:(j==2?k:30); if(i>=j){i-=j;}else{f=false;}} + day = i+1; // Получаем текущий день. + // Устанавливаем время: + settime(seconds, minutes, Hours, day, month, year, weekday); +} + +// Чтение даты и времени в переменные из регистров модуля: +void iarduino_RTC::funcReadTime(void){ // (без параметров) + seconds = arrCalculationTime[0] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(0)); // Получаем секунды + minutes = arrCalculationTime[1] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(1)); // Получаем минуты + Hours = arrCalculationTime[2] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(2)); // Получаем часы + day = arrCalculationTime[3] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(3)); // Получаем день + month = arrCalculationTime[4] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(4)); // Получаем месяц + year = arrCalculationTime[5] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(5)); // Получаем год + weekday = arrCalculationTime[6] = funcConvertCodeToNum(objClass -> funcReadTimeIndex(6))-1; // Получаем день недели (в регистре значение от 1 до 7, а в переменной от 0 до 6) + Unix = funcCalculationUnix(); // Получаем количество секунд (прошедших с начала эпохи Unix) + valRequest = millis(); // Сохраняем время данного запроса +} + +// Запись даты и времени в регистры модуля: +void iarduino_RTC::funcWriteTime(int i1, int i2, int i3, int i4, int i5, int i6, int i7){ // + if(i1<=60 && i1>=0){objClass -> funcWriteTimeIndex(0, funcConvertNumToCode(i1 ));} // Сохраняем секунды + if(i2<=60 && i2>=0){objClass -> funcWriteTimeIndex(1, funcConvertNumToCode(i2 ));} // Сохраняем минуты + if(i3<=23 && i3>=0){objClass -> funcWriteTimeIndex(2, funcConvertNumToCode(i3 ));} // Сохраняем часы + if(i4<=31 && i4>=1){objClass -> funcWriteTimeIndex(3, funcConvertNumToCode(i4 ));} // Сохраняем день + if(i5<=12 && i5>=1){objClass -> funcWriteTimeIndex(4, funcConvertNumToCode(i5 ));} // Сохраняем месяц + if(i6<=99 && i6>=0){objClass -> funcWriteTimeIndex(5, funcConvertNumToCode(i6 ));} // Сохраняем год + if(i7<= 6 && i7>=0){objClass -> funcWriteTimeIndex(6, funcConvertNumToCode(i7+1));} // Сохраняем день недели (в регистре значение от 1 до 7, а в переменной от 0 до 6) +} + +// Расчёт времени без обращения к модулю: +void iarduino_RTC::funcCalculationTime(void){ // (без параметров) + uint32_t i=(millis()-valRequest)/1000; // Определяем количество секунд (прошедших после последнего обращения к модулю) + uint8_t j=30 + ( (arrCalculationTime[4] + (arrCalculationTime[4]>7?1:0)) % 2 ); // Определяем количество дней в месяце (31, 30, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + if(arrCalculationTime[4]==2){j=28+((((uint16_t)valCentury-1)*100+arrCalculationTime[5])%4?0:1);}// Если текущий месяц - февраль, то меняем на ... (31, 28/29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + i+=arrCalculationTime[0]; seconds = i%60; i/=60; // Добавляем к прошедним секундам (i) посление прочитанные секунды (arrCalculationTime[0]), результатом будет остаток, а значение i превращаем в минуты + i+=arrCalculationTime[1]; minutes = i%60; i/=60; // Добавляем к прошедним минутам (i) посление прочитанные минуты (arrCalculationTime[1]), результатом будет остаток, а значение i превращаем в часы + i+=arrCalculationTime[2]; Hours = i%24; i/=24; // Добавляем к прошедним часам (i) посление прочитанные часы (arrCalculationTime[2]), результатом будет остаток, а значение i превращаем в дни + weekday = arrCalculationTime[6]+i; if(weekday>6){weekday=0;} // День недели увеличится на (i) от прочитанного дня недели (arrCalculationTime[6]) + i+=arrCalculationTime[3]; day = i%(j+1); i/=(j+1); day+=i; // Добавляем к прошедним дням (i) посление прочитанные дни (arrCalculationTime[3]), результатом будет остаток, а значение i превращаем в месяцы + i+=arrCalculationTime[4]; month = i%13; i/=13; month+=i; // Добавляем к прошедним месяцам (i) посление прочитанные месяцы (arrCalculationTime[4]), результатом будет остаток, а значение i превращаем в годы + i+=arrCalculationTime[5]; year = i%100; // Добавляем к прошедним годам (i) посление прочитанные годы (arrCalculationTime[5]), результатом будет остаток, а значение i превращаем в дни + Unix = funcCalculationUnix(); // Получаем количество секунд прошедших с начала эпохи Unix +} + +// Расчёт количества cекунд прошедших с начала эпохи Unix: +uint32_t iarduino_RTC::funcCalculationUnix(void){ // (без параметров) + uint32_t i; // Объявляем переменную для хранения результата. (рассчёты производятся из значений переменных: seconds, minutes, Hours, day, month, year и valCentury). + uint32_t j = (uint32_t)(valCentury-1) * 100 + year; // Определяем текущий год с учётом века. (valCentury - век, year - последние два знака текущего года). + i = j - 1970; // Определяем количество прошедших лет (с 01.01.1970 г.) + i = i * 365 + ((i+1)/4); // Определяем количество дней в прошедших годах ((i+1)/4) - количество прошедших високосных лет (без учёта текущего года). + i += (month-1)*30 + ( (month + (month<9?0:1) )/2 ); // Добавляем количество дней в прошедших месяцах ((month+(month<9?0:1))/2) - количество прошедших месяцев текущего года, содержащих 31 день. + i -= month>2? (j%4==0?1:2) : 0; // Вычитаем 1 или 2 дня из февраля текущего года ((month>2) - если февраль уже прошел, j%4==0 - если текущий год високосный) + i += day-1; // Добавляем количество прошедших дней этого месяца + i *= 86400; // Получаем количество секунд прошедших дней + i += (uint32_t)Hours * 3600 + (uint32_t)minutes * 60 + seconds; // Добавляем количество секунд текущего дня + return i; // Возвращаем результат +} diff --git a/lib/iarduino_RTC/src/iarduino_RTC.h b/lib/iarduino_RTC/src/iarduino_RTC.h new file mode 100644 index 00000000..7ab1a5d8 --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC.h @@ -0,0 +1,107 @@ +// Библиотека для работы с часами реального времени: (на чипе DS1302) https://iarduino.ru/shop/Expansion-payments/rtc-modul-ds1302.html +// (на чипе DS1307) https://iarduino.ru/shop/Expansion-payments/kroshechnye-rtc-modul-realnogo-vremeni.html +// https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html +// (на чипе DS3231) https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-ds3231.html +// https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul-v2-0.html +// (на чипе RX8025) +// Версия: 2.0.0 +// Последнюю версию библиотеки Вы можете скачать по ссылке: https://iarduino.ru/file/235.html +// Подробное описание функции бибилиотеки доступно по ссылке: https://wiki.iarduino.ru/page/chasy-realnogo-vremeni-rtc-trema-modul/ +// Библиотека является собственностью интернет магазина iarduino.ru и может свободно использоваться и распространяться! +// При публикации устройств или скетчей с использованием данной библиотеки, как целиком, так и её частей, +// в том числе и в некоммерческих целях, просим Вас опубликовать ссылку: https://iarduino.ru +// Автор библиотеки: Панькин Павел +// Если у Вас возникли технические вопросы, напишите нам: shop@iarduino.ru + +#ifndef iarduino_RTC_h // +#define iarduino_RTC_h // + // +#define RTC_UNDEFINED 0 // Модуль часов реального времени не определён + // +#if defined(ARDUINO) && (ARDUINO >= 100) // +#include // +#else // +#include // +#endif // + // +#include "memorysaver.h" // Подключаем файл «хранитель памяти» (внутри файла есть комментарий поясняющий как сэкономить мапять) + // +class iarduino_RTC_BASE{ // Объявляем полиморфный класс + public: // + virtual void begin (void); // Объявляем функцию инициализации модуля (без параметров) + virtual uint8_t funcReadTimeIndex (uint8_t); // Объявляем функцию чтения 1 значения из регистров даты и времени (0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели) + virtual void funcWriteTimeIndex (uint8_t, uint8_t); // Объявляем функцию записи 1 значения в регистры даты и времени (0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, значение) +}; // + // +#include "iarduino_RTC_DS1302.h" // Подключаем файл iarduino_RTC_DS1302.h +#include "iarduino_RTC_DS1307.h" // Подключаем файл iarduino_RTC_DS1307.h +#include "iarduino_RTC_DS3231.h" // Подключаем файл iarduino_RTC_DS3231.h +#include "iarduino_RTC_RX8025.h" // Подключаем файл iarduino_RTC_RX8025.h + // +class iarduino_RTC{ // + public: // + /** Конструктор класса **/ // + iarduino_RTC(uint8_t i, uint8_t j=SS, uint8_t k=SCK, uint8_t n=MOSI){ // Конструктор основного класса (название [, вывод SS/RST [, вывод SCK/CLK [, вывод MOSI/DAT]]]) + switch(i){ // Тип выбранного модуля + #ifdef RTC_ENABLE_DS1302 // + case RTC_DS1302: objClass = new iarduino_RTC_DS1302(j,k,n); break; // Если используется модуль на базе чипа DS1302, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS1302 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE + #endif // + #ifdef RTC_ENABLE_DS1307 // + case RTC_DS1307: objClass = new iarduino_RTC_DS1307; break; // Если используется модуль на базе чипа DS1307, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS1307 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE + #endif // + #ifdef RTC_ENABLE_DS3231 // + case RTC_DS3231: objClass = new iarduino_RTC_DS3231; break; // Если используется модуль на базе чипа DS3231, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_DS3231 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE + #endif // + #ifdef RTC_ENABLE_RX8025 // + case RTC_RX8025: objClass = new iarduino_RTC_RX8025; break; // Если используется модуль на базе чипа RX8025, то присваеиваем указателю objClass ссылку на новый объект производного класса iarduino_RTC_RX8025 переопределяя на него все виртуальные функции полиморфного класса iarduino_RTC_BASE + #endif // + } // + } // + /** Пользовательские функции **/ // + void begin (void) {objClass -> begin(); gettime();} // Определяем функцию инициализации модуля (без параметров) + void period (uint8_t i) {valPeriod=i; valPeriod*=60000;} // Определяем функцию задания минимального периода обращения к модулю (i = период в минутах) + void blinktime (uint8_t i, float j=1) {valBlink=i; valFrequency=uint32_t(1000/j);} // Определяем функцию позволяющую мигать одним из параметров времени (i = 0-нет / 1-сек / 2-мин / 3-час / 4-день / 5-мес / 6-год / 7-день_недели / 8-полдень , j = частота мигания в Гц) + void gettime (void) {gettime("");} // Определяем функцию получения даты и времени из переменных (без параметров) + char* gettime (String); // Объявляем функцию «дублёр» получения даты и времени из переменных (строка с параметрами) + char* gettime (const char*); // Объявляем функцию получения даты и времени ввиде строки (строка с параметрами) + void settime (int, int=-1, int=-1, int=-1, int=-1, int=-1, int=-1); // Объявляем функцию установки даты и времени (сек, мин, час, день, мес, год, день_недели) + uint32_t gettimeUnix (void) {gettime(""); return Unix;} // Определяем функцию получения cекунд прошедших с начала эпохи Unix (без параметров) + void settimeUnix (uint32_t); // Объявляем функцию установки cекунд прошедших с начала эпохи Unix (сек) + // + /** Переменные доступные для пользователя **/ // + uint8_t seconds = 0; // Секунды 0-59 + uint8_t minutes = 0; // Минуты 0-59 + uint8_t hours = 1; // Часы 1-12 + uint8_t Hours = 0; // Часы 0-23 + uint8_t midday = 0; // Полдень 0-1 (0-am, 1-pm) + uint8_t day = 1; // День месяца 1-31 + uint8_t weekday = 0; // День недели 0-6 (0-воскресенье, 1-понедельник, ... , 6-суббота) + uint8_t month = 1; // Месяц 1-12 + uint8_t year = 0; // Год 0-99 (без учёта века) + uint32_t Unix = 0; // Секунды прошедшие с начала эпохи Unix (01.01.1970 00:00:00 GMT) + // + /** Внутренние переменные **/ // + iarduino_RTC_BASE* objClass; // Объявляем указатель на объект полиморфного класса (функции данного класса будут переопределены, т.к. указателю будет присвоена ссылка на производный класс) + char* charReturn = (char*) malloc(1); // Определяем указатель на символьную область памяти в 1 байт (указатель будет ссылаться на строку вывода времени) + const char* charInput = "waAdhHimsyMDY"; // Определяем константу-строку с символами требующими замены (данные символы заменяются функцией gettime на значение времени) + const char* charMidday = "ampmAMPM"; // Определяем константу-строку для вывода полудня (am / pm / AM / PM) + const char* charDayMon = "SunMonTueWedThuFriSatJanFebMarAprMayJunJulAugSepOctNovDec"; // Определяем константу-строку для вывода дня недели или месяца (Mon ... Sun / Jan ... Dec) + uint8_t arrCalculationTime[7]; // Объявляем массив для рассчёта времени без обращения к модулю (для хранения последних, прочитанных из модуля, значений даты и времени) + uint8_t valBlink = 0; // Определяем параметр времени, который должен мигать (1-сек / 2-мин / 3-час / 4-день / 5-мес / 6-год / 7-день_недели / 8-полдень) + uint32_t valFrequency = 1000; // Определяем частоту мигания параметра времени для функции blinktime (по умолчанию 1 Гц) + uint8_t valCentury = 21; // Определяем переменную для хранения текущего века (по умолчанию 21 век) + uint16_t valPeriod = 0; // Определяем минимальный период опроса модуля (в минутах, от 00 до 255) + uint32_t valRequest = 0; // Определяем время последнего чтения регистров времени + private: // + /** Внутренние функции **/ // + void funcReadTime (void); // Объявляем функцию чтения даты и времени из регистров модуля (без параметров) + void funcWriteTime (int, int, int, int, int, int, int); // Объявляем функцию записи даты и времени в регистры модуля (без параметров) + uint8_t funcConvertCodeToNum (uint8_t i) {return (i >> 4)*10 + (i & 0x0F);} // Определяем функцию преобразования двоично-десятичного кода в число (код) + uint8_t funcConvertNumToCode (uint8_t i) {return ((i/10) << 4) + (i%10);} // Определяем функцию преобразования числа в двоично-десятичный код (число) + void funcSetMoreTime (void){hours=(Hours%12)?(Hours%12):12; midday=(Hours<12)?0:1;} // Корректировка переменных не читаемых из модуля (без параметров) + void funcCalculationTime (void); // Объявляем функцию расчёта времени без обращения к модулю (без параметров) + uint32_t funcCalculationUnix (void); // Объявляем функцию расчёта cекунд прошедших с начала эпохи Unix (без параметров) + void funcFillChar (uint8_t, uint8_t, uint8_t, uint8_t); // Объявляем функцию заполнения строки вывода времени (данные, тип данных, позиция для вставки, мигание) +}; // + // +#endif // \ No newline at end of file diff --git a/lib/iarduino_RTC/src/iarduino_RTC_DS1302.h b/lib/iarduino_RTC/src/iarduino_RTC_DS1302.h new file mode 100644 index 00000000..bc71fc04 --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC_DS1302.h @@ -0,0 +1,67 @@ +#ifndef iarduino_RTC_DS1302_h // +#define iarduino_RTC_DS1302_h // +#define RTC_DS1302 1 // Модуль часов реального времени с протоколом передачи данных SI3, памятью 040x8 (31 байт которой доступны для хранения данных пользователя) + // +class iarduino_RTC_DS1302: public iarduino_RTC_BASE{ // + public: // + /** Конструктор класса **/ // + iarduino_RTC_DS1302(uint8_t i=SS, uint8_t j=SCK, uint8_t k=MOSI){pinRES=i; pinCLK=j; pinDAT=k;} // (вывод RST, вывод CLK, вывод DAT) + /** функции доступные пользователю **/ // +// Инициализация модуля: // + void begin(void){ // (без параметров) +// Инициализация работы с трехпроводной шиной: // + funcBegin(); // (без параметров) +// Установка флагов управления и состояния модуля: // + varI=funcReadReg(0x81); if( varI & 0b10000000){funcWriteReg(0x81, (varI&~0b10000000));} // (если установлен 7 бит в 129 регистре, то сбрасываем его - запускаем генератор) + varI=funcReadReg(0x85); if( varI & 0b10000000){funcWriteReg(0x85, (varI&~0b10000000));} // (если установлен 7 бит в 133 регистре, то сбрасываем его - переводим модуль в 24 часовой режим) + varI=funcReadReg(0x8F); if( varI & 0b10000000){funcWriteReg(0x8F, (varI&~0b10000000));} // (если установлен 7 бит в 143 регистре, то сбрасываем его - разрешаем запись в регистры модуля) + } // + // +// Чтение одного значения из регистров даты и времени модуля: // + uint8_t funcReadTimeIndex(uint8_t i){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели) + delay(1); return funcReadReg(arrTimeRegAddr[i]) & arrTimeRegMack[i]; // + } // + // +// Запись одного значения в регистры даты и времени модуля: // + void funcWriteTimeIndex(uint8_t i, uint8_t j){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, j = значение) + varI=funcReadTimeIndex(i); // Читаем данные из регистра i + j |= ~arrTimeRegMack[i] & varI; // Устанавливаем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + j &= arrTimeRegMack[i] | varI; // Сбрасываем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + funcWriteReg(arrTimeRegAddr[i], j); // Сохраняем значение j в регистр arrTimeRegAddr[i] + } // + // + private: // + /** Внутренние переменные **/ // + uint8_t pinRES = 0; // Определяем переменную для хранения номера вывода RST трехпроводной шины + uint8_t pinCLK = 0; // Определяем переменную для хранения номера вывода CLK трехпроводной шины + uint8_t pinDAT = 0; // Определяем переменную для хранения номера вывода DAT трехпроводной шины + uint8_t arrTimeRegAddr[7] = {0x81,0x83,0x85,0x87,0x89,0x8D,0x8B}; // Определяем массив с адресами регистров чтения даты и времени (сек, мин, час, день, месяц, год, день недели) + uint8_t arrTimeRegMack[7] = {0x7F,0x7F,0x3F,0x3F,0x1F,0xFF,0x07}; // Определяем маскировочный массив для регистров даты и времени (при чтении/записи, нужно совершить побитовое «и») + uint8_t busRate = 10; // Скорость передачи данных трехпроводной шине в кГц (до 255 кГц) + uint8_t varI; // + // + /** Внутренние функции **/ // +// Функция чтения данных из регистра модуля: // + uint8_t funcReadReg(uint8_t i){ // Определяем функцию читения данных из регистра модуля (аргумент: адрес_регистра) + varI=1; // Предустанавливаем переменную varI в значение 1, чтоб не вывести: 45 апреля 255 часов 127 минут и 200 секунд + digitalWrite (pinRES, 1); // Устанавливаем линию RES в активное состояние + funcWriteByte (i); // Отправляем адрес регистра + varI=funcReadByte (true); // Читаем байт из регистра с учётом предустановленного бита + digitalWrite (pinRES, 0); // Устанавливаем линию RES в неактивное состояние + return varI; // Возвращаем значение переменной varI + } // +// Функция записи данных в регистр модуля: // + void funcWriteReg(uint8_t i, uint8_t j){ // Определяем функцию записи данных в регистр модуля (аргументы: адрес_регистра, байт_данных) + digitalWrite (pinRES, 1); // Устанавливаем линию RES в активное состояние + funcWriteByte (i-1); // Отправляем адрес регистра (при чтении указывается адрес-1) + funcWriteByte (j); // Отправляем байт данных + digitalWrite (pinRES, 0); // Устанавливаем линию RES в неактивное состояние + } // + // + /** функции для работы с трехпроводной шиной **/ // + void funcWriteByte (uint8_t j) /* Передача одного байта (байт для передачи) */ {uint8_t i=0, n=500/busRate+1; pinMode(pinDAT, OUTPUT); while(i>=0 && i<8){digitalWrite(pinDAT, (j & _BV(i))); delayMicroseconds(n); digitalWrite(pinCLK, 1); delayMicroseconds(n); digitalWrite(pinCLK, 0); i++;} pinMode(pinDAT, INPUT);} + uint8_t funcReadByte (bool j) /* Получение одного байта (флаг чтения предустановленного бита с линии DAT) */ {uint8_t i=0, k=0, n=500/busRate+1; pinMode(pinDAT, INPUT); if(j){if(digitalRead(pinDAT)){k |= _BV(i);} i++;} while(i>=0 && i<8){digitalWrite(pinCLK, 1); delayMicroseconds(n); digitalWrite(pinCLK, 0); delayMicroseconds(n); if(digitalRead(pinDAT)){k |= _BV(i);} i++;} return k;} + void funcBegin (void) /* Подготовка выводов шины (без параметров) */ {pinMode(pinRES, OUTPUT); pinMode(pinCLK, OUTPUT); pinMode(pinDAT, INPUT); digitalWrite(pinCLK, 0); digitalWrite(pinRES, 0);} +}; // + // +#endif // \ No newline at end of file diff --git a/lib/iarduino_RTC/src/iarduino_RTC_DS1307.h b/lib/iarduino_RTC/src/iarduino_RTC_DS1307.h new file mode 100644 index 00000000..b742f9bf --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC_DS1307.h @@ -0,0 +1,42 @@ +#ifndef iarduino_RTC_DS1307_h // +#define iarduino_RTC_DS1307_h // +#define RTC_DS1307 2 // Модуль часов реального времени с протоколом передачи данных I2C, памятью 064x8 (56 байт которой доступны для хранения данных пользователя) +#include "iarduino_RTC_I2C.h" // Подключаем файл iarduino_RTC_I2C.h - для работы с шиной I2C + // +class iarduino_RTC_DS1307: public iarduino_RTC_BASE{ // + public: // + /** функции доступные пользователю **/ // +// Инициализация модуля: // + void begin(void){ // (без параметров) +// Инициализация работы с шиной I2C: // + objI2C.begin(100); // (скорость шины в кГц) +// Установка флагов управления и состояния модуля: // + varI=objI2C.readByte(valAddress, 0x00); if( varI & 0b10000000 ){objI2C.writeByte(valAddress, 0x00, (varI&~0b10000000) );} // (если установлен 7 бит в 0 регистре, то сбрасываем его - запускаем генератор) + varI=objI2C.readByte(valAddress, 0x02); if( varI & 0b01000000 ){objI2C.writeByte(valAddress, 0x02, (varI&~0b01000000) );} // (если установлен 6 бит в 2 регистре, то сбрасываем его - переводим модуль в 24 часовой режим) + varI=objI2C.readByte(valAddress, 0x07); if((varI & 0b00000011) || !(varI & 0b00010000)){objI2C.writeByte(valAddress, 0x07, (varI&~0b00000011)|0b00010000 );} // (если установлены 1 и 0 биты или сброшен 4 бит в 7 регистре, то сбрасываем 1 с 0 битами, а 4 устанавливаем - выводим меандр с частотой 1 Гц на выводе SQW/OUT модуля) + } // + // +// Чтение одного значения из регистров даты и времени модуля: // + uint8_t funcReadTimeIndex(uint8_t i){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели) + delay(1); // + return objI2C.readByte(valAddress, arrTimeRegAddr[i]) & arrTimeRegMack[i]; // + } // + // +// Запись одного значения в регистры даты и времени модуля: // + void funcWriteTimeIndex(uint8_t i, uint8_t j){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, j = значение) + varI=funcReadTimeIndex(i); // Читаем данные из регистра i + j |= ~arrTimeRegMack[i] & varI; // Устанавливаем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + j &= arrTimeRegMack[i] | varI; // Сбрасываем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + objI2C.writeByte(valAddress, arrTimeRegAddr[i], j); // Сохраняем значение j в регистр arrTimeRegAddr[i] + } // + // + private: // + /** Внутренние переменные **/ // + iarduino_I2C objI2C; // Создаём объект для работы с шиной I2C + uint8_t valAddress = 0x68; // Адрес модуля на шине I2C + uint8_t arrTimeRegAddr[7] = {0x00,0x01,0x02,0x04,0x05,0x06,0x03}; // Определяем массив с адресами регистров даты и времени (сек, мин, час, день, месяц, год, день недели) + uint8_t arrTimeRegMack[7] = {0x7F,0x7F,0x3F,0x3F,0x1F,0xFF,0x07}; // Определяем маскировочный массив для регистров даты и времени (при чтении/записи, нужно совершить побитовое «и») + uint8_t varI; // +}; // + // +#endif // \ No newline at end of file diff --git a/lib/iarduino_RTC/src/iarduino_RTC_DS3231.h b/lib/iarduino_RTC/src/iarduino_RTC_DS3231.h new file mode 100644 index 00000000..fd8a4618 --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC_DS3231.h @@ -0,0 +1,42 @@ +#ifndef iarduino_RTC_DS3231_h // +#define iarduino_RTC_DS3231_h // +#define RTC_DS3231 3 // Модуль часов реального времени с протоколом передачи данных I2C, памятью 019x8, температурной компенсацией, двумя будильниками и встроенным кварцевым резонатором +#include "iarduino_RTC_I2C.h" // Подключаем файл iarduino_RTC_I2C.h - для работы с шиной I2C + // +class iarduino_RTC_DS3231: public iarduino_RTC_BASE{ // + public: // + /** функции доступные пользователю **/ // +// Инициализация модуля: // + void begin(void){ // (без параметров) +// Инициализация работы с шиной I2C: // + objI2C.begin(100); // (скорость шины в кГц) +// Установка флагов управления и состояния модуля: // + varI=objI2C.readByte(valAddress, 0x02); if( varI & 0b01000000 ){objI2C.writeByte(valAddress, 0x02, (varI&~0b01000000) );} // (если установлен 6 бит в 2 регистре, то сбрасываем его - переводим модуль в 24 часовой режим) + varI=objI2C.readByte(valAddress, 0x0E); if( varI & 0b11011111 ){objI2C.writeByte(valAddress, 0x0E, (varI&~0b11011111) );} // (если установлены 7,6,4,3,2,1 и 0 биты в 14 регистре, то сбрасываем их - разрешаем генератору работать от батарейки, запрещаем выводу SQW работать от батарейки, выводим меандр с частотой 1Гц на вывод SQW, переводим вывод INT/SQW в режим SQW, запрещаем прерывания будильников) + varI=objI2C.readByte(valAddress, 0x0F); if((varI & 0b10000011) || !(varI & 0b00001000)){objI2C.writeByte(valAddress, 0x0F, (varI&~0b10000011)|0b00001000 );} // (если установлены 7,1 и 0 биты или сброшен 3 бит в 15 регистре, то сбрасываем 7,1 и 0 биты, а 3 устанавливаем - сбрасываем флаг остановки генератора, разрешаем меандр с частотой 32768Гц на выводе 32kHz, сбрасываем флаги будильников) + } // + // +// Чтение одного значения из регистров даты и времени модуля: // + uint8_t funcReadTimeIndex(uint8_t i){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели) + delay(1); // + return objI2C.readByte(valAddress, arrTimeRegAddr[i]) & arrTimeRegMack[i]; // + } // + // +// Запись одного значения в регистры даты и времени модуля: // + void funcWriteTimeIndex(uint8_t i, uint8_t j){ // (i = 0-секунды / 1-минуты / 2-часы / 3-день / 4-месяц / 5-год / 6-день недели, j = значение) + varI=funcReadTimeIndex(i); // Читаем данные из регистра i + j |= ~arrTimeRegMack[i] & varI; // Устанавливаем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + j &= arrTimeRegMack[i] | varI; // Сбрасываем биты значения j по маске arrTimeRegMack[i] в прочитанные из регистра i + objI2C.writeByte(valAddress, arrTimeRegAddr[i], j); // Сохраняем значение j в регистр arrTimeRegAddr[i] + } // + // + private: // + /** Внутренние переменные **/ // + iarduino_I2C objI2C; // Создаём объект для работы с шиной I2C + uint8_t valAddress = 0x68; // Адрес модуля на шине I2C + uint8_t arrTimeRegAddr[7] = {0x00,0x01,0x02,0x04,0x05,0x06,0x03}; // Определяем массив с адресами регистров даты и времени (сек, мин, час, день, месяц, год, день недели) + uint8_t arrTimeRegMack[7] = {0x7F,0x7F,0x3F,0x3F,0x1F,0xFF,0x07}; // Определяем маскировочный массив для регистров даты и времени (при чтении/записи, нужно совершить побитовое «и») + uint8_t varI; // +}; // + // +#endif // \ No newline at end of file diff --git a/lib/iarduino_RTC/src/iarduino_RTC_I2C.h b/lib/iarduino_RTC/src/iarduino_RTC_I2C.h new file mode 100644 index 00000000..1f7cf17e --- /dev/null +++ b/lib/iarduino_RTC/src/iarduino_RTC_I2C.h @@ -0,0 +1,461 @@ +#ifndef iarduino_I2C_h // Разрешаем включить данный код в скетч, только если он не был включён ранее +#define iarduino_I2C_h // Запрещаем повторное включение данного кода в скетч + // +// Определяем тип реализации шины I2C: // + #define iarduino_I2C_SW // Объявляем константу iarduino_I2C_SW - Возможна программная реализация шины I2C. +#if (!defined(pin_SW_SDA) || !defined(pin_SW_SCL)) // Если выводы не определены пользователем, то ... + #undef iarduino_I2C_SW // Отменяем объявление константы iarduino_I2C_SW - Программная реализация шины I2C не возможна (так как выводы не указаны). + #if defined(ESP8266) || defined(ESP32) // Если используются указанные платы, то ... + #include // Подключаем библиотеку Wire. + #define pin_SW_SDA 255 // № вывода SDA не определён + #define pin_SW_SCL 255 // № вывода SCL не определён + #define iarduino_I2C_TW // Объявляем константу iarduino_I2C_TW - Будет использована аппаратная реализация шины I2C под управлением библиотеки Wire. + #elif defined(TwoWire_h) // Проверяем не подключена ли библиотека Wire. + #define pin_SW_SDA 255 // № вывода SDA не определён + #define pin_SW_SCL 255 // № вывода SCL не определён + #define iarduino_I2C_TW // Объявляем константу iarduino_I2C_TW - Будет использована аппаратная реализация шины I2C под управлением библиотеки Wire. + #elif (defined(SDA) && defined(SCL)) // Определяем выводы для шины I2C + #define pin_SW_SDA SDA // № вывода SDA = SDA + #define pin_SW_SCL SCL // № вывода SCL = SCL + #define iarduino_I2C_HW // Объявляем константу iarduino_I2C_HW - Будет использована аппаратная реализация шины I2C. + #elif (defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL)) // Определяем выводы для шины I2C + #define pin_SW_SDA PIN_WIRE_SDA // № вывода SDA = PIN_WIRE_SDA + #define pin_SW_SCL PIN_WIRE_SCL // № вывода SCL = PIN_WIRE_SCL + #define iarduino_I2C_HW // Объявляем константу iarduino_I2C_HW - Будет использована аппаратная реализация шины I2C. + #elif (defined(SDA1) && defined(SCL1)) // Определяем выводы для шины I2C-1 + #define pin_SW_SDA SDA1 // № вывода SDA = SDA1 + #define pin_SW_SCL SCL1 // № вывода SCL = SCL1 + #define iarduino_I2C_HW_1 // Объявляем константу iarduino_I2C_HW_1 - Будет использована аппаратная реализация шины I2C-1. + #else // Если выводы определить не удалось, то ... + #define pin_SW_SDA 255 // № вывода SDA не определён - Аппаратная реализация шины I2C не возможна. + #define pin_SW_SCL 255 // № вывода SCL не определён - Аппаратная реализация шины I2C не возможна. + #endif // +#endif // + // +class iarduino_I2C_BASE{ // Определяем полиморфный класс + public: // Доступные методы и функции: +// ОСНОВНЫЕ ФУНКЦИИ: (поддерживаются библиотекой Wire) // + virtual void begin (uint32_t); // Объявляем функцию указания скорости шины I2C. Аргументы: скорость в кГц. + virtual uint8_t readByte (uint8_t, uint8_t ); // Объявляем функцию чтения байта данных из регистра модуля. Аргументы: адрес_модуля, адрес_регистра. (адрес регистра указывает модулю, данные какого регистра требуется отправить мастеру) + virtual bool writeByte (uint8_t, uint8_t, uint8_t); // Объявляем функцию записи байта данных в регистр модуля. Аргументы: адрес_модуля, адрес_регистра, байт_данных. (адрес регистра указывает модулю, в какой регистр требуется сохранить данные) + virtual uint8_t readByte (uint8_t ); // Объявляем функцию чтения байта данных из модуля. Аргументы: адрес_модуля (функция отличается тем, что она не отправляет модулю адрес регистра) + virtual bool writeByte (uint8_t, uint8_t); // Объявляем функцию записи байта данных в модуль. Аргументы: адрес_модуля, байт_данных. (функция отличается тем, что она не отправляет модулю адрес регистра) + virtual bool readBytes (uint8_t, uint8_t, uint8_t*, uint8_t); // Объявляем функцию чтения байтов данных из регистров модуля. Аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт. (адрес первого регистра указывает модулю, с какого регистра требуется начать передачу данных мастеру) + virtual bool writeBytes (uint8_t, uint8_t, uint8_t*, uint8_t); // Объявляем функцию записи байтов данных в регистры модуля. Аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт. (адрес первого регистра указывает модулю, начиная с какого регистра требуется сохранять данные) + virtual bool readBytes (uint8_t, uint8_t*, uint8_t); // Объявляем функцию чтения байтов данных из модуля. Аргументы: адрес_модуля, указатель_на_массив, количество_байт. (функция отличается тем, что она не отправляет модулю адрес первого регистра, а начинает цикл чтения сразу после отправки адреса модуля.) + virtual bool writeBytes (uint8_t, uint8_t*, uint8_t); // Объявляем функцию записи байтов данных в модуль. Аргументы: адрес_модуля, указатель_на_массив, количество_байт. (функция отличается тем, что после отправки адреса модуля она сразу начинает цикл отправки данных, без передачи адреса первого регистра.) +// ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ: (поддерживаются библиотекой Wire) // + virtual uint8_t getType (void); // Объявляем функцию получения типа реализации шины I2C. Аргументы: отсутствуют. + virtual bool checkAddress(uint8_t); // Объявляем функцию поиска модуля на шине I2C. Аргументы: адрес_модуля. +// ФУНКЦИИ НИЖНЕГО УРОВНЯ: (не поддерживаются библиотекой Wire) // + virtual bool start (void); // Объявляем функцию установки состояния START. Аргументы: отсутствуют. + virtual bool reStart (void); // Объявляем функцию установки состояния RESTART. Аргументы: отсутствуют. + virtual void stop (void); // Объявляем функцию установки состояния STOP. Аргументы: отсутствуют. + virtual bool sendID (uint8_t, bool); // Объявляем функцию передачи адреса модуля. Аргументы: ID-адрес модуля, бит RW (0-запись, 1-чтение). + virtual bool setByte (uint8_t); // Объявляем функцию передачи байта данных. Аргументы: байт для передачи. + virtual uint8_t getByte (bool); // Объявляем функцию получения байта данных. Аргументы: бит подтверждения ACK/NACK + private: // + virtual bool setSCL (bool); // Объявляем функцию установки уровня на линии SCL. Аргументы: логический уровень. + virtual void setSDA (bool); // Объявляем функцию установки уровня на линии SDA. Аргументы: логический уровень. + virtual bool getSDA (void); // Объявляем функцию чтения уровня с линии SDA. Аргументы: отсутствуют. +}; // + // +class iarduino_I2C: public iarduino_I2C_BASE{ // Определяем производный класс + public: // Доступные методы и функции: + // + /** ОСНОВНЫЕ ФУНКЦИИ: **/ // + // +// Функция подготовки шины I2C: // Определяем функцию подготовки шины I2C. + void begin(uint32_t speed){ // Аргумент: скорость шины в кГц. +// _ _ _ _______ _ _ _ // +// SCL: _?_?_/ OUTPUT // +// _ _ _ _ _____ _ _ _ // +// SDA: _?_?_ _/ OUTPUT // +// // + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + Wire.setClock(speed*1000L); // Устанавливаем скорость передачи данных по шине I2C. + Wire.begin(); // Инициируем работу на шине I2C в качестве мастера. + #elif defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + pinMode(pin_SDA, INPUT ); digitalWrite(pin_SDA, HIGH); // Определяем вывод pin_SDA как вход и подтягиваем его к Vcc. + pinMode(pin_SCL, INPUT ); digitalWrite(pin_SCL, HIGH); // Определяем вывод pin_SCL как вход и подтягиваем его к Vcc. + TWBR=((F_CPU/(speed*1000L))-16)/2; // Определяем значение регистра скорости связи TWBR: speed = F_CPU / (16 + 2 * TWBR * 4^TWPS). Значит TWBR = (F_CPU/speed - 16) / (2 * 4^TWPS), но так как биты предделителя TWPS мы далее сбросим в 0, то формула упростится до: TWBR = (F_CPU/speed - 16) / 2. Так как speed указана в кГц, а F_CPU в Гц, то умножаем speed на 1000. + if(TWBR<10){TWBR=10;} // Если значение регистра скорости TWBR стало меньше 10 (параметр speed > 400 кГ) то оставляем значение этого регистра равным 10 иначе шина будет работать нестабильно. + TWSR&=(~(_BV(TWPS1)|_BV(TWPS0))); // Определяем значение регистра статуса TWSR: оставляем все его биты не тронутыми, кроме двух битов предделитея TWPS сбрасывая их в 0. + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + port_SDA = digitalPinToPort (pin_SDA); // Определяем номер порта для вывода pin_SDA. + port_SCL = digitalPinToPort (pin_SCL); // Определяем номер порта для вывода pin_SCL. + mask_SDA = digitalPinToBitMask (pin_SDA); // Определяем маску для вывода pin_SDA. в переменной mask_SDA будет установлен только тот бит который соответствует позиции бита управления выводом pin_SDA. + mask_SCL = digitalPinToBitMask (pin_SCL); // Определяем маску для вывода pin_SCL. в переменной mask_SCL будет установлен только тот бит который соответствует позиции бита управления выводом pin_SCL. + mod_SDA = portModeRegister (port_SDA); // Определяем указатель на адрес регистра конфигурации направления работы выводов порта port_SDA. + mod_SCL = portModeRegister (port_SCL); // Определяем указатель на адрес регистра конфигурации направления работы выводов порта port_SCL. + inp_SDA = portInputRegister (port_SDA); // Определяем указатель на адрес регистра входных значений для управления портом port_SDA. + inp_SCL = portInputRegister (port_SCL); // Определяем указатель на адрес регистра входных значений для управления портом port_SCL. + out_SDA = portOutputRegister (port_SDA); // Определяем указатель на адрес регистра выходных значений для управления портом port_SDA. + out_SCL = portOutputRegister (port_SCL); // Определяем указатель на адрес регистра выходных значений для управления портом port_SCL. + setSCL(1); // Переводим вывод SCL в режим входа с подтяжкой к Vcc + setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc + #endif // + } // + // +// Функция чтения одного байта из регистра модуля: // + uint8_t readByte(uint8_t adr, uint8_t reg){ // Определяем функцию чтения одного байта данных из регистра модуля (аргументы: адрес_модуля, адрес_регистра) + uint8_t i=0; readBytes(adr, reg, &i, 1); return i; // Объявляем переменную i и указываем её ссылку для получения 1 байта из регистра reg модуля с адресом adr + } // + // +// Функция чтения одного байта из модуля: // + uint8_t readByte(uint8_t adr){ // Определяем функцию чтения одного байта данных из регистра модуля (аргументы: адрес_модуля) + uint8_t i=0; readBytes(adr, &i, 1); return i; // Объявляем переменную i и указываем её ссылку для получения 1 байта из модуля с адресом adr + } // + // +// Функция записи одного байта в регистр модуля: // + bool writeByte(uint8_t adr, uint8_t reg, uint8_t data){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_модуля, адрес_регистра, байт_данных) + return writeBytes(adr, reg, &data, 1); // Запысываем 1 байт по ссылке на переменную data в регистр reg модуля с адресом adr + } // + // +// Функция записи одного байта в модуль: // + bool writeByte(uint8_t adr, uint8_t data){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_модуля, байт_данных) + return writeBytes(adr, &data, 1); // Запысываем 1 байт по ссылке на переменную data в модуль с адресом adr + } // + // +// Функция пакетного чтения нескольких байт данных из регистров модуля: // + bool readBytes(uint8_t adr, uint8_t reg, uint8_t *data, uint8_t sum){ // Определяем функцию пакетного чтения нескольких байт данных из регистров модуля (аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт) + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это ответ по умолчанию. + Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся. + Wire.write(reg); // Определяем значение первого байта (reg - адреса регистра) который будет отправлен после байта адреса. Функция write() поместит указанный байт в буфер для передачи. + i=Wire.endTransmission(false); if(i){return 0;} // Выполняем инициированную ранее передачу данных (без установки состояния STOP). Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. + if(!Wire.requestFrom( adr, sum )) {return i;} // Читаем (запрашиваем) sum байт данных от устройства с адресом adr и битом RW=1 => чтение. Функция requestFrom() возвращает количество реально принятых байтов. Так как предыдущая функция не установила состояние STOP, то состояние START данной функции будет расценено как RESTART. + while(Wire.available() && i0){ *data=getByte(sum>1); // Получаем по одному байту из очередного регистра за каждый проход цикла while. Прочитанный байт записываются по указателю data. Аргумент функции getByte имеет значение true на всех проходах цикла кроме последнего + data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму прочитанных байт sum. + #if defined(iarduino_I2C_HW) // Проверить корректность чтения каждого байта можно только на аппаратном уровне + if (sum) { if(TWSR&0xF8!=0x50) { i=0;}} // Если после чтения очередного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x50 значит произошла ошибка при чтении + else { if(TWSR&0xF8!=0x58) { i=0;}} // Если после чтения последного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x58 значит произошла ошибка при чтении + #endif // + }}}}}} stop (); return i==5; // Отправляем команду STOP и возвращаем результат успешности чтения + #endif // + } // + // +// Функция пакетного чтения нескольких байт данных из модуля: // + bool readBytes(uint8_t adr, uint8_t *data, uint8_t sum){ // Определяем функцию пакетного чтения нескольких байт данных из модуля (аргументы: адрес_модуля, указатель_на_массив, количество_байт) + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это ответ по умолчанию. + if(!Wire.requestFrom( adr, sum )) {return i;} // Читаем (запрашиваем) sum байт данных от устройства с адресом adr и битом RW=1 => чтение. Функция requestFrom() возвращает количество реально принятых байтов. + while(Wire.available() && i0){ *data=getByte(sum>1); // Получаем по одному байту из очередного регистра за каждый проход цикла while. Прочитанный байт записываются по указателю data. Аргумент функции getByte имеет значение true на всех проходах цикла кроме последнего + data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму прочитанных байт sum. + #if defined(iarduino_I2C_HW) // Проверить корректность чтения каждого байта можно только на аппаратном уровне + if (sum) { if(TWSR&0xF8!=0x50) { i=0;}} // Если после чтения очередного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x50 значит произошла ошибка при чтении + else { if(TWSR&0xF8!=0x58) { i=0;}} // Если после чтения последного байта пакета значение регистра состояния шины I2C Arduino TWSR с маской 0xF8 не равно 0x58 значит произошла ошибка при чтении + #endif // + }}} stop (); return i==2; // Отправляем команду STOP и возвращаем результат успешности чтения + #endif // + } // + // +// Функция пакетной записи нескольких байт данных в регистр модуля: // + bool writeBytes(uint8_t adr, uint8_t reg, uint8_t *data, uint8_t sum){ // Определяем функцию пакетной записи нескольких байт данных в регистры модуля (аргументы: адрес_модуля, адрес_первого_регистра, указатель_на_массив, количество_байт) + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи + Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся. + Wire.write(reg); // Определяем значение первого байта (reg - адреса регистра) который будет отправлен после байта адреса. Функция write() поместит указанный байт в буфер для передачи. + Wire.write(data, sum); // Определяем значения следующих байт (data - массив данных) который будет отправлен после байта регистра. Функция write() поместит sum элементов массива data в буфер для передачи. + i = Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу данных. Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true. + #else // + // Если шина управляется функциями нижнего уровня данного класса: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи + if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ... + if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ... + if ( setByte (reg) ) { i=3; // Если модуль ответил ACK на получение адреса регистра reg, то ... + while(sum>0){if(!setByte(*data )) { i=0;} // Передаём один байт из массива по указателю data в очередной регистр за каждый проход цикла while. Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum. + data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum. + }}}} stop (); return i==3; // Отправляем команду STOP и возвращаем результат записи. + #endif // + } // + // +// Функция пакетной записи нескольких байт данных в модуль: // + bool writeBytes(uint8_t adr, uint8_t *data, uint8_t sum){ // Определяем функцию пакетной записи нескольких байт данных в модуль (аргументы: адрес_модуля, указатель_на_массив, количество_байт) + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи + Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся. + Wire.write(data, sum); // Указываем массив данных который будет отправлен после байта адреса. Функция write() поместит sum элементов массива data в буфер для передачи. + i = Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу данных. Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true. + #else // + // Если шина управляется функциями нижнего уровня данного класса: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи + if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ... + if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ... + while(sum>0){if(!setByte(*data )) { i=0;} // Передаём один байт из массива по указателю data в очередной регистр за каждый проход цикла while. Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum. + data++; sum--; // Увеличиваем адрес указателя data, и уменьшаем сумму переданных байт sum. + }}} stop (); return i==2; // Отправляем команду STOP и возвращаем результат записи. + #endif // + } // + // + /** ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ: **/ // + // +// Функция получения типа реализации шины I2C: // Определяем функцию получения типа шины Ш2С. + uint8_t getType(void){ // Аргументы: отсутсвуют. + #if defined(iarduino_I2C_TW) // + return 4; // Используется аппаратная реализация шины I2C под управлением библиотеки Wire. + #elif defined(iarduino_I2C_HW) // + return 3; // Используется аппаратная реализация шины I2C. + #elif defined(iarduino_I2C_HW_1) // + return 2; // Используется аппаратная реализация шины I2C-1. + #elif defined(iarduino_I2C_SW) // + return 1; // Используется программная реализация шины I2C. + #else // + return 0; // Тип реализации шины I2C не определён. + #endif // + } // + // +// Функция проверки наличия ведомого по его адресу: // + bool checkAddress(uint8_t adr){ // Определяем функцию записи одного байта данных в регистр модуля (аргументы: адрес_регистра, байт_данных) + #if defined(iarduino_I2C_TW) // + // Если используется шина I2C под управлением библиотеки Wire: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат проверки. + Wire.beginTransmission(adr); // Инициируем передачу данных по шине I2C к устройству с адресом adr и битом RW=0 => запись. При этом сама передача не начнётся. + i=Wire.endTransmission(); return i==0; // Выполняем инициированную ранее передачу но без данных (отправится только START, байт адреса, STOP). Функция endTransmission() возвращает: 0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка. В качестве параметра функция endTransmission() может принимать флаг установки стсояния STOP - по умолчанию true. + #else // + // Если шина управляется функциями нижнего уровня данного класса: // + uint8_t i=0; // Предустанавливаем переменную i в значение 0 - это результат записи. + if ( start () ) { i=1; // Если на шине I2C установилось состояние START, то ... + if ( sendID (adr,0) ) { i=2; // Если модуль ответил ACK на получение адреса устройства adr с битом RW=0 (запись), то ... + }} stop (); return i==2; // Отправляем команду STOP и возвращаем результат записи. + #endif // + } // + // + /** ФУНКЦИИ НИЖНЕГО УРОВНЯ: **/ // + // +// Функция нижнего уровня - установка состояния START: // Определяем функцию установки состояния START. + bool start(void){ // Аргументы: отсутствуют. +// _ _ _ _____:___ _ _ _ // +// SCL: : \___ _ _ _ _ _/ // +// _ _ _ _____: _ _ _ _ _ _ // Спад логического уровня на линии SDA с «1» в «0», при наличии уровня логической «1» на линии SCL +// SDA: :\______ _ _/_ _ _ _ _ _ // +// // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния start. + TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTA (бит условия START). + while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов. + if((TWSR & 0xF8)==0x08){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x08, значит состояние START установилось на шине I2C. + return false; // Если предыдущая строка не вернула true, значит состояние START не установилось на шине I2C, возвращаем false. + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + bool i= setSCL(1); // Устанавливаем «1» на линии SCL. Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + setSDA(0); // Устанавливаем «0» на линии SDA + setSCL(0); // Устанавливаем «0» на линии SCL + return i; // Если счетчик i не дошел до 0 при ожидании «1» на линии SCL, то вернётся true. + #else // + // Если тип шины I2C не поддерживается или не определён: // + return false; // Возвращаем false. + #endif // + } // + // +// Функция нижнего уровня - установка состояния RESTART: // Определяем функцию установки состояния RESTART. + bool reStart(void){ // Аргументы: отсутствуют. +// _ _ _ ___:___ _ _ _ // +// SCL: \_ _ _ _ / : \___ _ _ _ _ _/ // +// _ _ _ _ _ ________: _ _ _ _ _ _ // Спад логического уровня на линии SDA с «1» в «0», при наличии уровня логической «1» на линии SCL +// SDA: _ _ _ _ _/ :\______ _ _/_ _ _ _ _ _ // Сигнал рестрат аналогичен сигналу старт и отличается лишь тем, что ему не предшествует сигнал STOP +// // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния RESTART. + TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTA (бит условия START). + while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов. + if((TWSR & 0xF8)==0x10){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x10, значит состояние RESTART установилось на шине I2C. + return false; // Если предыдущая строка не вернула true, значит состояние RESTART не установилось на шине I2C, возвращаем false. + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + return start(); // Программная реализация состояния RESTART идентична программной реализации состояния START. + #else // + // Если тип шины I2C не поддерживается или не определён: // + return false; // Возвращаем false. + #endif // + } // + // +// Функция нижнего уровня - установка состояния STOP: // Определяем функцию установки состояния STOP. + void stop(void){ // Аргументы: отсутствуют. +// _ _ _ ___:____ _ _ _ // +// SCL: \_ _ _ _/ : // +// _ _ _ _ _ :____ _ _ _ // Подъём логического уровня на линии SDA с «0» в «1», при наличии уровня логической «1» на линии SCL +// SDA: _ _ _ _ _\______/: // +// // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания установки состояния STOP + TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания), TWEN (бит разрешения работы шины), TWSTO (бит условия STOP). + while((!(TWCR & _BV(TWSTO))) && i){i--;} // Ждём сброса бита условия стоп TWSTO в регистре управления TWCR, но не дольше чем i циклов + // if((TWSR & 0xF8)==0xA0){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0xA0, значит состояние STOP установилось на шине I2C. + delayMicroseconds(20); // Данная функция не возвращает результат, но в любом случае делаем задержку меджу завершением текущего пакета и возможным началом следующего + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + setSDA(0); // Устанавливаем «0» на линии SDA + setSCL(1); // Устанавливаем «1» на линии SCL. Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + setSDA(1); // Устанавливаем «1» на линии SDA + #endif // + } // + // +// Функция нижнего уровня - передача первого байта АДРЕС+RW: // Определяем функцию передачи первого байта после cигнала START. Это байт адреса модуля с битом RW + bool sendID(uint8_t adr, bool rw){ // Аргументы: ID-адрес модуля, бит RW (0-запись, 1-чтение) +// 1 2 3 4 5 6 7 8 9 // +// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ // +// ________________________ // Передача 7-битного адреса и 1 бита RW. На 9 тактирубщем импульсе модуль отвечает ACK («0» - «я сдесь»), или NACK («1» - «нет») +// SDA: _ _ _ ___/________ADDRESS_______RW>----____ _ _ _ // +// вход // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания получения подтверждения от ведомого ввиде бита ACK. + TWDR = (adr<<1)+rw; // Определяем значение регистра данных TWDR: записываем в него адрес adr сдвигая его на 1 бит влево, при этом младший бит (освободившийся) займёт бит RW (0-запись / 1-чтение) + TWCR = _BV(TWINT) | _BV(TWEN); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания) и TWEN (бит разрешения работы шины). + while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов + if((TWSR & 0xF8)==0x40 && rw){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x40, значит ведомый опознал свой адрес с битом RW=1 и отправил бит подтверждения ACK (в противном случае значение регистра TWSR будет равно 0x48). + if((TWSR & 0xF8)==0x18 && !rw){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x18, значит ведомый опознал свой адрес с битом RW=0 и отправил бит подтверждения ACK (в противном случае значение регистра TWSR будет равно 0x20). + return false; // Если предыдущая строка не вернула true, значит на шине нет ведомых у сказанным адресом, возвращаем false. + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + bool i=true; // Определяем флаг возвращаемый данной функцией. + uint8_t j=7; // Определяем счётчик количества переданных бит адреса. + while(j){ j--; // Передаём 7 бит адреса adr в цикле + setSDA(adr & bit(j)); // Устанавливаем очередной бит адреса на линии SDA + if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) + } setSDA(rw); // Устанавливаем бит RW на линии SDA + if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) + setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc + if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + if( getSDA( ) ) {i=false;} // Если на линии SDA установлена «1» значит ведомый ответил NACK + setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) + return i; // Если линия SCL была занята или модуль ответил NACK, то вернётся false. + #else // + // Если тип шины I2C не поддерживается или не определён: // + return false; // Возвращаем false + #endif // + } // + // +// Функция нижнего уровня - передача одного байта данных: // Определяем функцию передачи одного байта (в любом месте между байтом адреса с битом RW=0 и сигналом STOP). + bool setByte(uint8_t data){ // Аргумент: байт для передачи. +// 1 2 3 4 5 6 7 8 9 // +// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ // +// ________________________ // Передача 1 байта, каждый бит читается модулем по фронту тактирубщих импульсов. На 9 тактирубщем импульсе модуль отвечает ACK («0» - «принял»), или NACK («1» - «нет») +// SDA: _ _ _ ___/__________DATA__________>----____ _ _ _ // +// вход // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания получения подтверждения от ведомого ввиде бита ACK. + TWDR = data; // Определяем значение регистра данных TWDR: записываем в него байт для передачи по шине I2C. + TWCR = _BV(TWINT) | _BV(TWEN); // Определяем значение регистра управления TWCR: устанавливаем биты TWINT (флаг прерывания) и TWEN (бит разрешения работы шины). + while((!(TWCR & _BV(TWINT))) && i){i--;} // Ждём сброса флага прерывания TWINT в регистре управления TWCR, но не дольше чем i циклов + if((TWSR & 0xF8)==0x28){return true;} // Проверяем значение регистра статуса TWSR: если его значение с маской 0xF8 равно 0x28, значит ведомый отправил бит подтверждения приёма данных ACK (в противном случае значение регистра TWSR будет равно 0x30). + return false; // Если предыдущая строка не вернула true, значит ведомый не принял отправленный ему байт, возвращаем false. + #elif defined(iarduino_I2C_SW) // + // Если используется программная шина I2C: // + bool i=true; // Определяем флаг возвращаемый данной функцией. + uint8_t j=8; // Определяем счётчик количества переданных байт. + while(j){ j--; // Передаём 8 бит байта data в цикле + setSDA(data & bit(j)); // Устанавливаем очередной бит байта на линии SDA + if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) + } setSDA(1); // Переводим вывод SDA в режим входа с подтяжкой к Vcc + if(!setSCL(1) ) {i=false;} // Устанавливаем «1» на линии SCL (Фронт тактирующего импульса). Если ведомый удерживает линию SCL прижатой, то функция ждёт освобождения линии и, если не дождётся, то возвращает false. + if( getSDA( ) ) {i=false;} // Если на линии SDA установлена «1» значит ведомый ответил NACK + setSCL(0); // Устанавливаем «0» на линии SCL (Спад тактирубщего импульса) + return i; // Если счетчик i не дошел до 0 и не был сброшен в 0 при получении NACK, то вернётся true. + #else // + // Если тип шины I2C не поддерживается или не определён: // + return false; // Возвращаем false + #endif // + } // + // +// Функция нижнего уровня - получение одного байта данных: // Определяем функцию получения одного байта (в любом месте между байтом адреса с битом RW=1 и сигналом STOP). + uint8_t getByte(bool ack){ // Аргумент: бит подтверждения ACK/NACK +// 1 2 3 4 5 6 7 8 9 // +// SCL: _ _ _ _____/\_/\_/\_/\_/\_/\_/\_/\__/\_____ _ _ _ // +// __ // Получение 1 байта, каждый бит читается мастером по фронту тактирубщих импульсов. На 9 тактирубщем импульсе мастер отвечает ACK («0» - «давай еще»), или NACK («1» - «хватит») +// SDA: _ _ _ ___/----------DATA-----------<__\____ _ _ _ // +// вход выход // + #if defined(iarduino_I2C_HW) // + // Если используется аппаратная шина I2C: // + uint16_t i=60000L; // Определяем счётчик указывая максимальное количество циклов для ожидания. + TWCR = _BV(TWINT) | _BV(TWEN) | ack<7) ){ j=1; } j=bit(j-1); } // Устанавливаем один бит в переменной j; + // if( i==6 ){j--;} // Если чип считает день недели как написано в datasheet, то раскомментируйте данную строку и закомментируйте предыдущую. + objI2C.writeByte(valAddress, arrTimeRegAddr[i], j&arrTimeRegMack[i]); // Сохраняем значение j в регистр arrTimeRegAddr[i] с маской arrTimeRegMack[i]. + } // + // + private: // + /** Внутренние переменные **/ // + iarduino_I2C objI2C; // Создаём объект для работы с шиной I2C + uint8_t valAddress = 0x32; // Адрес модуля на шине I2C + uint8_t arrTimeRegAddr[7] = {0x00,0x01,0x02,0x04,0x05,0x06,0x03}; // Определяем массив с адресами регистров даты и времени (сек, мин, час, день, месяц, год, день недели) + uint8_t arrTimeRegMack[7] = {0x7F,0x7F,0x3F,0x3F,0x1F,0xFF,0x7F}; // Определяем маскировочный массив для регистров даты и времени (при чтении/записи, нужно совершить побитовое «и») + uint8_t varI; // +}; // + // +#endif // diff --git a/lib/iarduino_RTC/src/memorysaver.h b/lib/iarduino_RTC/src/memorysaver.h new file mode 100644 index 00000000..27cb6774 --- /dev/null +++ b/lib/iarduino_RTC/src/memorysaver.h @@ -0,0 +1,9 @@ +// Файл: «ХРАНИТЕЛЬ ПАМЯТИ» + +// Если закомментировать строки с названиями тех модулей которые не используются в Вашем устройстве, можно освободить около 50 байт флеш-памяти. +// Если закомментировать строку с названием используемого модуля, компилятор выдаст ошибку. + + #define RTC_ENABLE_DS1302 1 // Модуль часов реального времени (RTC) на базе чипа DS1302, с протоколом передачи данных SI3, памятью 040x8 (31 байт которой доступны для хранения данных пользователя). + #define RTC_ENABLE_DS1307 1 // Модуль часов реального времени (RTC) на базе чипа DS1307, с протоколом передачи данных I2C, памятью 064x8 (56 байт которой доступны для хранения данных пользователя). + #define RTC_ENABLE_DS3231 1 // Модуль часов реального времени (RTC) на базе чипа DS3231, с протоколом передачи данных I2C, памятью 019x8, температурной компенсацией, двумя будильниками и встроенным кварцевым резонатором. + #define RTC_ENABLE_RX8025 1 // Модуль часов реального времени (RTC) на базе чипа RX8025, с протоколом передачи данных I2C, памятью 016x8, температурной компенсацией, двумя будильниками и встроенным кварцевым резонатором. \ No newline at end of file diff --git a/myProfile.json b/myProfile.json index 7432922b..97fd71cd 100644 --- a/myProfile.json +++ b/myProfile.json @@ -3,7 +3,7 @@ "name": "IoTmanagerVer4", "apssid": "IoTmanager", "appass": "", - "routerssid": "rise", + "routerssid": "iot", "routerpass": "hostel3333", "timezone": 2, "ntp": "pool.ntp.org", @@ -25,15 +25,117 @@ "projectProp": { "platformio": { "default_envs": "esp8266_4mb", - "comments_default_envs": "choose from: esp8266_4mb or esp32_4mb or esp8266_1mb or esp8266_1mb_ota or esp8285_1mb or esp8285_1mb_ota" + "comments_default_envs": "choose from: esp8266_4mb or esp32_4mb or esp32cam_4mb or esp32s2_4mb or esp32_4mb3f or esp32s3_16mb or esp32c3m_4mb or esp8266_1mb or esp8266_1mb_ota or esp8285_1mb or esp8285_1mb_ota", + "envs": [ + { + "name": "esp8266_4mb", + "firmware": "0x00000", + "littlefs": "0x300000" + }, + { + "name": "esp8266_16mb", + "firmware": "0x00000", + "littlefs": "0x200000" + }, + { + "name": "esp32_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32_4mb3f", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x310000" + }, + { + "name": "esp32cam_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32_16mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x910000" + }, + { + "name": "esp8266_1mb", + "firmware": "0x00000000", + "littlefs": "0x000bb000" + }, + { + "name": "esp8266_1mb_ota", + "firmware": "0x00000000", + "littlefs": "0x000eb000" + }, + { + "name": "esp8266_2mb", + "firmware": "0x00000000", + "littlefs": "0x00100000" + }, + { + "name": "esp8266_2mb_ota", + "firmware": "0x00000000", + "littlefs": "0x001c0000" + }, + { + "name": "esp8285_1mb", + "firmware": "0x00000000", + "littlefs": "0x000bb000" + }, + { + "name": "esp8285_1mb_ota", + "firmware": "0x00000000", + "littlefs": "0x000eb000" + }, + { + "name": "esp32s2_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x290000" + }, + { + "name": "esp32c3m_4mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x310000" + }, + { + "name": "esp32s3_16mb", + "boot_app0": "0xe000", + "bootloader_qio_80m": "0x1000", + "firmware": "0x10000", + "partitions": "0x8000", + "littlefs": "0x910000" + } + ] } }, "modules": { - "Виртуальные элементы": [ + "virtual_elments": [ { "path": "src/modules/virtual/Cron", "active": true }, + { + "path": "src/modules/virtual/GoogleSheet", + "active": false + }, { "path": "src/modules/virtual/Loging", "active": true @@ -42,6 +144,14 @@ "path": "src/modules/virtual/LogingDaily", "active": true }, + { + "path": "src/modules/virtual/Math", + "active": false + }, + { + "path": "src/modules/virtual/owmWeather", + "active": true + }, { "path": "src/modules/virtual/Timer", "active": true @@ -50,10 +160,6 @@ "path": "src/modules/virtual/Variable", "active": true }, - { - "path": "src/modules/virtual/VariableColor", - "active": true - }, { "path": "src/modules/virtual/VButton", "active": true @@ -63,7 +169,11 @@ "active": false } ], - "Сенсоры": [ + "sensors": [ + { + "path": "src/modules/exec/Pcf8591", + "active": false + }, { "path": "src/modules/sensors/A02Distance", "active": true @@ -92,6 +202,14 @@ "path": "src/modules/sensors/Ble", "active": false }, + { + "path": "src/modules/sensors/Ble_part1", + "active": false + }, + { + "path": "src/modules/sensors/Ble_part2", + "active": false + }, { "path": "src/modules/sensors/Bme280", "active": true @@ -116,6 +234,10 @@ "path": "src/modules/sensors/Emon", "active": false }, + { + "path": "src/modules/sensors/ExampleModule", + "active": false + }, { "path": "src/modules/sensors/ExternalMQTT", "active": false @@ -148,10 +270,18 @@ "path": "src/modules/sensors/Ina219", "active": false }, + { + "path": "src/modules/sensors/Ina226", + "active": false + }, { "path": "src/modules/sensors/IoTWiegand", "active": false }, + { + "path": "src/modules/sensors/ld2410", + "active": false + }, { "path": "src/modules/sensors/Max6675", "active": false @@ -160,6 +290,10 @@ "path": "src/modules/sensors/Mhz19", "active": false }, + { + "path": "src/modules/sensors/Ntc", + "active": false + }, { "path": "src/modules/sensors/Pzem004t", "active": true @@ -176,6 +310,10 @@ "path": "src/modules/sensors/S8", "active": true }, + { + "path": "src/modules/sensors/Scd40", + "active": false + }, { "path": "src/modules/sensors/Sds011", "active": false @@ -201,7 +339,7 @@ "active": true } ], - "Исполнительные устройства": [ + "executive_devices": [ { "path": "src/modules/exec/ButtonIn", "active": true @@ -222,6 +360,10 @@ "path": "src/modules/exec/EspCam", "active": false }, + { + "path": "src/modules/exec/Ftp", + "active": false + }, { "path": "src/modules/exec/HttpGet", "active": false @@ -278,12 +420,20 @@ "path": "src/modules/exec/TelegramLT", "active": true }, + { + "path": "src/modules/exec/Telegram_v2", + "active": false + }, { "path": "src/modules/exec/Thermostat", "active": false + }, + { + "path": "src/modules/sensors/Ds2423", + "active": false } ], - "Экраны": [ + "screens": [ { "path": "src/modules/display/DwinI", "active": false @@ -296,6 +446,22 @@ "path": "src/modules/display/NextionUpload", "active": false }, + { + "path": "src/modules/display/Oled128", + "active": false + }, + { + "path": "src/modules/display/Oled64", + "active": true + }, + { + "path": "src/modules/display/Smi2_m", + "active": true + }, + { + "path": "src/modules/display/TM16XX", + "active": true + }, { "path": "src/modules/display/Ws2812b", "active": false diff --git a/platformio.ini b/platformio.ini index 7f1d6593..beb5f904 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,6 +7,9 @@ lib_deps_external = bblanchon/ArduinoJson @6.18.0 knolleary/PubSubClient +[env] +extra_scripts = pre:tools/prebuildscript.py + [env:esp8266_1mb_ota] lib_deps = ${common_env_data.lib_deps_external} @@ -79,7 +82,7 @@ build_flags = -Desp8266_2mb="esp8266_2mb" framework = arduino board = d1_wroom_02 board_build.ldscript = eagle.flash.2m1m.ld -platform = espressif8266 +platform = espressif8266 @4.2.0 monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -100,7 +103,7 @@ build_flags = -Desp8266_2mb_ota="esp8266_2mb_ota" framework = arduino board = d1_wroom_02 board_build.ldscript = eagle.flash.2m256.ld -platform = espressif8266 +platform = espressif8266 @4.2.0 monitor_filters = esp8266_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -154,6 +157,28 @@ build_src_filter = + ${env:esp8266_4mb_fromitems.build_src_filter} +[env:esp8266_16mb] +extra_scripts = pre:tools/patch8266_16m.py +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp8266_16mb_fromitems.lib_deps} + ESPAsyncUDP +build_flags = -Desp8266_16mb="esp8266_16mb" +framework = arduino +board = nodemcuv2 +platform = espressif8266 @4.0.1 +board_build.ldscript = eagle.flash.16m14m.ld +monitor_filters = esp8266_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp8266_16mb_fromitems.build_src_filter} + [env:esp32_4mb] lib_deps = ${common_env_data.lib_deps_external} @@ -174,6 +199,142 @@ build_src_filter = + ${env:esp32_4mb_fromitems.build_src_filter} +[env:esp32_4mb3f] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32_4mb3f_fromitems.lib_deps} +build_flags = -Desp32_4mb="esp32_4mb" +framework = arduino +board = esp32dev +platform = espressif32 @5.1.1 +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.partitions = tools/partitions_custom.csv +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32_4mb3f_fromitems.build_src_filter} + +[env:esp32cam_4mb] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32cam_4mb_fromitems.lib_deps} +build_flags = + -Desp32cam_4mb="esp32cam_4mb" + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue +framework = arduino +board = esp32cam +platform = espressif32 @5.1.1 +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32cam_4mb_fromitems.build_src_filter} + +[env:esp32s2_4mb] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32s2_4mb_fromitems.lib_deps} +build_flags = + -Desp32s2_4mb="esp32s2_4mb" + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=0 +framework = arduino +board = lolin_s2_mini +platform = espressif32 @6.3.1 +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32s2_4mb_fromitems.build_src_filter} + +[env:esp32c3m_4mb] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32c3m_4mb_fromitems.lib_deps} +build_flags = + -Desp32c3m_4mb="esp32c3m_4mb" +framework = arduino +board = lolin_c3_mini +platform = espressif32 @6.3.1 +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.filesystem = littlefs +board_build.partitions = tools/partitions_custom.csv +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32c3m_4mb_fromitems.build_src_filter} + +[env:esp32s3_16mb] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32s3_16mb_fromitems.lib_deps} +build_flags = + -Desp32s3_16mb="esp32s3_16mb" +framework = arduino +board = esp32-s3-devkitc-1 +board_build.mcu = esp32s3 +platform = espressif32 @6.3.1 +monitor_filters = esp32_exception_decoder +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.partitions = tools/large_spiffs_16MB.csv +board_upload.flash_size = 16MB +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32s3_16mb_fromitems.build_src_filter} + +[env:esp32_16mb] +lib_deps = + ${common_env_data.lib_deps_external} + ${env:esp32_16mb_fromitems.lib_deps} +build_flags = -Desp32_16mb="esp32_16mb" +framework = arduino +board = esp32dev +platform = espressif32 @5.1.1 +monitor_filters = esp32_exception_decoder +upload_port = COM11 +upload_speed = 921600 +monitor_speed = 115200 +debug_tool = esp-prog +board_build.partitions = tools/large_spiffs_16MB.csv +board_upload.flash_size = 16MB +board_build.filesystem = littlefs +build_src_filter = + +<*.cpp> + + + + + + + ${env:esp32_16mb_fromitems.build_src_filter} + [env:esp8266_1mb_ota_fromitems] lib_deps = adafruit/Adafruit BME280 Library @@ -222,11 +383,11 @@ lib_deps = adafruit/Adafruit BME280 Library adafruit/Adafruit BMP280 Library https://github.com/milesburton/Arduino-Temperature-Control-Library - IRremote @ ^4.1.2 https://github.com/tremaru/iarduino_RTC robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 plerup/EspSoftwareSerial + gyverlibs/EncButton @ ^2.0 adafruit/Adafruit MCP23017 Arduino Library@^2.1.0 adafruit/Adafruit BusIO @ ^1.13.2 adafruit/Adafruit BusIO @ ^1.13.2 @@ -247,7 +408,7 @@ build_src_filter = + + + - + + + + + + @@ -256,6 +417,8 @@ build_src_filter = + + + + + + + + + + @@ -348,6 +511,7 @@ build_src_filter = + + + + + + + + @@ -356,6 +520,7 @@ build_src_filter = + + + + + + + + @@ -371,7 +536,6 @@ lib_deps = adafruit/Adafruit BMP280 Library beegee-tokyo/DHT sensor library for ESPx https://github.com/milesburton/Arduino-Temperature-Control-Library - https://github.com/tremaru/iarduino_RTC robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 plerup/EspSoftwareSerial @@ -382,13 +546,18 @@ lib_deps = adafruit/Adafruit BusIO @ ^1.13.2 https://github.com/robotclass/RobotClass_LiquidCrystal_I2C marcoschwartz/LiquidCrystal_I2C@^1.1.4 + https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED + https://github.com/adafruit/Adafruit-GFX-Library + https://github.com/maxint-rd/TM16xx + adafruit/Adafruit GFX Library @ ^1.11.5 + adafruit/Adafruit BusIO @ ^1.13.2 build_src_filter = + + + + + + + - + + + + @@ -418,8 +587,73 @@ build_src_filter = + + + + + + + [env:esp32_4mb_fromitems] +lib_deps = + https://github.com/enjoyneering/AHTxx.git + adafruit/Adafruit BME280 Library + adafruit/Adafruit BMP280 Library + beegee-tokyo/DHT sensor library for ESPx + https://github.com/milesburton/Arduino-Temperature-Control-Library + robtillaart/SHT2x@^0.1.1 + WEMOS SHT3x@1.0.0 + plerup/EspSoftwareSerial + gyverlibs/EncButton @ ^2.0 + https://github.com/RoboticsBrno/ServoESP32#v1.0.3 + adafruit/Adafruit MCP23017 Arduino Library@^2.1.0 + adafruit/Adafruit BusIO @ ^1.13.2 + dfrobot/DFRobotDFPlayerMini @ ^1.0.5 + adafruit/Adafruit BusIO @ ^1.13.2 + https://github.com/robotclass/RobotClass_LiquidCrystal_I2C + marcoschwartz/LiquidCrystal_I2C@^1.1.4 + https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED + https://github.com/adafruit/Adafruit-GFX-Library + https://github.com/maxint-rd/TM16xx + adafruit/Adafruit GFX Library @ ^1.11.5 + adafruit/Adafruit BusIO @ ^1.13.2 +build_src_filter = + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[env:esp32_4mb3f_fromitems] lib_deps = https://github.com/enjoyneering/AHTxx.git adafruit/Adafruit BME280 Library @@ -430,13 +664,16 @@ lib_deps = robtillaart/SHT2x@^0.1.1 WEMOS SHT3x@1.0.0 plerup/EspSoftwareSerial - https://github.com/RoboticsBrno/ServoESP32 + gyverlibs/EncButton @ ^2.0 + https://github.com/RoboticsBrno/ServoESP32#v1.0.3 adafruit/Adafruit MCP23017 Arduino Library@^2.1.0 adafruit/Adafruit BusIO @ ^1.13.2 dfrobot/DFRobotDFPlayerMini @ ^1.0.5 adafruit/Adafruit BusIO @ ^1.13.2 https://github.com/robotclass/RobotClass_LiquidCrystal_I2C marcoschwartz/LiquidCrystal_I2C@^1.1.4 + https://github.com/maxint-rd/TM16xx + adafruit/Adafruit GFX Library @ ^1.11.5 build_src_filter = + + @@ -462,6 +699,8 @@ build_src_filter = + + + + + + + + + + @@ -470,4 +709,87 @@ build_src_filter = + + + + + + + + +[env:esp32cam_4mb_fromitems] +lib_deps = + espressif/esp32-camera @ ^2.0.0 + gyverlibs/FastBot +build_src_filter = + + + + + + + + + + + + + + + + + + + +[env:esp32s2_4mb_fromitems] +lib_deps = +build_src_filter = + + + + + + + + + + + + + + + + + + + + + +[env:esp8266_16mb_fromitems] +lib_deps = + gyverlibs/EncButton @ ^2.0 +build_src_filter = + + + + + + + + + + + + + + + + + + + + + + + +[env:esp32_16mb_fromitems] +lib_deps = +build_src_filter = + + + + + + + + + + + + + + + + + +[env:esp32c3m_4mb_fromitems] +lib_deps = +build_src_filter = + + + + + + + + + + + + + + + + + +[env:esp32s3_16mb_fromitems] +lib_deps = +build_src_filter = + + + + + + + + + + + + + + + + diff --git a/src/DeviceList.cpp b/src/DeviceList.cpp index 9a0c42b4..96903e16 100644 --- a/src/DeviceList.cpp +++ b/src/DeviceList.cpp @@ -2,64 +2,94 @@ const String getThisDevice() { String thisDevice = "{}"; - jsonWriteStr_(thisDevice, F("devicelist_"), ""); //метка для парсинга - jsonWriteStr_(thisDevice, F("wg"), jsonReadStr(settingsFlashJson, F("wg"))); //рабочая группа + // jsonWriteStr_(thisDevice, F("devicelist_"), ""); // метка для парсинга - она не нужна теперь + jsonWriteStr_(thisDevice, F("wg"), jsonReadStr(settingsFlashJson, F("wg"))); // рабочая группа jsonWriteStr_(thisDevice, F("ip"), jsonReadStr(settingsFlashJson, F("ip"))); jsonWriteStr_(thisDevice, F("id"), jsonReadStr(settingsFlashJson, F("id"))); jsonWriteStr_(thisDevice, F("name"), jsonReadStr(settingsFlashJson, F("name"))); + // приложение svelte хранит свой список и в нем поддерживает корректные статусы, а при получении этого списка + // лишь изменяет в своем те элементы массива которые добавились новые, не трогая остальные + jsonWriteBool_(thisDevice, F("status"), false); + jsonWriteInt_(thisDevice, F("fv"), FIRMWARE_VERSION); thisDevice = "[" + thisDevice + "]"; return thisDevice; } void addThisDeviceToList() { - devListHeapJson = getThisDevice(); - SerialPrint("i", "List", "Add this dev to list"); + if (jsonReadInt(settingsFlashJson, F("udps")) != 0) { + // если включен автопоиск то записываем это устройство в оперативную память + devListHeapJson = getThisDevice(); + } else { + // если выключен автопоиск то записываем это устройство в флешь память + // если файла не было никогда + String list = readFile("/devlist.json", 2048); + if (list == "failed") { + writeFile("/devlist.json", getThisDevice()); + } else { + // если файл был то перепишем в нем первое устройство - to do + } + } + + SerialPrint("i", F("List"), F("This dev added to list")); } #ifdef UDP_ENABLED AsyncUDP asyncUdp; -void asyncUdpInit() { +void udpListningInit() { if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210)) { asyncUdp.onPacket([](AsyncUDPPacket packet) { - // Serial.print("UDP Packet Type: "); - // Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); - // Serial.print("From: "); - // Serial.print(packet.remoteIP()); - // Serial.print(":"); - // Serial.println(packet.remotePort()); - // Serial.print("To: "); - // Serial.print(packet.localIP()); - // Serial.print(":"); - // Serial.println(packet.localPort()); - // Serial.print(", Length: "); - // Serial.print(packet.length()); - // Serial.print(", Data: "); - // Serial.write(packet.data(), packet.length()); - - //String data = {packet.data(), packet.length()}; // для ESP32 подходит как замена uint8tToString, но 8266 не переваривает - String data = uint8tToString(packet.data(), packet.length()); - String remoteWorkgroup = ""; - data.replace("[", ""); - data.replace("]", ""); - if (jsonRead(data, F("wg"), remoteWorkgroup)) { // проверяем чтоб полученный формат был Json и заодно вытягиваем имя группы - String loacalWorkgroup = ""; - jsonRead(settingsFlashJson, F("wg"), loacalWorkgroup); - if (remoteWorkgroup == loacalWorkgroup) { - SerialPrint("i", F("UDP"), "IP: " + packet.remoteIP().toString() + ":" + String(packet.remotePort())); - jsonMergeArrays(devListHeapJson, data); + // если был включен автоматический поиск устройств то начнем запись в оперативную память + if (jsonReadInt(settingsFlashJson, F("udps")) != 0) { + // Serial.print("UDP Packet Type: "); + // Serial.println(packet.isBroadcast() ? "Broadcast" : packet.isMulticast() ? "Multicast" : "Unicast"); + // Serial.print("From: "); + // Serial.print(packet.remoteIP()); + // Serial.print(":"); + // Serial.println(packet.remotePort()); + // Serial.print("To: "); + // Serial.print(packet.localIP()); + // Serial.print(":"); + // Serial.println(packet.localPort()); + // Serial.print(", Length: "); + // Serial.print(packet.length()); + // Serial.print(", Data: "); + // Serial.write(packet.data(), packet.length()); + // String data = {packet.data(), packet.length()}; // для ESP32 подходит как замена uint8tToString, но 8266 не переваривает + + String data = uint8tToString(packet.data(), packet.length()); + String remoteWorkgroup = ""; + data.replace("[", ""); + data.replace("]", ""); + if (jsonRead(data, F("wg"), remoteWorkgroup)) { // проверяем чтоб полученный формат был Json и заодно вытягиваем имя группы + String loacalWorkgroup = ""; + jsonRead(settingsFlashJson, F("wg"), loacalWorkgroup); + if (remoteWorkgroup == loacalWorkgroup) { + SerialPrint("i", F("UDP"), "IP: " + packet.remoteIP().toString() + ":" + String(packet.remotePort())); + jsonMergeArrays(devListHeapJson, data); + // эксперементальный вариант отправки нового списка сразу по приходу + // sendStringToWs("devlis", devListHeapJson, -1); + } + } else { + SerialPrint("E", F("UDP"), F("Udp packet invalid")); } + + // reply to the client + // String ip = WiFi.localIP().toString(); + // asyncUdp.broadcastTo(ip.c_str(), packet.remotePort()); + // packet.printf(ip.c_str(), packet.length()); + } else { - SerialPrint("E", F("UDP"), F("Udp packet invalid")); + devListHeapJson = ""; } - // reply to the client - // String ip = WiFi.localIP().toString(); - // asyncUdp.broadcastTo(ip.c_str(), packet.remotePort()); - // packet.printf(ip.c_str(), packet.length()); }); } - //будем отправлять каждые 60 секунд презентацию данного устройства + SerialPrint("i", F("UDP"), F("Udp listning inited")); +} + +void udpBroadcastInit() { + // будем отправлять каждые 60 секунд презентацию данного устройства ts.add( UDP, 60000, [&](void*) { // UDPP if (isNetworkActive()) { @@ -71,11 +101,11 @@ void asyncUdpInit() { }, nullptr, true); - SerialPrint("i", F("UDP"), F("Udp Init")); + SerialPrint("i", F("UDP"), F("Udp broadcast inited")); } void jsonMergeArrays(String& existJson, String& incJson) { - DynamicJsonDocument incJsonDoc(1024); + DynamicJsonDocument incJsonDoc(4096); DeserializationError incJsonError = deserializeJson(incJsonDoc, incJson); // if (incJsonError) { // upd: devlist заведомо верный, зачем проверять еще раз? // SerialPrint("E", F("UDP"), "Invailed json in incomming udp packet " + String(incJsonError.f_str())); @@ -83,7 +113,7 @@ void jsonMergeArrays(String& existJson, String& incJson) { // return; // } - DynamicJsonDocument existJsonDoc(1024); + DynamicJsonDocument existJsonDoc(4096); DeserializationError existJsonError = deserializeJson(existJsonDoc, existJson); // if (existJsonError) { // upd: полученный json уже проверен на целостность // SerialPrint("E", F("UDP"), "Invailed json in existing udp dev list " + String(incJsonError.f_str())); diff --git a/src/ESPConfiguration.cpp b/src/ESPConfiguration.cpp index 796e7bb5..d0ee0bc8 100644 --- a/src/ESPConfiguration.cpp +++ b/src/ESPConfiguration.cpp @@ -31,7 +31,10 @@ void configure(String path) { if (driver = myIoTItem->getGpioDriver()) IoTgpio.regDriver((IoTGpio*)driver); // пробуем спросить драйвер RTC if (driver = myIoTItem->getRtcDriver()) rtcItem = (IoTItem*)driver; - + // пробуем спросить драйвер CAM + //if (driver = myIoTItem->getCAMDriver()) camItem = (IoTItem*)driver; + // пробуем спросить драйвер Telegram_v2 + if (driver = myIoTItem->getTlgrmDriver()) tlgrmItem = (IoTItem*)driver; IoTItems.push_back(myIoTItem); } } @@ -44,6 +47,8 @@ void configure(String path) { void clearConfigure() { Serial.printf("Start clearing config\n"); rtcItem = nullptr; + //camItem = nullptr; + tlgrmItem = nullptr; IoTgpio.clearDrivers(); for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { diff --git a/src/EspFileSystem.cpp b/src/EspFileSystem.cpp index 21d6f11f..7c8777eb 100644 --- a/src/EspFileSystem.cpp +++ b/src/EspFileSystem.cpp @@ -1,7 +1,8 @@ #include "EspFileSystem.h" #include "Global.h" -bool fileSystemInit() { +bool fileSystemInit() +{ if (!FileFS.begin()) { SerialPrint(F("E"), F("FS"), F("Init ERROR, may be FS was not flashed")); return false; @@ -10,7 +11,8 @@ bool fileSystemInit() { return true; } -void globalVarsSync() { +void globalVarsSync() +{ settingsFlashJson = readFile(F("settings.json"), 4096); settingsFlashJson.replace("\r\n", ""); @@ -18,44 +20,42 @@ void globalVarsSync() { valuesFlashJson.replace("\r\n", ""); mqttPrefix = jsonReadStr(settingsFlashJson, F("mqttPrefix")); - mqttRootDevice = mqttPrefix + "/" + chipId; - jsonWriteStr_(settingsFlashJson, "root", mqttRootDevice); jsonWriteStr_(settingsFlashJson, "id", chipId); - // jsonWriteStr_(errorsHeapJson, "errors_", ""); //метка для парсинга удалить - // jsonWriteStr_(ssidListHeapJson, "ssids_", ""); //метка для парсинга удалить + mqttRootDevice = mqttPrefix + "/" + chipId; + + // это не используется - удалить в последствии + jsonWriteStr_(settingsFlashJson, "root", mqttRootDevice); } -//к удалению. не используется -// String getParamsJson() { -// String json; -// serializeJson(*getLocalItemsAsJSON(), json); -// jsonWriteStr_(json, "params", ""); -// return json; -// } - -void syncSettingsFlashJson() { +void syncSettingsFlashJson() +{ writeFile(F("settings.json"), settingsFlashJson); } -void syncValuesFlashJson() { +void syncValuesFlashJson() +{ writeFile(F("values.json"), valuesFlashJson); } -const String getChipId() { - return String(ESP_getChipId()) + "-" + String(ESP_getFlashChipId()); +const String getChipId() +{ + return String(ESP_getChipId()) + "-" + String(getFlashChipIdNew()); // + "v" + String(FIRMWARE_VERSION); } -void setChipId() { +void setChipId() +{ chipId = getChipId(); SerialPrint("i", "System", "id: " + chipId); } -const String getUniqueId(const char* name) { +const String getUniqueId(const char* name) +{ return String(name) + getMacAddress(); } -const String getWebVersion() { +const String getWebVersion() +{ String text = readFile("/index.html", 2000); text = selectFromMarkerToMarker(text, "title", 1); text = selectFromMarkerToMarker(text, " ", 2); @@ -64,7 +64,8 @@ const String getWebVersion() { return text; } -uint32_t ESP_getChipId(void) { +uint32_t ESP_getChipId(void) +{ #ifdef ESP32 uint32_t id = 0; for (uint32_t i = 0; i < 17; i = i + 8) { @@ -76,7 +77,11 @@ uint32_t ESP_getChipId(void) { #endif } -uint32_t ESP_getFlashChipId(void) { +// устарела используем новую функцию ниже +#if !defined(esp32s2_4mb) && !defined(esp32c3m_4mb) && !defined(esp32s3_16mb) +//#ifndef esp32s2_4mb +uint32_t ESP_getFlashChipId(void) +{ #ifdef ESP32 // Нет аналогичной (без доп.кода) функций в 32 // надо использовать другой id - варианты есть @@ -85,10 +90,38 @@ uint32_t ESP_getFlashChipId(void) { return ESP.getFlashChipId(); #endif } +#endif -const String getMacAddress() { +// https://github.com/espressif/arduino-esp32/issues/6945#issuecomment-1199900892 +// получение flash ch id из проекта esp easy + +uint32_t getFlashChipIdNew() +{ + // Cache since size does not change + static uint32_t flashChipId = 0; + + if (flashChipId == 0) { +#ifdef ESP32 + uint32_t tmp = g_rom_flashchip.device_id; + + for (int i = 0; i < 3; ++i) { + flashChipId = flashChipId << 8; + flashChipId |= (tmp & 0xFF); + tmp = tmp >> 8; + } + + // esp_flash_read_id(nullptr, &flashChipId); +#elif defined(ESP8266) + flashChipId = ESP.getFlashChipId(); +#endif // ifdef ESP32 + } + return flashChipId; +} + +const String getMacAddress() +{ uint8_t mac[6]; - char buf[13] = {0}; + char buf[13] = { 0 }; #if defined(ESP8266) WiFi.macAddress(mac); sprintf(buf, MACSTR, MAC2STR(mac)); diff --git a/src/EventsAndOrders.cpp b/src/EventsAndOrders.cpp index 20416471..4099d1a2 100644 --- a/src/EventsAndOrders.cpp +++ b/src/EventsAndOrders.cpp @@ -51,16 +51,21 @@ void handleEvent() { if (eventBuf.length()) { String event = selectToMarker(eventBuf, ","); SerialPrint("i", F("EVENT"), event); - String enentIdName = selectToMarker(event, " "); + String eventIdName = selectToMarker(event, " "); + IoTItem* eventIoTItem = findIoTItem(eventIdName); + + if (eventIoTItem) + // распространяем событие через хуки + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + (*it)->onRegEvent(eventIoTItem); // прямой хук - // распространяем событие через хуки - for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { - (*it)->onRegEvent(findIoTItem(enentIdName)); - } + // вызов хука при условии отслеживания изменения + if ((*it)->isTracking(eventIoTItem)) (*it)->onTrackingValue(eventIoTItem); + } //здесь нужно пропускать данное событие через условия сценариев - //и если оно есть в условии сценария и совподает - iotScen.exec(enentIdName); + //и если оно есть в условии сценария и совпадает + iotScen.exec(eventIdName); eventBuf = deleteBeforeDelimiter(eventBuf, ","); } diff --git a/src/Global.cpp b/src/Global.cpp index efab097a..1130691c 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -31,7 +31,8 @@ WebSocketsServer standWebSocket = WebSocketsServer(81); **********************************************************************************************************************/ IoTGpio IoTgpio(0); IoTItem* rtcItem = nullptr; - +//IoTItem* camItem = nullptr; +IoTItem* tlgrmItem = nullptr; String settingsFlashJson = "{}"; // переменная в которой хранятся все настройки, находится в оперативной памяти и синхронизированна с flash памятью String valuesFlashJson = "{}"; // переменная в которой хранятся все значения элементов, которые необходимо сохранить на flash. Находится в оперативной памяти и синхронизированна с flash памятью String errorsHeapJson = "{}"; // переменная в которой хранятся все ошибки, находится в оперативной памяти только diff --git a/src/Main.cpp b/src/Main.cpp index 0e621c25..75341d64 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -3,14 +3,15 @@ #include "classes/IoTDB.h" #include "utils/Statistic.h" #include +#if defined(esp32s2_4mb) || defined(esp32s3_16mb) +#include +#endif IoTScenario iotScen; // объект управления сценарием String volStrForSave = ""; -unsigned long currentMillis; -unsigned long prevMillis; - - +// unsigned long currentMillis; // это сдесь лишнее +// unsigned long prevMillis; void elementsLoop() { // передаем управление каждому элементу конфигурации для выполнения своих функций @@ -29,69 +30,65 @@ void elementsLoop() { handleEvent(); } +#define SETUPBASE_ERRORMARKER 0 +#define SETUPCONF_ERRORMARKER 1 +#define SETUPSCEN_ERRORMARKER 2 +#define SETUPINET_ERRORMARKER 3 +#define SETUPLAST_ERRORMARKER 4 +#define TICKER_ERRORMARKER 5 +#define HTTP_ERRORMARKER 6 +#define SOCKETS_ERRORMARKER 7 +#define MQTT_ERRORMARKER 8 +#define MODULES_ERRORMARKER 9 +#define COUNTER_ERRORMARKER 4 // количество шагов счетчика +#define STEPPER_ERRORMARKER 100000 // размер шага счетчика интервала доверия выполнения блока кода мкс +#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) - -#define SETUPBASE_ERRORMARKER 0 -#define SETUPCONF_ERRORMARKER 1 -#define SETUPSCEN_ERRORMARKER 2 -#define SETUPINET_ERRORMARKER 3 -#define SETUPLAST_ERRORMARKER 4 -#define TICKER_ERRORMARKER 5 -#define HTTP_ERRORMARKER 6 -#define SOCKETS_ERRORMARKER 7 -#define MQTT_ERRORMARKER 8 -#define MODULES_ERRORMARKER 9 - -#define COUNTER_ERRORMARKER 4 // количество шагов счетчика -#define STEPPER_ERRORMARKER 100000 // размер шага счетчика интервала доверия выполнения блока кода мкс - -#ifdef esp32_4mb - -static int IRAM_ATTR initErrorMarkerId = 0; // ИД маркера +static int IRAM_ATTR initErrorMarkerId = 0; // ИД маркера static int IRAM_ATTR errorMarkerId = 0; static int IRAM_ATTR errorMarkerCounter = 0; hw_timer_t *My_timer = NULL; -void IRAM_ATTR onTimer(){ +void IRAM_ATTR onTimer() { if (errorMarkerCounter >= 0) { if (errorMarkerCounter >= COUNTER_ERRORMARKER) { errorMarkerId = initErrorMarkerId; errorMarkerCounter = -1; - } else + } else errorMarkerCounter++; - } + } } #endif void initErrorMarker(int id) { -#ifdef esp32_4mb +#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) initErrorMarkerId = id; errorMarkerCounter = 0; #endif } void stopErrorMarker(int id) { -#ifdef esp32_4mb +#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) errorMarkerCounter = -1; - if (errorMarkerId) - SerialPrint("I", "WARNING!", "A lazy (freezing loop more than " + (String)(COUNTER_ERRORMARKER * STEPPER_ERRORMARKER / 1000) + " ms) section has been found! With ID=" + (String)errorMarkerId); + if (errorMarkerId) + SerialPrint("I", "WARNING!", "A lazy (freezing loop more than " + (String)(COUNTER_ERRORMARKER * STEPPER_ERRORMARKER / 1000) + " ms) section has been found! With ID=" + (String)errorMarkerId); errorMarkerId = 0; initErrorMarkerId = 0; #endif } - - void setup() { - -#ifdef esp32_4mb +#if defined(esp32s2_4mb) || defined(esp32s3_16mb) + USB.begin(); +#endif +#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) My_timer = timerBegin(0, 80, true); timerAttachInterrupt(My_timer, &onTimer, true); timerAlarmWrite(My_timer, STEPPER_ERRORMARKER, true); timerAlarmEnable(My_timer); - //timerAlarmDisable(My_timer); + // timerAlarmDisable(My_timer); initErrorMarker(SETUPBASE_ERRORMARKER); #endif @@ -102,7 +99,7 @@ void setup() { Serial.println(F("--------------started----------------")); // создание экземпляров классов - // myNotAsyncActions = new NotAsync(do_LAST); + // myNotAsyncActions = new NotAsync(do_LAST); // инициализация файловой системы fileSystemInit(); @@ -110,6 +107,9 @@ void setup() { Serial.println("FIRMWARE NAME " + String(FIRMWARE_NAME)); Serial.println("FIRMWARE VERSION " + String(FIRMWARE_VERSION)); Serial.println("WEB VERSION " + getWebVersion()); + const String buildTime = String(BUILD_DAY) + "." + String(BUILD_MONTH) + "." + String(BUILD_YEAR) + " " + String(BUILD_HOUR) + ":" + String(BUILD_MIN) + ":" + String(BUILD_SEC); + Serial.println("BUILD TIME " + buildTime); + jsonWriteStr_(errorsHeapJson, F("bt"), buildTime); Serial.println(F("------------------------")); // получение chip id @@ -129,7 +129,7 @@ void setup() { jsonRead(settingsFlashJson, "i2cFreq", i2cFreq, false); jsonRead(settingsFlashJson, "i2c", i2c, false); if (i2c != 0) { -#ifdef esp32_4mb +#ifdef ESP32 Wire.end(); Wire.begin(pinSDA, pinSCL, (uint32_t)i2cFreq); #else @@ -141,23 +141,19 @@ void setup() { // настраиваем микроконтроллер configure("/config.json"); - + stopErrorMarker(SETUPCONF_ERRORMARKER); - - initErrorMarker(SETUPSCEN_ERRORMARKER); - + // подготавливаем сценарии iotScen.loadScenario("/scenario.txt"); // создаем событие завершения инициализации основных моментов для возможности выполнения блока кода при загрузке createItemFromNet("onInit", "1", 1); elementsLoop(); - - stopErrorMarker(SETUPSCEN_ERRORMARKER); - - + stopErrorMarker(SETUPSCEN_ERRORMARKER); + initErrorMarker(SETUPINET_ERRORMARKER); // подключаемся к роутеру @@ -181,8 +177,6 @@ void setup() { stopErrorMarker(SETUPINET_ERRORMARKER); - - initErrorMarker(SETUPLAST_ERRORMARKER); // NTP @@ -191,11 +185,10 @@ void setup() { // инициализация задач переодического выполнения periodicTasksInit(); - // синхронизация списка устройств - addThisDeviceToList(); - // запуск работы udp - asyncUdpInit(); + addThisDeviceToList(); + udpListningInit(); + udpBroadcastInit(); // создаем событие завершения конфигурирования для возможности выполнения блока кода при загрузке createItemFromNet("onStart", "1", 1); @@ -227,8 +220,6 @@ void setup() { stopErrorMarker(SETUPLAST_ERRORMARKER); } - - void loop() { #ifdef LOOP_DEBUG unsigned long st = millis(); @@ -253,7 +244,7 @@ void loop() { initErrorMarker(MQTT_ERRORMARKER); mqttLoop(); stopErrorMarker(MQTT_ERRORMARKER); - + initErrorMarker(MODULES_ERRORMARKER); elementsLoop(); stopErrorMarker(MODULES_ERRORMARKER); diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp index c2480941..c6edb6e7 100644 --- a/src/MqttClient.cpp +++ b/src/MqttClient.cpp @@ -5,8 +5,8 @@ void mqttInit() { ts.add( WIFI_MQTT_CONNECTION_CHECK, MQTT_RECONNECT_INTERVAL, [&](void*) { - if (WiFi.status() == WL_CONNECTED) { - SerialPrint("i", F("WIFI"), "OK: " + jsonReadStr(settingsFlashJson, F("ip"))); + if (isNetworkActive()) { + SerialPrint("i", F("WIFI"), "http://" + jsonReadStr(settingsFlashJson, F("ip"))); wifiUptimeCalc(); if (mqtt.connected()) { SerialPrint("i", F("MQTT"), "OK"); @@ -95,6 +95,10 @@ void mqttReconnect() { mqttConnect(); } +bool mqttIsConnect(){ + return mqtt.connected(); +} + void getMqttData() { mqttServer = jsonReadStr(settingsFlashJson, F("mqttServer")); mqttPort = jsonReadInt(settingsFlashJson, F("mqttPort")); @@ -114,6 +118,28 @@ void mqttSubscribe() { mqtt.subscribe((mqttPrefix + "/+/+/order/#").c_str()); mqtt.subscribe((mqttPrefix + "/+/+/info").c_str()); } + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + if ((*it)->getSubtype() == "ExternalMQTT") { + String tmps = (*it)->getMqttExterSub(); + if (tmps != ""){ + mqtt.subscribe(tmps.c_str()); + SerialPrint("i", F("MQTT"), ("subscribed external " + tmps).c_str()); + } + } + } +} + +void mqttSubscribeExternal(String topic, bool usePrefix) { + + // SerialPrint("i", F("MQTT"), mqttRootDevice); + String _sb_topic = topic; + if (usePrefix) + { + _sb_topic = mqttPrefix + "/" + topic; + } + mqtt.subscribe(_sb_topic.c_str()); + + SerialPrint("i", F("MQTT"), ("subscribed external " + _sb_topic).c_str()); } void mqttCallback(char* topic, uint8_t* payload, size_t length) { @@ -133,10 +159,10 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { if (payloadStr.startsWith("HELLO")) { SerialPrint("i", F("MQTT"), F("Full update")); - //публикация всех виджетов + // публикация всех виджетов publishWidgets(); - //публикация всех статус сообщений при подключении приложения и генерация события подключения приложения в модулях + // публикация всех статус сообщений при подключении приложения и генерация события подключения приложения в модулях for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { if ((*it)->iAmLocal) { publishStatusMqtt((*it)->getID(), (*it)->getValue()); @@ -144,7 +170,7 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { } } - //отправка данных графиков - данный код будет оптимизирован после завершения написания приложения с новыми графиками + // отправка данных графиков - данный код будет оптимизирован после завершения написания приложения с новыми графиками for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { if ((*it)->getSubtype() == "Loging" || "LogingDaily") { (*it)->setPublishDestination(TO_MQTT); @@ -162,16 +188,17 @@ void mqttCallback(char* topic, uint8_t* payload, size_t length) { else if (topicStr.indexOf(F("control")) != -1) { String key = selectFromMarkerToMarker(topicStr, "/", 3); - - String valueIfJson = ""; // проверяем формат, если json то берем статус, иначе - как есть + + String valueIfJson = ""; // проверяем формат, если json то берем статус, иначе - как есть if (!jsonRead(payloadStr, F("status"), valueIfJson, false)) generateOrder(key, payloadStr); - else generateOrder(key, valueIfJson); + else + generateOrder(key, valueIfJson); SerialPrint("i", F("=>MQTT"), "Msg from iotmanager app: " + key + " " + payloadStr); } - //здесь мы получаем события с других устройств, которые потом проверяются в сценариях этого устройства + // здесь мы получаем события с других устройств, которые потом проверяются в сценариях этого устройства else if (topicStr.indexOf("event") != -1) { if (!jsonReadBool(settingsFlashJson, "mqttin")) { return; @@ -251,7 +278,7 @@ void publishWidgets() { DeserializationError error = deserializeJson(doc, file); if (error) { SerialPrint("E", F("MQTT"), error.f_str()); - jsonWriteInt(errorsHeapJson, F("jse3"), 1); //Ошибка чтения json файла с виджетами при отправки в mqtt + jsonWriteInt(errorsHeapJson, F("jse3"), 1); // Ошибка чтения json файла с виджетами при отправки в mqtt } JsonArray arr = doc.as(); for (JsonVariant value : arr) { @@ -292,43 +319,43 @@ void handleMqttStatus(bool send, int state) { const String getStateStr(int e) { switch (e) { - case -4: //Нет ответа от сервера + case -4: // Нет ответа от сервера return F("e1"); break; - case -3: //Соединение было разорвано + case -3: // Соединение было разорвано return F("e2"); break; - case -2: //Ошибка соединения. Обычно возникает когда неверно указано название сервера MQTT + case -2: // Ошибка соединения. Обычно возникает когда неверно указано название сервера MQTT return F("e3"); break; - case -1: //Клиент был отключен + case -1: // Клиент был отключен return F("e4"); break; - case 0: //подключено + case 0: // подключено return F("e5"); break; - case 1: //Ошибка версии + case 1: // Ошибка версии return F("e6"); break; - case 2: //Отклонен идентификатор + case 2: // Отклонен идентификатор return F("e7"); break; - case 3: //Не могу установить соединение + case 3: // Не могу установить соединение return F("e8"); break; - case 4: //Неправильное имя пользователя/пароль + case 4: // Неправильное имя пользователя/пароль return F("e9"); break; - case 5: //Не авторизован для подключения + case 5: // Не авторизован для подключения return F("e10"); break; - case 6: //Название сервера пустое + case 6: // Название сервера пустое return F("e11"); break; - case 7: //Имя пользователя или пароль пустые + case 7: // Имя пользователя или пароль пустые return F("e12"); break; - case 8: //Подключение в процессе + case 8: // Подключение в процессе return F("e13"); break; default: diff --git a/src/NTP.cpp b/src/NTP.cpp index 046f1612..6d4a44e1 100644 --- a/src/NTP.cpp +++ b/src/NTP.cpp @@ -184,6 +184,14 @@ const String getDateTimeDotFormatedFromUnix(unsigned long unixTime) { return String(buf); } +const String getTimeDotFormatedFromUnix(unsigned long unixTime) { + Time_t time; + breakEpochToTime(unixTime, time); + char buf[32]; + sprintf(buf, "%02d:%02d:%02d", time.hour, time.minute, time.second); + return String(buf); +} + const String getDateDotFormatedFromUnix(unsigned long unixTime) { Time_t time; breakEpochToTime(unixTime, time); diff --git a/src/PeriodicTasks.cpp b/src/PeriodicTasks.cpp index 64675569..f9fa9c9d 100644 --- a/src/PeriodicTasks.cpp +++ b/src/PeriodicTasks.cpp @@ -58,7 +58,50 @@ void printGlobalVarSize() { String ESP_getResetReason(void) { return ESP.getResetReason(); } -#else +#endif +#if defined(esp32s2_4mb) || defined(esp32s3_16mb) || defined(esp32c3m_4mb) +String ESP_getResetReason(void) { + return ESP32GetResetReason(0); // CPU 0 +} +String ESP32GetResetReason(uint32_t cpu_no) { + // tools\sdk\include\esp32\rom\rtc.h + switch (rtc_get_reset_reason((RESET_REASON)cpu_no)) { + case POWERON_RESET: + return F("Vbat power on reset"); // 1 + case RTC_SW_SYS_RESET: + return F("Software reset digital core"); // 3 +// case OWDT_RESET: +// return F("Legacy Watchdog reset digital core"); // 4 + case DEEPSLEEP_RESET: + return F("Deep Sleep reset digital core"); // 5 +// case SDIO_RESET: +// return F("Reset by SLC module, reset digital core"); // 6 + case TG0WDT_SYS_RESET: + return F("Timer Group0 Watchdog reset digital core"); // 7 + case TG1WDT_SYS_RESET: + return F("Timer Group1 Watchdog reset digital core"); // 8 + case RTCWDT_SYS_RESET: + return F("RTC Watchdog Reset digital core"); // 9 + case INTRUSION_RESET: + return F("Instrusion tested to reset CPU"); // 10 + case TG0WDT_CPU_RESET: + return F("Time Group reset CPU"); // 11 + case RTC_SW_CPU_RESET: + return F("Software reset CPU"); // 12 + case RTCWDT_CPU_RESET: + return F("RTC Watchdog Reset CPU"); // 13 +// case EXT_CPU_RESET: +// return F("or APP CPU, reseted by PRO CPU"); // 14 + case RTCWDT_BROWN_OUT_RESET: + return F("Reset when the vdd voltage is not stable"); // 15 + case RTCWDT_RTC_RESET: + return F("RTC Watchdog reset digital core and rtc module"); // 16 + default: + return F("NO_MEAN"); // 0 + } +} +#endif +#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) String ESP_getResetReason(void) { return ESP32GetResetReason(0); // CPU 0 } diff --git a/src/StandWebServer.cpp b/src/StandWebServer.cpp index 5f899613..6a23378f 100644 --- a/src/StandWebServer.cpp +++ b/src/StandWebServer.cpp @@ -7,24 +7,22 @@ String unsupportedFiles = String(); static const char TEXT_PLAIN[] PROGMEM = "text/plain"; static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR"; static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound"; -//static bool fsOK; -//const char* fsName = "LittleFS"; +// static bool fsOK; +// const char* fsName = "LittleFS"; -void standWebServerInit() -{ +void standWebServerInit() { // Кэшировать файлы для быстрой работы - HTTP.serveStatic("/bundle.js", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.css", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.js.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/bundle.css.gz", FileFS, "/", "max-age=31536000"); // кеширование на 1 год - HTTP.serveStatic("/favicon.png", FileFS, "/", "max-age=31536000"); // кеширование на 1 год + // если указана директория то все файлы будут отмечены как Directory Request Handler + // если указан файл то он будет отмечен как File Request Handler + HTTP.serveStatic("/build", FileFS, "/build", "max-age=31536000"); // кеширование на 1 год + HTTP.serveStatic("/favicon.ico", FileFS, "/favicon.ico", "max-age=31536000"); // кеширование на 1 год // HTTP.on("/devicelist.json", HTTP_GET, []() { // HTTP.send(200, "application/json", devListHeapJson); // }); - HTTP.on("/settings.h.json", HTTP_GET, []() { - HTTP.send(200, "application/json", settingsFlashJson); - }); + // HTTP.on("/settings.h.json", HTTP_GET, []() { + // HTTP.send(200, "application/json", settingsFlashJson); + //}); // HTTP.on("/settings.f.json", HTTP_GET, []() { // HTTP.send(200, "application/json", readFile(F("settings.json"), 20000)); // }); @@ -46,21 +44,19 @@ void standWebServerInit() // HTTP.send(200, "text/plain", "ok"); // }); - HTTP.on("/set", HTTP_GET, []() { if (HTTP.hasArg(F("routerssid")) && WiFi.getMode() == WIFI_AP) { - jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid"))); - syncSettingsFlashJson(); - HTTP.send(200, "text/plain", "ok"); - } + jsonWriteStr(settingsFlashJson, F("routerssid"), HTTP.arg(F("routerssid"))); + syncSettingsFlashJson(); + HTTP.send(200, "text/plain", "ok"); + } if (HTTP.hasArg(F("routerpass")) && WiFi.getMode() == WIFI_AP) { - jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass"))); - syncSettingsFlashJson(); - HTTP.send(200, "text/plain", "ok"); - } - - }); + jsonWriteStr(settingsFlashJson, F("routerpass"), HTTP.arg(F("routerpass"))); + syncSettingsFlashJson(); + HTTP.send(200, "text/plain", "ok"); + } + }); // Добавляем функцию Update для перезаписи прошивки по WiFi при 1М(256K FileFS) и выше // httpUpdater.setup(&HTTP); @@ -101,39 +97,33 @@ void standWebServerInit() //////////////////////////////// // Utils to return HTTP codes, and determine content-type -void replyOK() -{ +void replyOK() { HTTP.send(200, FPSTR(TEXT_PLAIN), ""); } -void replyOKWithMsg(String msg) -{ +void replyOKWithMsg(String msg) { HTTP.send(200, FPSTR(TEXT_PLAIN), msg); } -void replyNotFound(String msg) -{ +void replyNotFound(String msg) { HTTP.send(404, FPSTR(TEXT_PLAIN), msg); } -void replyBadRequest(String msg) -{ -// DBG_OUTPUT_PORT.println(msg); +void replyBadRequest(String msg) { + // DBG_OUTPUT_PORT.println(msg); HTTP.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n"); } -void replyServerError(String msg) -{ -// DBG_OUTPUT_PORT.println(msg); +void replyServerError(String msg) { + // DBG_OUTPUT_PORT.println(msg); HTTP.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n"); } /* Return the FS type, status and size info */ -void handleStatus() -{ -// DBG_OUTPUT_PORT.println("handleStatus"); +void handleStatus() { + // DBG_OUTPUT_PORT.println("handleStatus"); String json; json.reserve(128); @@ -141,22 +131,22 @@ void handleStatus() json += FS_NAME; json += "\", \"isOk\":"; - #ifdef ESP8266 +#ifdef ESP8266 FSInfo fs_info; - FileFS.info(fs_info); - json += F("\"true\", \"totalBytes\":\""); - json += fs_info.totalBytes; - json += F("\", \"usedBytes\":\""); - json += fs_info.usedBytes; - json += "\""; -#endif + FileFS.info(fs_info); + json += F("\"true\", \"totalBytes\":\""); + json += fs_info.totalBytes; + json += F("\", \"usedBytes\":\""); + json += fs_info.usedBytes; + json += "\""; +#endif #ifdef ESP32 - json += F("\"true\", \"totalBytes\":\""); - json += String(FileFS.totalBytes()); - json += F("\", \"usedBytes\":\""); - json += String(FileFS.usedBytes()); - json += "\""; + json += F("\"true\", \"totalBytes\":\""); + json += String(FileFS.totalBytes()); + json += F("\", \"usedBytes\":\""); + json += String(FileFS.usedBytes()); + json += "\""; #endif json += F(",\"unsupportedFiles\":\""); @@ -168,74 +158,66 @@ void handleStatus() #ifdef ESP32 String getContentType(String filename) { - if (HTTP.hasArg("download")) { - return "application/octet-stream"; - } else if (filename.endsWith(".htm")) { - return "text/html"; - } else if (filename.endsWith(".html")) { - return "text/html"; - } else if (filename.endsWith(".css")) { - return "text/css"; - } else if (filename.endsWith(".js")) { - return "application/javascript"; - } else if (filename.endsWith(".png")) { - return "image/png"; - } else if (filename.endsWith(".gif")) { - return "image/gif"; - } else if (filename.endsWith(".jpg")) { - return "image/jpeg"; - } else if (filename.endsWith(".ico")) { - return "image/x-icon"; - } else if (filename.endsWith(".xml")) { - return "text/xml"; - } else if (filename.endsWith(".pdf")) { - return "application/x-pdf"; - } else if (filename.endsWith(".zip")) { - return "application/x-zip"; - } else if (filename.endsWith(".gz")) { - return "application/x-gzip"; - } - return "text/plain"; + if (HTTP.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; } #endif /* Read the given file from the filesystem and stream it back to the client */ -bool handleFileRead(String path) -{ -// DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); - if (path.endsWith("/")) - { +bool handleFileRead(String path) { + // DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); + if (path.endsWith("/")) { path += "index.html"; } String contentType; - if (HTTP.hasArg("download")) - { + if (HTTP.hasArg("download")) { contentType = F("application/octet-stream"); - } - else - { -#ifdef ESP32 + } else { +#ifdef ESP32 contentType = getContentType(path); #endif #ifdef ESP8266 contentType = mime::getContentType(path); -#endif +#endif } - if (!FileFS.exists(path)) - { + if (!FileFS.exists(path)) { // File not found, try gzip version path = path + ".gz"; } - if (FileFS.exists(path)) - { + if (FileFS.exists(path)) { File file = FileFS.open(path, "r"); - if (HTTP.streamFile(file, contentType) != file.size()) - { - // DBG_OUTPUT_PORT.println("Sent less data than expected!"); + if (HTTP.streamFile(file, contentType) != file.size()) { + // DBG_OUTPUT_PORT.println("Sent less data than expected!"); } file.close(); return true; @@ -248,90 +230,69 @@ bool handleFileRead(String path) As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, return the path of the closest parent still existing */ -String lastExistingParent(String path) -{ - while (!path.isEmpty() && !FileFS.exists(path)) - { - if (path.lastIndexOf('/') > 0) - { +String lastExistingParent(String path) { + while (!path.isEmpty() && !FileFS.exists(path)) { + if (path.lastIndexOf('/') > 0) { path = path.substring(0, path.lastIndexOf('/')); - } - else - { - path = String(); // No slash => the top folder does not exist + } else { + path = String(); // No slash => the top folder does not exist } } -// DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + // DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); return path; } /* Handle a file upload request */ -void handleFileUpload() -{ - if (HTTP.uri() != "/edit") - { +void handleFileUpload() { + if (HTTP.uri() != "/edit") { return; } HTTPUpload &upload = HTTP.upload(); - if (upload.status == UPLOAD_FILE_START) - { + if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; // Make sure paths always start with "/" - if (!filename.startsWith("/")) - { + if (!filename.startsWith("/")) { filename = "/" + filename; } -// DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); + // DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = FileFS.open(filename, "w"); - if (!uploadFile) - { + if (!uploadFile) { return replyServerError(F("CREATE FAILED")); } -// DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); - } - else if (upload.status == UPLOAD_FILE_WRITE) - { - if (uploadFile) - { + // DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); - if (bytesWritten != upload.currentSize) - { + if (bytesWritten != upload.currentSize) { return replyServerError(F("WRITE FAILED")); } } -// DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); - } - else if (upload.status == UPLOAD_FILE_END) - { - if (uploadFile) - { + // DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + if (uploadFile) { uploadFile.close(); } -// DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); + // DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } - #ifdef ESP8266 -void deleteRecursive(String path) -{ +void deleteRecursive(String path) { File file = FileFS.open(path, "r"); bool isDir = file.isDirectory(); file.close(); // If it's a plain file, delete it - if (!isDir) - { + if (!isDir) { FileFS.remove(path); return; } Dir dir = FileFS.openDir(path); - while (dir.next()) - { + while (dir.next()) { deleteRecursive(path + '/' + dir.fileName()); - } + } // Then delete the folder itself FileFS.rmdir(path); @@ -339,41 +300,37 @@ void deleteRecursive(String path) #endif #ifdef ESP32 -struct treename{ - uint8_t type; - char *name; +struct treename { + uint8_t type; + char *name; }; +void deleteRecursive(String path) { + fs::File dir = FileFS.open(path); -void deleteRecursive( String path ){ - fs::File dir = FileFS.open( path ); - - if(!dir.isDirectory()){ - Serial.printf("%s is a file\n", path); - dir.close(); - Serial.printf( "result of removing file %s: %d\n", path, FileFS.remove( path ) ); - return; + if (!dir.isDirectory()) { + Serial.printf("%s is a file\n", path); + dir.close(); + Serial.printf("result of removing file %s: %d\n", path, FileFS.remove(path)); + return; } - + Serial.printf("%s is a directory\n", path); - + fs::File entry, nextentry; - - while ( entry = dir.openNextFile() ){ - -if ( entry.isDirectory() ){ - deleteRecursive( entry.path() ); - } else{ - String tmpname = path+"/"+strdup( entry.name() ); // buffer file name - entry.close(); - Serial.printf( "result of removing file %s: %d\n", tmpname, FileFS.remove( tmpname ) ); - } - + + while (entry = dir.openNextFile()) { + if (entry.isDirectory()) { + deleteRecursive(entry.path()); + } else { + String tmpname = path + "/" + strdup(entry.name()); // buffer file name + entry.close(); + Serial.printf("result of removing file %s: %d\n", tmpname, FileFS.remove(tmpname)); + } } - dir.close(); - Serial.printf( "result of removing directory %s: %d\n", path, FileFS.rmdir( path ) ); - + dir.close(); + Serial.printf("result of removing directory %s: %d\n", path, FileFS.rmdir(path)); } #endif /* @@ -383,23 +340,18 @@ if ( entry.isDirectory() ){ Delete file | parent of deleted file, or remaining ancestor Delete folder | parent of deleted folder, or remaining ancestor */ -void handleFileDelete() -{ +void handleFileDelete() { String path = HTTP.arg(0); - if (path.isEmpty() || path == "/") - { + if (path.isEmpty() || path == "/") { return replyBadRequest("BAD PATH"); } -// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (!FileFS.exists(path)) - { + // DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); + if (!FileFS.exists(path)) { return replyNotFound(FPSTR(FILE_NOT_FOUND)); } deleteRecursive(path); - - replyOKWithMsg(lastExistingParent(path)); } @@ -414,93 +366,72 @@ void handleFileDelete() Rename folder | parent of source folder Move folder | parent of source folder, or remaining ancestor */ -void handleFileCreate() -{ +void handleFileCreate() { String path = HTTP.arg("path"); - if (path.isEmpty()) - { + if (path.isEmpty()) { return replyBadRequest(F("PATH ARG MISSING")); } #ifdef USE_SPIFFS - if (checkForUnsupportedPath(path).length() > 0) - { + if (checkForUnsupportedPath(path).length() > 0) { return replyServerError(F("INVALID FILENAME")); } #endif - if (path == "/") - { + if (path == "/") { return replyBadRequest("BAD PATH"); } - if (FileFS.exists(path)) - { + if (FileFS.exists(path)) { return replyBadRequest(F("PATH FILE EXISTS")); } String src = HTTP.arg("src"); - if (src.isEmpty()) - { + if (src.isEmpty()) { // No source specified: creation -// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); - if (path.endsWith("/")) - { + // DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { // Create a folder path.remove(path.length() - 1); - if (!FileFS.mkdir(path)) - { + if (!FileFS.mkdir(path)) { return replyServerError(F("MKDIR FAILED")); } - } - else - { + } else { // Create a file File file = FileFS.open(path, "w"); - if (file) - { -#ifdef ESP8266 + if (file) { +#ifdef ESP8266 file.write((const char *)0); #endif #ifdef ESP32 file.write(0); -#endif +#endif file.close(); - } - else - { + } else { return replyServerError(F("CREATE FAILED")); } } - if (path.lastIndexOf('/') > -1) - { + if (path.lastIndexOf('/') > -1) { path = path.substring(0, path.lastIndexOf('/')); } replyOKWithMsg(path); - } - else - { + } else { // Source specified: rename - if (src == "/") - { + if (src == "/") { return replyBadRequest("BAD SRC"); } - if (!FileFS.exists(src)) - { + if (!FileFS.exists(src)) { return replyBadRequest(F("SRC FILE NOT FOUND")); } -// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); + // DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); - if (path.endsWith("/")) - { + if (path.endsWith("/")) { path.remove(path.length() - 1); } - if (src.endsWith("/")) - { + if (src.endsWith("/")) { src.remove(src.length() - 1); } - if (!FileFS.rename(src, path)) - { + if (!FileFS.rename(src, path)) { return replyServerError(F("RENAME FAILED")); } replyOKWithMsg(lastExistingParent(src)); @@ -512,26 +443,22 @@ void handleFileCreate() Also demonstrates the use of chunked responses. */ #ifdef ESP8266 -void handleFileList() -{ - if (!HTTP.hasArg("dir")) - { +void handleFileList() { + if (!HTTP.hasArg("dir")) { return replyBadRequest(F("DIR ARG MISSING")); } String path = HTTP.arg("dir"); - if (path != "/" && !FileFS.exists(path)) - { + if (path != "/" && !FileFS.exists(path)) { return replyBadRequest("BAD PATH"); } -// DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); + // DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = FileFS.openDir(path); path.clear(); // use HTTP/1.1 Chunked response to avoid building a huge temporary string - if (!HTTP.chunkedResponseModeStart(200, "text/json")) - { + if (!HTTP.chunkedResponseModeStart(200, "text/json")) { HTTP.send(505, F("text/html"), F("HTTP1.1 required")); return; } @@ -539,47 +466,36 @@ void handleFileList() // use the same string for every line String output; output.reserve(64); - while (dir.next()) - { + while (dir.next()) { #ifdef USE_SPIFFS String error = checkForUnsupportedPath(dir.fileName()); - if (error.length() > 0) - { -// DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + if (error.length() > 0) { + // DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); continue; } #endif - if (output.length()) - { + if (output.length()) { // send string from previous iteration // as an HTTP chunk HTTP.sendContent(output); output = ','; - } - else - { + } else { output = '['; } output += "{\"type\":\""; - if (dir.isDirectory()) - { + if (dir.isDirectory()) { output += "dir"; - } - else - { + } else { output += F("file\",\"size\":\""); output += dir.fileSize(); } output += F("\",\"name\":\""); // Always return names without leading "/" - if (dir.fileName()[0] == '/') - { + if (dir.fileName()[0] == '/') { output += &(dir.fileName()[1]); - } - else - { + } else { output += dir.fileName(); } @@ -595,65 +511,57 @@ void handleFileList() #ifdef ESP32 void handleFileList() { - if (!HTTP.hasArg("dir")) { - HTTP.send(500, "text/plain", "BAD ARGS"); - return; - } + if (!HTTP.hasArg("dir")) { + HTTP.send(500, "text/plain", "BAD ARGS"); + return; + } - String path = HTTP.arg("dir"); -// DBG_OUTPUT_PORT.println("handleFileList: " + path); + String path = HTTP.arg("dir"); + // DBG_OUTPUT_PORT.println("handleFileList: " + path); + File root = FileFS.open(path); + path = String(); - File root = FileFS.open(path); - path = String(); + String output = "["; + if (root.isDirectory()) { + File file = root.openNextFile(); + while (file) { + if (output != "[") { + output += ','; + } + output += "{\"type\":\""; + // output += (file.isDirectory()) ? "dir" : "file"; + if (file.isDirectory()) { + output += "dir"; + } else { + output += F("file\",\"size\":\""); + output += file.size(); + } - String output = "["; - if(root.isDirectory()){ - File file = root.openNextFile(); - while(file){ - if (output != "[") { - output += ','; - } - output += "{\"type\":\""; - // output += (file.isDirectory()) ? "dir" : "file"; - if (file.isDirectory()) - { - output += "dir"; - } - else - { - output += F("file\",\"size\":\""); - output += file.size(); - } - - output += "\",\"name\":\""; + output += "\",\"name\":\""; output += String(file.name()); - output += "\"}"; - file = root.openNextFile(); - } - } - output += "]"; - HTTP.send(200, "text/json", output); - + output += "\"}"; + file = root.openNextFile(); + } + } + output += "]"; + HTTP.send(200, "text/json", output); } #endif - /* The "Not Found" handler catches all URI not explicitly declared in code First try to find and return the requested file from the filesystem, and if it fails, return a 404 page with debug information */ -void handleNotFound() -{ +void handleNotFound() { #ifdef ESP8266 - String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks + String uri = ESP8266WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks #endif #ifdef ESP32 - String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks + String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks #endif - if (handleFileRead(uri)) - { + if (handleFileRead(uri)) { return; } @@ -667,8 +575,7 @@ void handleNotFound() message += F("\nArguments: "); message += HTTP.args(); message += '\n'; - for (uint8_t i = 0; i < HTTP.args(); i++) - { + for (uint8_t i = 0; i < HTTP.args(); i++) { message += F(" NAME:"); message += HTTP.argName(i); message += F("\n VALUE:"); @@ -678,7 +585,7 @@ void handleNotFound() message += "path="; message += HTTP.arg("path"); message += '\n'; -// DBG_OUTPUT_PORT.print(message); + // DBG_OUTPUT_PORT.print(message); return replyNotFound(message); } @@ -689,10 +596,8 @@ void handleNotFound() embedded in the program code. Otherwise, fails with a 404 page with debug information */ -void handleGetEdit() -{ - if (handleFileRead(F("/edit.htm"))) - { +void handleGetEdit() { + if (handleFileRead(F("/edit.htm"))) { return; } diff --git a/src/UpgradeFirm.cpp b/src/UpgradeFirm.cpp index 20385771..525ea345 100644 --- a/src/UpgradeFirm.cpp +++ b/src/UpgradeFirm.cpp @@ -2,12 +2,14 @@ updateFirm update; -void upgrade_firmware(int type) { +void upgrade_firmware(int type, String path) { putUserDataToRam(); + // сбросим файл статуса последнего обновления + writeFile("ota.json", "{}"); // only build if (type == 1) { - if (upgradeBuild()) { + if (upgradeBuild(path)) { saveUserDataToFlash(); restartEsp(); } @@ -15,7 +17,7 @@ void upgrade_firmware(int type) { // only littlefs else if (type == 2) { - if (upgradeFS()) { + if (upgradeFS(path)) { saveUserDataToFlash(); restartEsp(); } @@ -23,71 +25,83 @@ void upgrade_firmware(int type) { // littlefs and build else if (type == 3) { - if (upgradeFS()) { + if (upgradeFS(path)) { saveUserDataToFlash(); - if (upgradeBuild()) { + if (upgradeBuild(path)) { restartEsp(); } } } } -bool upgradeFS() { +bool upgradeFS(String path) { bool ret = false; WiFiClient wifiClient; - SerialPrint("!!!", F("Update"), F("Start upgrade FS...")); - handleUpdateStatus(true, UPDATE_FS_IN_PROGRESS); - if (getBinPath("") == "error") { + SerialPrint("!!!", F("Update"), "Start upgrade FS... " + path); + + if (path == "") { SerialPrint("E", F("Update"), F("FS Path error")); - handleUpdateStatus(true, PATH_ERROR); + saveUpdeteStatus("fs", PATH_ERROR); return ret; } #ifdef ESP8266 ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return retFS = ESPhttpUpdate.updateFS(wifiClient, getBinPath("littlefs.bin")); + t_httpUpdate_return retFS = ESPhttpUpdate.updateFS(wifiClient, path + "/littlefs.bin"); #endif #ifdef ESP32 httpUpdate.rebootOnUpdate(false); - //обновляем little fs с помощью метода обновления spiffs - HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, getBinPath("littlefs.bin")); + // обновляем little fs с помощью метода обновления spiffs!!!! + HTTPUpdateResult retFS = httpUpdate.updateSpiffs(wifiClient, path + "/littlefs.bin"); #endif - //если FS обновилась успешно + // если FS обновилась успешно if (retFS == HTTP_UPDATE_OK) { SerialPrint("!!!", F("Update"), F("FS upgrade done!")); - handleUpdateStatus(true, UPDATE_FS_COMPLETED); + saveUpdeteStatus("fs", UPDATE_COMPLETED); ret = true; + } else { + saveUpdeteStatus("fs", UPDATE_FAILED); + if (retFS == HTTP_UPDATE_FAILED) { + SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED"); + } else if (retFS == HTTP_UPDATE_NO_UPDATES) { + SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES"); + } } - handleUpdateStatus(true, UPDATE_FS_FAILED); return ret; } -bool upgradeBuild() { +bool upgradeBuild(String path) { bool ret = false; WiFiClient wifiClient; - SerialPrint("!!!", F("Update"), F("Start upgrade BUILD...")); - handleUpdateStatus(true, UPDATE_BUILD_IN_PROGRESS); - if (getBinPath("") == "error") { + SerialPrint("!!!", F("Update"), "Start upgrade BUILD... " + path); + + if (path == "") { SerialPrint("E", F("Update"), F("Build Path error")); - handleUpdateStatus(true, PATH_ERROR); + saveUpdeteStatus("build", PATH_ERROR); return ret; } -#if defined (esp8266_4mb) || defined (esp8266_1mb) || defined (esp8266_1mb_ota) || defined (esp8266_2mb) || defined (esp8266_2mb_ota) +#if defined(esp8266_4mb) || defined(esp8266_16mb) || defined(esp8266_1mb) || defined(esp8266_1mb_ota) || defined(esp8266_2mb) || defined(esp8266_2mb_ota) ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, getBinPath("firmware.bin")); + t_httpUpdate_return retBuild = ESPhttpUpdate.update(wifiClient, path + "/firmware.bin"); #endif -#ifdef esp32_4mb +#ifdef ESP32 httpUpdate.rebootOnUpdate(false); - HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, getBinPath("firmware.bin")); + HTTPUpdateResult retBuild = httpUpdate.update(wifiClient, path + "/firmware.bin"); #endif - //если BUILD обновился успешно + // если BUILD обновился успешно if (retBuild == HTTP_UPDATE_OK) { SerialPrint("!!!", F("Update"), F("BUILD upgrade done!")); - handleUpdateStatus(true, UPDATE_BUILD_COMPLETED); + saveUpdeteStatus("build", UPDATE_COMPLETED); ret = true; + } else { + saveUpdeteStatus("build", UPDATE_FAILED); + if (retBuild == HTTP_UPDATE_FAILED) { + SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED"); + } else if (retBuild == HTTP_UPDATE_NO_UPDATES) { + SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES"); + } } - handleUpdateStatus(true, UPDATE_BUILD_FAILED); return ret; } @@ -97,29 +111,31 @@ void restartEsp() { ESP.restart(); } -const String getBinPath(String file) { - String path = "error"; - int targetVersion = 0; - String serverip; - if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) { - if (targetVersion >= 400) { - if (jsonRead(settingsFlashJson, F("serverip"), serverip)) { - if (serverip != "") { - path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" + file; - } - } - } - } - SerialPrint("i", F("Update"), "path: " + path); - return path; -} +// теперь путь к обнавленю прошивки мы получаем из веб интерфейса +// const String getBinPath(String file) { +// String path = "error"; +// int targetVersion = 0; +// String serverip; +// if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) { +// if (targetVersion >= 400) { +// if (jsonRead(settingsFlashJson, F("serverip"), serverip)) { +// if (serverip != "") { +// path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" + file; +// } +// } +// } +// } +// SerialPrint("i", F("Update"), "path: " + path); +// return path; +// } +// https://t.me/IoTmanager/128814/164752 - убрал ограничение void putUserDataToRam() { - update.configJson = readFile("config.json", 4096); - update.settingsFlashJson = readFile("settings.json", 4096); - update.layoutJson = readFile("layout.json", 4096); - update.scenarioTxt = readFile("scenario.txt", 4096); - update.chartsData = createDataBaseSting(); + update.configJson = readFile("config.json", 4096 * 4); + update.settingsFlashJson = readFile("settings.json", 4096 * 4); + update.layoutJson = readFile("layout.json", 4096 * 4); + update.scenarioTxt = readFile("scenario.txt", 4096 * 4); + // update.chartsData = createDataBaseSting(); } void saveUserDataToFlash() { @@ -127,10 +143,13 @@ void saveUserDataToFlash() { writeFile("/settings.json", update.settingsFlashJson); writeFile("/layout.json", update.layoutJson); writeFile("/scenario.txt", update.scenarioTxt); - writeDataBaseSting(update.chartsData); + // writeDataBaseSting(update.chartsData); } -void handleUpdateStatus(bool send, int state) { - jsonWriteInt_(errorsHeapJson, F("upd"), state); - if (!send) sendStringToWs("errors", errorsHeapJson, -1); +void saveUpdeteStatus(String key, int val) { + const String path = "ota.json"; + String json = readFile(path, 1024); + if (json == "failed") json = "{}"; + jsonWriteInt_(json, key, val); + writeFile(path, json); } \ No newline at end of file diff --git a/src/WsServer.cpp b/src/WsServer.cpp index 6eec69aa..0965338d 100644 --- a/src/WsServer.cpp +++ b/src/WsServer.cpp @@ -28,8 +28,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) standWebSocket.close(); standWebSocketsInit(); } - // Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); - // standWebSocket.sendTXT(num, "Connected"); + // Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], + // ip[1], ip[2], ip[3], payload); standWebSocket.sendTXT(num, + // "Connected"); } break; case WStype_TEXT: { @@ -118,7 +119,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) clearConfigure(); configure("/config.json"); iotScen.loadScenario("/scenario.txt"); - // создаем событие завершения конфигурирования для возможности выполнения блока кода при загрузке + // создаем событие завершения конфигурирования для возможности + // выполнения блока кода при загрузке createItemFromNet("onStart", "1", 1); } @@ -128,33 +130,42 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) // отвечаем данными на запрос страницы if (headerStr == "/connection|") { + sendFileToWsByFrames("/widgets.json", "widget", "", num, WEB_SOCKETS_FRAME_SIZE); + sendFileToWsByFrames("/config.json", "config", "", num, WEB_SOCKETS_FRAME_SIZE); sendStringToWs("settin", settingsFlashJson, num); sendStringToWs("ssidli", ssidListHeapJson, num); sendStringToWs("errors", errorsHeapJson, num); - // запуск асинхронного сканирования wifi сетей при переходе на страницу соединений - // RouterFind(jsonReadStr(settingsFlashJson, F("routerssid"))); + // запуск асинхронного сканирования wifi сетей при переходе на страницу + // соединений RouterFind(jsonReadStr(settingsFlashJson, + // F("routerssid"))); } - // обработка кнопки сохранить настройки wifi + // обработка кнопки сохранить settings.json if (headerStr == "/sgnittes|") { writeUint8tToString(payload, length, headerLenth, settingsFlashJson); writeFileUint8tByFrames("settings.json", payload, length, headerLenth, 256); sendStringToWs("errors", errorsHeapJson, num); + // если не было создано приема данных по udp - то создадим его addThisDeviceToList(); } // обработка кнопки сохранить настройки mqtt if (headerStr == "/mqtt|") { - sendStringToWs("settin", settingsFlashJson, num); // отправляем в ответ новые полученные настройки - handleMqttStatus(false, 8); // меняем статус на неопределенный - mqttReconnect(); // начинаем переподключение - sendStringToWs("errors", errorsHeapJson, num); // отправляем что статус неопределен + sendStringToWs("settin", settingsFlashJson, + num); // отправляем в ответ новые полученные настройки + handleMqttStatus(false, 8); // меняем статус на неопределенный + mqttReconnect(); // начинаем переподключение + sendStringToWs("errors", errorsHeapJson, + num); // отправляем что статус неопределен sendStringToWs("ssidli", ssidListHeapJson, num); } - // запуск асинхронного сканирования wifi сетей при нажатии выпадающего списка + // запуск асинхронного сканирования wifi сетей при нажатии выпадающего + // списка if (headerStr == "/scan|") { - RouterFind(jsonReadStr(settingsFlashJson, F("routerssid"))); + std::vector jArray; + jsonReadArray(settingsFlashJson, "routerssid", jArray); + RouterFind(jArray); sendStringToWs("ssidli", ssidListHeapJson, num); } @@ -162,9 +173,23 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) // Страница веб интерфейса list //----------------------------------------------------------------------// - // отвечаем данными на запрос страницы + // отвечаем данными на запрос страницы list if (headerStr == "/list|") { - sendStringToWs("devlis", devListHeapJson, num); + sendStringToWs("settin", settingsFlashJson, num); + // отправим список устройств в зависимости от того что выбрал user + // sendDeviceList(num); + } + + // отвечаем на запрос списка устройств (это отдельный запрос который + // делает приложение при подключении) + if (headerStr == "/devlist|") { + // отправим список устройств в зависимости от того что выбрал user + sendDeviceList(num); + } + + // сохраняем данные листа + if (headerStr == "/tsil|") { + writeFileUint8tByFrames("devlist.json", payload, length, headerLenth, 256); } //----------------------------------------------------------------------// @@ -185,10 +210,22 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) sendStringToWs("settin", settingsFlashJson, num); sendFileToWsByFrames("/config.json", "config", "", num, WEB_SOCKETS_FRAME_SIZE); sendFileToWsByFrames("/items.json", "itemsj", "", num, WEB_SOCKETS_FRAME_SIZE); - // sendFileToWsByFrames("/layout.json", "layout", "", num, WEB_SOCKETS_FRAME_SIZE); + // sendFileToWsByFrames("/layout.json", "layout", "", num, + // WEB_SOCKETS_FRAME_SIZE); } - if (headerStr == "/test|") { + //----------------------------------------------------------------------// + // Страница веб интерфейса update + //----------------------------------------------------------------------// + if (headerStr == "/profile|") { + // для версии 451 отдаем myProfile.json + sendFileToWsByFrames("/ota.json", "otaupd", "", num, WEB_SOCKETS_FRAME_SIZE); + if (FileFS.exists("/myProfile.json")) { + sendFileToWsByFrames("/myProfile.json", "prfile", "", num, WEB_SOCKETS_FRAME_SIZE); + // для версии 452 и более отдаем flashProfile.json + } else if (FileFS.exists("/flashProfile.json")) { + sendFileToWsByFrames("/flashProfile.json", "prfile", "", num, WEB_SOCKETS_FRAME_SIZE); + } } //----------------------------------------------------------------------// @@ -212,7 +249,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) // команда обновления прошивки esp if (headerStr == "/update|") { - upgrade_firmware(3); + String path; + writeUint8tToString(payload, length, headerLenth, path); + upgrade_firmware(3, path); } // Прием команд control c dashboard @@ -286,7 +325,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) } } -// публикация статус сообщений в ws (недостаток в том что делаем бродкаст всем клиентам поднятым в свелте!!!) +// публикация статус сообщений в ws (недостаток в том что делаем бродкаст всем +// клиентам поднятым в свелте!!!) void publishStatusWs(const String& topic, const String& data) { String path = mqttRootDevice + "/" + topic; String json = "{}"; @@ -307,7 +347,10 @@ void publishJsonWs(const String& topic, String& json) { void periodicWsSend() { sendStringToWs("ssidli", ssidListHeapJson, -1); sendStringToWs("errors", errorsHeapJson, -1); - sendStringToWs("devlis", devListHeapJson, -1); + // отправляем переодичестки только в авто режиме + if (jsonReadInt(settingsFlashJson, F("udps")) != 0) { + sendStringToWs("devlis", devListHeapJson, -1); + } } #ifdef ESP32 @@ -340,7 +383,8 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St } size_t totalSize = file.size(); - // Serial.println("Send file '" + String(filename) + "', file size: " + String(totalSize)); + // Serial.println("Send file '" + String(filename) + "', file size: " + + // String(totalSize)); char buf[32]; sprintf(buf, "%04d", json.length() + 12); @@ -380,7 +424,9 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St continuation = true; } - // Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz: " + String(size) + " fin: " + String(fin) + " cnt: " + String(continuation)); + // Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz: + // " + String(size) + " fin: " + String(fin) + " cnt: " + + // String(continuation)); if (client_id == -1) { standWebSocket.broadcastBIN(frameBuf, size, fin, continuation); @@ -397,6 +443,12 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St } void sendStringToWs(const String& header, String& payload, int client_id) { + if ((!getNumAPClients() && !isNetworkActive()) || !getNumWSClients()) { + // standWebSocket.disconnect(); // это и ниже надо сделать при - + // standWebSocket.close(); // - отключении AP И WiFi(STA), надо менять ядро WiFi. Сейчас не закрывается сессия клиента при пропаже AP И WiFi(STA) + return; + } + if (header.length() != 6) { SerialPrint("E", "FS", F("wrong header size")); return; @@ -413,3 +465,17 @@ void sendStringToWs(const String& header, String& payload, int client_id) { standWebSocket.sendBIN(client_id, (uint8_t*)dataArray, totalSize); } } + +void sendDeviceList(uint8_t num) { + if (jsonReadInt(settingsFlashJson, F("udps")) != 0) { + // если включен автопоиск то отдаем список из оперативной памяти + SerialPrint("i", "FS", "heap list"); + sendStringToWs("devlis", devListHeapJson, num); + } else { + // если выключен автопоиск то отдаем список из флешь памяти + sendFileToWsByFrames("/devlist.json", "devlis", "", num, WEB_SOCKETS_FRAME_SIZE); + SerialPrint("i", "FS", "flash list"); + } +} + +int getNumWSClients() { return standWebSocket.connectedClients(false); } \ No newline at end of file diff --git a/src/classes/IoTItem.cpp b/src/classes/IoTItem.cpp index 73918f46..ee588dc2 100644 --- a/src/classes/IoTItem.cpp +++ b/src/classes/IoTItem.cpp @@ -7,14 +7,13 @@ IoTItem::IoTItem(const String& parameters) { jsonRead(parameters, F("int"), _interval, false); - if (_interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле - if (_interval > 0) _interval = _interval * 1000; // если int положителен, то считаем, что получены секунды - if (_interval < 0) _interval = _interval * -1; // если int отрицательный, то миллисекунды + setInterval(_interval); jsonRead(parameters, F("subtype"), _subtype, false); jsonRead(parameters, F("id"), _id); if (!jsonRead(parameters, F("multiply"), _multiply, false)) _multiply = 1; if (!jsonRead(parameters, F("plus"), _plus, false)) _plus = 0; if (!jsonRead(parameters, F("round"), _round, false)) _round = -1; + if (!jsonRead(parameters, F("numDigits"), _numDigits, false)) _numDigits = 1; if (!jsonRead(parameters, F("global"), _global, false)) _global = false; @@ -35,14 +34,25 @@ IoTItem::IoTItem(const String& parameters) { jsonRead(parameters, F("needSave"), _needSave, false); if (_needSave && jsonRead(valuesFlashJson, _id, valAsStr, false)) // пробуем достать из сохранения значение элемента, если указано, что нужно сохранять setValue(valAsStr, false); + + // проверяем нужно ли отслеживать значение другого элемента + String trackingID = ""; + IoTItem* item = nullptr; + if (jsonRead(parameters, F("trackingID"), trackingID, false) && (item = findIoTItem(trackingID)) != nullptr) { + _trackingValue = &(item->value); + } +} + +void IoTItem::suspendNextDoByInt(unsigned long _delay) { // 0 - force + nextMillis = millis() + _delay; } void IoTItem::loop() { if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) { - prevMillis = millis(); + unsigned long currentMillis = millis(); // _interval должен быть < 2147483647 мс (24 суток) + if (nextMillis - currentMillis > 2147483647UL /*ULONG_MAX/2*/ ) { + nextMillis = currentMillis + _interval; + // SerialPrint(F("i"), _id, "this->doByInterval"); this->doByInterval(); } } @@ -60,19 +70,19 @@ long IoTItem::getInterval() { return _interval; } bool IoTItem::isGlobal() { return _global; } void IoTItem::setValue(const String& valStr, bool genEvent) { - value.isDecimal = isDigitDotCommaStr(valStr); - + value.isDecimal = isDigitDotCommaStr(valStr); + if (value.isDecimal) { value.valD = valStr.toFloat(); - getRoundValue(); + getRoundValue(); } else { - value.valS = valStr; + value.valS = valStr; } setValue(value, genEvent); } void IoTItem::setValue(const IoTValue& Value, bool genEvent) { - value = Value; + value = Value; if (value.isDecimal) { regEvent(value.valD, "", false, genEvent); @@ -90,7 +100,7 @@ void IoTItem::sendSubWidgetsValues(String& id, String& json) { // когда событие случилось void IoTItem::regEvent(const String& value, const String& consoleInfo, bool error, bool genEvent) { - if (_needSave) { + if (_needSave) { jsonWriteStr_(valuesFlashJson, _id, value); needSaveValues = true; } @@ -119,9 +129,9 @@ String IoTItem::getRoundValue() { if (_round >= 0 && _round <= 6) { int sot = _round ? pow(10, (int)_round) : 1; value.valD = round(value.valD * sot) / sot; - + //todo: оптимизировать. Вынести расчет строки формата округления, чтоб использовать постоянно готовую char buf[15]; - sprintf(buf, ("%1." + (String)_round + "f").c_str(), value.valD); + sprintf(buf, ("%0" + (String)(_numDigits + _round) + "." + (String)_round + "f").c_str(), value.valD); value.valS = (String)buf; return value.valS; } else { @@ -131,7 +141,7 @@ String IoTItem::getRoundValue() { } void IoTItem::regEvent(float regvalue, const String& consoleInfo, bool error, bool genEvent) { - value.valD = regvalue; + value.valD = regvalue; if (_multiply) value.valD = value.valD * _multiply; if (_plus) value.valD = value.valD + _plus; @@ -175,14 +185,29 @@ void IoTItem::checkIntFromNet() { } } +String IoTItem::getMqttExterSub() { + return ""; +} + // хуки для системных событий (должны начинаться с "on") void IoTItem::onRegEvent(IoTItem* item) {} void IoTItem::onMqttRecive(String& topic, String& msg) {} void IoTItem::onMqttWsAppConnectEvent() {} void IoTItem::onModuleOrder(String& key, String& value) {} +void IoTItem::onTrackingValue(IoTItem* item) { + setValue(item->getValue(), false); +} + +bool IoTItem::isTracking(IoTItem* item) { + return &(item->value) == _trackingValue; +} // делаем доступным модулям отправку сообщений в телеграм void IoTItem::sendTelegramMsg(bool often, String msg) {} +void IoTItem::sendFoto(uint8_t *buf, uint32_t length, const String &name) {} +void IoTItem::editFoto(uint8_t *buf, uint32_t length, const String &name) {} +// для обновления экрана Nextion из телеграм +void IoTItem::uploadNextionTlgrm(String &url) {} // методы для графиков (будет упрощено) void IoTItem::publishValue() {} @@ -200,7 +225,13 @@ bool IoTItem::isStrInID(const String& str) { } void IoTItem::setInterval(long interval) { - _interval = interval; + if (interval == 0) enableDoByInt = false; // выключаем использование периодического выполнения в модуле + else { + enableDoByInt = true; + if (interval > 0) _interval = interval * 1000; // если int положителен, то считаем, что получены секунды + else if (interval < 0) _interval = interval * -1; // если int отрицательный, то миллисекунды + } + // SerialPrint(F("i"), F("IoTItem"), "setInterval: " + _interval.toString); } IoTGpio* IoTItem::getGpioDriver() { @@ -210,6 +241,14 @@ IoTGpio* IoTItem::getGpioDriver() { IoTItem* IoTItem::getRtcDriver() { return nullptr; } +/* +IoTItem* IoTItem::getCAMDriver() { + return nullptr; +} +*/ +IoTItem* IoTItem::getTlgrmDriver() { + return nullptr; +} unsigned long IoTItem::getRtcUnixTime() { return 0; diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index b1c7b988..6aa26e77 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -341,7 +341,8 @@ enum SysOp { sysop_getRSSI, sysop_getIP, sysop_mqttPub, - sysop_getUptime + sysop_getUptime, + sysop_mqttIsConnect }; IoTValue sysExecute(SysOp command, std::vector ¶m) { @@ -442,6 +443,9 @@ IoTValue sysExecute(SysOp command, std::vector ¶m) { value.valS = jsonReadStr(errorsHeapJson, F("upt")); value.isDecimal = false; break; + case sysop_mqttIsConnect: + value.valD = mqttIsConnect(); + break; } return value; @@ -496,6 +500,8 @@ class SysCallExprAST : public ExprAST { operation = sysop_getTime; else if (Callee == F("getUptime")) operation = sysop_getUptime; + else if (Callee == F("mqttIsConnect")) + operation = sysop_mqttIsConnect; else operation = sysop_notfound; } diff --git a/src/modules/API.cpp b/src/modules/API.cpp index e4f040c1..af5a8dd8 100644 --- a/src/modules/API.cpp +++ b/src/modules/API.cpp @@ -3,9 +3,9 @@ void* getAPI_Cron(String subtype, String params); void* getAPI_Loging(String subtype, String params); void* getAPI_LogingDaily(String subtype, String params); +void* getAPI_owmWeather(String subtype, String params); void* getAPI_Timer(String subtype, String params); void* getAPI_Variable(String subtype, String params); -void* getAPI_VariableColor(String subtype, String params); void* getAPI_VButton(String subtype, String params); void* getAPI_A02Distance(String subtype, String params); void* getAPI_Acs712(String subtype, String params); @@ -35,15 +35,17 @@ void* getAPI_Pcf8574(String subtype, String params); void* getAPI_Pwm8266(String subtype, String params); void* getAPI_TelegramLT(String subtype, String params); void* getAPI_Lcd2004(String subtype, String params); +void* getAPI_Oled64(String subtype, String params); +void* getAPI_TM16XX(String subtype, String params); void* getAPI(String subtype, String params) { void* tmpAPI; if ((tmpAPI = getAPI_Cron(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Loging(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_LogingDaily(subtype, params)) != nullptr) return tmpAPI; +if ((tmpAPI = getAPI_owmWeather(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_VariableColor(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_VButton(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_A02Distance(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Acs712(subtype, params)) != nullptr) return tmpAPI; @@ -73,5 +75,7 @@ if ((tmpAPI = getAPI_Pcf8574(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Pwm8266(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_TelegramLT(subtype, params)) != nullptr) return tmpAPI; if ((tmpAPI = getAPI_Lcd2004(subtype, params)) != nullptr) return tmpAPI; +if ((tmpAPI = getAPI_Oled64(subtype, params)) != nullptr) return tmpAPI; +if ((tmpAPI = getAPI_TM16XX(subtype, params)) != nullptr) return tmpAPI; return nullptr; } \ No newline at end of file diff --git a/src/modules/display/DwinI/modinfo.json b/src/modules/display/DwinI/modinfo.json index 116fda53..5f89a4c1 100644 --- a/src/modules/display/DwinI/modinfo.json +++ b/src/modules/display/DwinI/modinfo.json @@ -1,23 +1,22 @@ -{ - "menuSection": "Экраны", - - "configItem": [{ - "global": 0, - "name": "LCD Dwin экран", - "type": "Reading", - "subtype": "DwinI", - "id": "dwin", - "widget": "", - "page": "", - "descr": "", - - "int": 15, - "TX": 17, - "RX": 16, - "line": 2, - "speed": 115200 - }], - +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "LCD Dwin экран", + "type": "Reading", + "subtype": "DwinI", + "id": "dwin", + "widget": "", + "page": "", + "descr": "", + "int": 15, + "TX": 17, + "RX": 16, + "line": 2, + "speed": 115200 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -33,6 +32,7 @@ "propInfo": { "int": "" }, + "title": "Экраны от компании Dwin", "funcInfo": [ { "name": "rrrr", @@ -41,21 +41,15 @@ } ] }, - "defActive": false, - "usedLibs": { - "esp32_4mb": [ - ], - "esp8266_4mb": [ - ], - "esp8266_1mb": [ - ], - "esp8266_1mb_ota": [ - ], - "esp8285_1mb": [ - ], - "esp8285_1mb_ota": [ - ] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [] } } \ No newline at end of file diff --git a/src/modules/display/Lcd2004/Lcd2004.cpp b/src/modules/display/Lcd2004/Lcd2004.cpp index 0caf4ebe..4580c49c 100644 --- a/src/modules/display/Lcd2004/Lcd2004.cpp +++ b/src/modules/display/Lcd2004/Lcd2004.cpp @@ -1,26 +1,18 @@ #include "Global.h" #include "classes/IoTItem.h" - -//#include "LiquidCrystal_I2C.h" #include -#include - -void scanI2C(); - -//LiquidCrystal_I2C *LCDI2C; -RobotClass_LiquidCrystal_I2C *LCDI2C; +RobotClass_LiquidCrystal_I2C* LCDI2C; class Lcd2004 : public IoTItem { private: unsigned int _x; unsigned int _y; - String _id2show; - String _descr; + String _id2show, _prefix = "", _postfix = ""; int _prevStrSize; String _addr; - bool _isShow = true; // экран показывает + bool _isShow = true; // экран показывает public: Lcd2004(String parameters) : IoTItem(parameters) { @@ -34,45 +26,58 @@ class Lcd2004 : public IoTItem { } jsonRead(parameters, "size", size); - int w = selectFromMarkerToMarker(size, ",", 0).toInt(); //количество столбцов - int h = selectFromMarkerToMarker(size, ",", 1).toInt(); //количество строк - if (LCDI2C == nullptr) { //инициализации экрана еще не было - //LCDI2C = new LiquidCrystal_I2C(hexStringToUint8(_addr), w, h); + int w = selectFromMarkerToMarker(size, ",", 0).toInt(); // количество столбцов + int h = selectFromMarkerToMarker(size, ",", 1).toInt(); // количество строк + if (LCDI2C == nullptr) { // инициализации экрана еще не было LCDI2C = new RobotClass_LiquidCrystal_I2C(hexStringToUint8(_addr), w, h, CP_UTF8); if (LCDI2C != nullptr) { LCDI2C->init(); + LCDI2C->clear(); + LCDI2C->backlight(); } } - - LCDI2C->clear(); - LCDI2C->backlight(); jsonRead(parameters, "coord", xy); _x = selectFromMarkerToMarker(xy, ",", 0).toInt(); _y = selectFromMarkerToMarker(xy, ",", 1).toInt(); - jsonRead(parameters, "descr", _descr); jsonRead(parameters, "id2show", _id2show); + jsonRead(parameters, "prefix", _prefix); + jsonRead(parameters, "postfix", _postfix); } - void doByInterval() { - if (LCDI2C != nullptr) { - printBlankStr(_prevStrSize); - - String tmpStr = getItemValue(_id2show); - if (_descr != "none") tmpStr = _descr + " " + tmpStr; - LCDI2C->setCursor(_x, _y); - LCDI2C->print(tmpStr); - - //LCDI2C->print("Helloy,Manager 404 !"); - //Serial.printf("ffff %s\n", _id2show); - _prevStrSize = tmpStr.length(); - } else { + void drawItem(IoTItem* item) { + String tmpStr = _prefix; + tmpStr += item->getValue(); + tmpStr += _postfix; + + printBlankStr(_prevStrSize); + LCDI2C->setCursor(_x, _y); + LCDI2C->print(tmpStr); + _prevStrSize = tmpStr.length(); + } + + void setValue(const IoTValue& Value, bool genEvent = true) { + if (LCDI2C == nullptr) return; + + value = Value; + drawItem(this); + IoTItem::setValue(Value, genEvent); + } + + void onRegEvent(IoTItem* eventItem) { + if (LCDI2C == nullptr) { scanI2C(); + return; + } + if (!eventItem || _id2show == "") return; + + if (_id2show == eventItem->getID()) { + setValue(eventItem->value, false); } } - IoTValue execute(String command, std::vector ¶m) { // будет возможным использовать, когда сценарии запустятся + IoTValue execute(String command, std::vector& param) { if (command == "noBacklight") LCDI2C->noBacklight(); else if (command == "backlight") @@ -87,7 +92,7 @@ class Lcd2004 : public IoTItem { if (_isShow) { LCDI2C->noDisplay(); _isShow = false; - } else { + } else { LCDI2C->display(); _isShow = true; } @@ -99,9 +104,13 @@ class Lcd2004 : public IoTItem { if (param.size()) { _y = param[0].valD; } - } else if (command == "descr") { + } else if (command == "prefix") { if (param.size()) { - _descr = param[0].valS; + _prefix = param[0].valS; + } + } else if (command == "postfix") { + if (param.size()) { + _postfix = param[0].valS; } } else if (command == "id2show") { if (param.size()) { @@ -113,7 +122,7 @@ class Lcd2004 : public IoTItem { return {}; } - //печать пустой строки нужной длинны для затирания предыдущего значения на экране + // печать пустой строки нужной длинны для затирания предыдущего значения на экране void printBlankStr(int strSize) { String tmpStr = ""; for (int i = 0; i < strSize; i++) tmpStr += " "; @@ -121,13 +130,13 @@ class Lcd2004 : public IoTItem { LCDI2C->print(tmpStr); } - ~Lcd2004(){ + ~Lcd2004() { if (LCDI2C) delete LCDI2C; LCDI2C = nullptr; }; }; -void *getAPI_Lcd2004(String subtype, String param) { +void* getAPI_Lcd2004(String subtype, String param) { if (subtype == F("Lcd2004")) { return new Lcd2004(param); } else { diff --git a/src/modules/display/Lcd2004/modinfo.json b/src/modules/display/Lcd2004/modinfo.json index 024750b8..1176d45e 100644 --- a/src/modules/display/Lcd2004/modinfo.json +++ b/src/modules/display/Lcd2004/modinfo.json @@ -1,38 +1,38 @@ -{ - "menuSection": "Экраны", - - "configItem": [{ - "global": 0, - "name": "LCD экран 2004", - "type": "Reading", - "subtype": "Lcd2004", - "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - - "int": 15, - "addr": "0x27", - "size": "20,4", - "coord": "0,0", - "id2show": "id датчика" - }, - { - "name": "LCD экран 1602", - "type": "Reading", - "subtype": "Lcd2004", - "id": "Lcd", - "widget": "", - "page": "", - "descr": "T", - - "int": 15, - "addr": "0x27", - "size": "16,2", - "coord": "0,0", - "id2show": "id датчика" - }], - +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "LCD экран 2004", + "type": "Reading", + "subtype": "Lcd2004", + "id": "Lcd", + "widget": "inputTxt", + "page": "screens", + "descr": "LCD Экран", + "addr": "0x27", + "size": "20,4", + "coord": "0,0", + "id2show": "", + "prefix": "", + "postfix": "" + }, + { + "name": "LCD экран 1602", + "type": "Reading", + "subtype": "Lcd2004", + "id": "Lcd", + "widget": "inputTxt", + "page": "screens", + "descr": "LCD Экран", + "addr": "0x27", + "size": "16,2", + "coord": "0,0", + "id2show": "", + "prefix": "", + "postfix": "" + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -46,12 +46,14 @@ }, "moduleDesc": "Позволяет выводить на символьные экраны по указанным позициям значения других элементов конфигурации.", "propInfo": { - "int": "Период времени в секундах обновления информации на экране по конкретному элементу.", - "addr": "Адрес устройства на шине, обычно 0x27.", + "addr": "Адрес устройства на шине, обычно 0x27. Установите пустую строку для включения режима сканирования адресов на шине (результат в консоли).", "size": "Размерность матрицы экрана.", "coord": "Координата позиции для вывода данных элемента конфигурации.", - "id2show": "id элемента конфигурации." + "id2show": "id элемента конфигурации для отображения на экране. Если пустое значение, то данные берутся из собственной переменной.", + "prefix": "Символы до значения.", + "postfix": "Символы после значения." }, + "title": "Символьный дисплей Lcd2004", "funcInfo": [ { "name": "noBacklight", @@ -81,33 +83,54 @@ { "name": "x", "descr": "Устанавливает первую координату", - "params": ["Номер строки первого символа"] + "params": [ + "Номер строки первого символа" + ] }, { "name": "y", "descr": "Устанавливает вторую координату", - "params": ["Номер столбца первого символа"] + "params": [ + "Номер столбца первого символа" + ] }, { - "name": "descr", + "name": "prefix", "descr": "Задает приставку слева от значения", - "params": ["Строка"] + "params": [ + "Строка" + ] + }, + { + "name": "postfix", + "descr": "Задает приставку справа от значения", + "params": [ + "Строка" + ] }, { "name": "id2show", "descr": "Задает ИД элемента, значение которого хотим отображать на экране", - "params": ["Имя элемента конфигурации"] + "params": [ + "Имя элемента конфигурации" + ] } ] }, - "defActive": true, - "usedLibs": { "esp32_4mb": [ "https://github.com/robotclass/RobotClass_LiquidCrystal_I2C", "marcoschwartz/LiquidCrystal_I2C@^1.1.4" ], + "esp32_4mb3f": [ + "https://github.com/robotclass/RobotClass_LiquidCrystal_I2C", + "marcoschwartz/LiquidCrystal_I2C@^1.1.4" + ], + "esp32cam_4mb": [ + "https://github.com/robotclass/RobotClass_LiquidCrystal_I2C", + "marcoschwartz/LiquidCrystal_I2C@^1.1.4" + ], "esp8266_4mb": [ "https://github.com/robotclass/RobotClass_LiquidCrystal_I2C", "marcoschwartz/LiquidCrystal_I2C@^1.1.4" diff --git a/src/modules/display/NextionUpload/NextionUpload.cpp b/src/modules/display/NextionUpload/NextionUpload.cpp index 9239d3ee..f2db9474 100644 --- a/src/modules/display/NextionUpload/NextionUpload.cpp +++ b/src/modules/display/NextionUpload/NextionUpload.cpp @@ -4,9 +4,6 @@ #include "classes/IoTItem.h" #include "ESPNexUpload.h" bool updated = false; -// const char *host = "live-control.com"; -// const char *url = "/iotm/Live-Control.tft"; -extern IoTGpio IoTgpio; class NextionUpload : public IoTItem { @@ -15,17 +12,21 @@ private: String _host; int _NEXT_RX; int _NEXT_TX; + bool _UpTelegram; public: NextionUpload(String parameters) : IoTItem(parameters) { _url = jsonReadStr(parameters, "url"); + _url = "/" + _url; _host = jsonReadStr(parameters, "host"); _NEXT_RX = jsonReadInt(parameters, "NEXT_RX"); _NEXT_TX = jsonReadInt(parameters, "NEXT_TX"); -#define NEXT_RX _NEXT_RX // Nextion RX pin | Default 16 -#define NEXT_TX _NEXT_TX // Nextion TX pin | Default 17 + jsonRead(parameters, "UpTelegram", _UpTelegram); + +//#define NEXT_RX _NEXT_RX // Nextion RX pin | Default 16 +//#define NEXT_TX _NEXT_TX // Nextion TX pin | Default 17 } IoTValue execute(String command, std::vector ¶m) @@ -43,50 +44,23 @@ public: #if defined ESP8266 if (!http.begin(_host, 80, _url)) { -#elif defined ESP32 - if (!http.begin(String("http://") + _host + _url)) - { -#endif // Serial.println("connection failed"); SerialPrint("I", F("NextionUpdate"), "connection failed "); } +#elif defined ESP32 + if (!http.begin(String("http://") + _host + _url)) + { + // Serial.println("connection failed"); + SerialPrint("I", F("NextionUpdate"), "connection failed "); + } +#endif SerialPrint("I", F("NextionUpdate"), "Requesting file: " + (String)_url); int code = http.GET(); - int contentLength = http.getSize(); - // Update the nextion display if (code == 200) { - SerialPrint("I", F("NextionUpdate"), "File received. Update Nextion... "); - bool result; - ESPNexUpload nextion(115200); - nextion.setUpdateProgressCallback([]() - { SerialPrint("I", F("NextionUpdate"), "... "); }); - - result = nextion.prepareUpload(contentLength); - - if (!result) - { - SerialPrint("I", F("NextionUpdate"), "Error: " + (String)nextion.statusMessage); - } - else - { - SerialPrint("I", F("NextionUpdate"), "Start upload. File size is: " + (String)contentLength); - result = nextion.upload(*http.getStreamPtr()); - - if (result) - { - updated = true; - SerialPrint("I", F("NextionUpdate"), "Succesfully updated Nextion! "); - } - else - { - SerialPrint("I", F("NextionUpdate"), "Error updating Nextion: " + (String)nextion.statusMessage); - } - - nextion.end(); - } + flashNextion(http); } else { @@ -101,6 +75,75 @@ public: return {}; } + void uploadNextionTlgrm(String &url) + { + if (!_UpTelegram) + return; + if (!updated) + { + SerialPrint("I", F("NextionUpdate"), "connecting to " + url); + + HTTPClient http; + +#ifdef ESP8266 // esp8266 требует SSl + return; + // BearSSL::WiFiClientSecure client; + // client.setInsecure(); + // http.begin(client, url); // пингуем файл +#else // esp32 сама умеет SSL + if (!http.begin(url)) // пингуем файл + { + SerialPrint("I", F("NextionUpdate"), "connection failed "); + } +#endif + SerialPrint("I", F("NextionUpdate"), "Requesting file: OK" ); + int code = http.GET(); + // Update the nextion display + if (code == 200) + { // файл доступен + flashNextion(http); + } + else + { + SerialPrint("I", F("NextionUpdate"), "HTTP error: " + (String)http.errorToString(code).c_str()); + } + + http.end(); + SerialPrint("I", F("NextionUpdate"), "Closing connection "); + } + } + + void flashNextion(HTTPClient &http) + { + int contentLength = http.getSize(); + SerialPrint("I", F("NextionUpdate"), "File received. Update Nextion... "); + bool result; + ESPNexUpload nextion(115200, _NEXT_RX, _NEXT_TX); + nextion.setUpdateProgressCallback([]() + { SerialPrint("I", F("NextionUpdate"), "... "); }); + + result = nextion.prepareUpload(contentLength); + if (!result) + { + SerialPrint("I", F("NextionUpdate"), "Error: " + (String)nextion.statusMessage); + } + else + { + SerialPrint("I", F("NextionUpdate"), "Start upload. File size is: " + (String)contentLength); + result = nextion.upload(*http.getStreamPtr()); + if (result) + { + updated = true; + SerialPrint("I", F("NextionUpdate"), "Succesfully updated Nextion! "); + } + else + { + SerialPrint("I", F("NextionUpdate"), "Error updating Nextion: " + (String)nextion.statusMessage); + } + nextion.end(); + } + } + ~NextionUpload(){}; }; diff --git a/src/modules/display/NextionUpload/modinfo.json b/src/modules/display/NextionUpload/modinfo.json index a97f2343..54fdb1c7 100644 --- a/src/modules/display/NextionUpload/modinfo.json +++ b/src/modules/display/NextionUpload/modinfo.json @@ -1,6 +1,5 @@ { - "menuSection": "Экраны", - + "menuSection": "screens", "configItem": [ { "global": 0, @@ -12,19 +11,19 @@ "page": "", "descr": "", "host": "192.168.1.10", - "url": "castom_nextion.tif", + "url": "nextion.tft", "NEXT_TX": 16, - "NEXT_RX": 17 + "NEXT_RX": 17, + "UpTelegram": 1 } ], - "about": { "authorName": "AVAKS", "authorContact": "https://t.me/@avaks_dev", "authorGit": "https://github.com/avaksru", "specialThanks": "", "moduleName": "NextionUpload", - "moduleVersion": "1.0", + "moduleVersion": "1.1", "usedRam": { "esp32_4mb": 15, "esp8266_4mb": 15 @@ -32,19 +31,19 @@ "title": "Nextion Upload", "moduleDesc": "загрузка прошивки в дисплей Nextion. Команда для запуска обновления дисплея: Nextion.Update(); ", "propInfo": { - "host": "Сервер обновления", - "url": "файл прошивки" + "host": "Сервер обновления. Можно использовать LiveServer из VisualCode, указывать ip адрес", + "url": "файл прошивки экрана, указывать с расширением, например nextion.tft или iotm/test.tft", + "UpTelegram": "1 - разрешает прошивать экран через модуль Telegram_v2" } }, - "defActive": false, - "usedLibs": { - "esp32_4mb": ["https://github.com/avaksru/ESPNexUpload.git"], - "esp8266_4mb": ["https://github.com/avaksru/ESPNexUpload.git"], - "esp8266_1mb": ["https://github.com/avaksru/ESPNexUpload.git"], - "esp8266_1mb_ota": ["https://github.com/avaksru/ESPNexUpload.git"], - "esp8285_1mb": ["https://github.com/avaksru/ESPNexUpload.git"], - "esp8285_1mb_ota": ["https://github.com/avaksru/ESPNexUpload.git"] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/display/Oled128/Oled128.cpp b/src/modules/display/Oled128/Oled128.cpp new file mode 100644 index 00000000..fc25ca96 --- /dev/null +++ b/src/modules/display/Oled128/Oled128.cpp @@ -0,0 +1,174 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include + +GyverOLED oled; + +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; +// GyverOLED oled; + +class Oled128 : public IoTItem { + private: + unsigned int _x; + unsigned int _y; + + unsigned int _k; + + int _shrift; + + String _id2show; + String _descr; + String _descr1; + + int _prevStrSize; + + bool _isShow = true; // экран показывает + + public: + Oled128(String parameters) : IoTItem(parameters) { + String addr, size, xy, k; + _prevStrSize = 0; + + jsonRead(parameters, "addr", addr); + if (addr == "") { + scanI2C(); + return; + } + + jsonRead(parameters, "coord", xy); + _x = selectFromMarkerToMarker(xy, ",", 0).toInt(); + _y = selectFromMarkerToMarker(xy, ",", 1).toInt(); + + jsonRead(parameters, "descr", _descr); + jsonRead(parameters, "id2show", _id2show); + jsonRead(parameters, "descr1", _descr1); + // jsonRead(parameters, "scale", _k); + jsonRead(parameters, "shrift", _shrift); + + // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 + + oled.init(); // инициализация экрана + } + + void doByInterval() { + printBlankStr(_prevStrSize); + + String tmpStr = ""; + + // if (_descr != "none") tmpStr = _descr + " " + getItemValue(_id2show); + if (_descr != "none") + tmpStr = _descr + " " + getItemValue(_id2show) + " " + _descr1; + else + tmpStr = getItemValue(_id2show); + + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursorXY(_x, _y); + + oled.print(tmpStr); + + oled.update(); + + _prevStrSize = tmpStr.length(); + } + + IoTValue execute(String command, std::vector ¶m) { // будет возможным использовать, когда сценарии запустятся + + if (command == "scroll") { + String tmpStr = ""; + oled.clear(); + uint32_t tmr = millis(); + oled.autoPrintln(false); + int val = 128; + for (;;) { + // oled.clear(); // ЗАКОММЕНТИРУЙ, ЕСЛИ ВКЛЮЧЕН БУФЕР + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursor(val, _y); + + oled.print(tmpStr); + oled.update(); + val--; + if (millis() - tmr > 5000) + ; // return; + + _isShow = true; + } + + } + + else if (command == "stopscroll") { + _isShow = true; + // display->backlight(); + // else if (command == "noDisplay") { + // display->noDisplay(); + // _isShow = false; + } else if (command == "display") { + // display.display(); + _isShow = true; + } else if (command == "toggle") { + if (_isShow) { + // display->noDisplay(); + _isShow = false; + } else { + // display.display(); + _isShow = true; + } + } else if (command == "x") { + if (param.size()) { + _x = param[0].valD; + } + } else if (command == "y") { + if (param.size()) { + _y = param[0].valD; + } + } else if (command == "descr") { + if (param.size()) { + _descr = param[0].valS; + } + } else if (command == "descr1") { + if (param.size()) { + _descr1 = param[0].valS; + } + } else if (command == "id2show") { + if (param.size()) { + _id2show = param[0].valS; + } + } + + doByInterval(); + return {}; + } + + // печать пустой строки нужной длинны для затирания предыдущего значения на экране + void printBlankStr(int strSize) { + String tmpStr = ""; + for (int i = 0; i < strSize; i++) tmpStr += " "; + + // oled.setScale(2); + + oled.setScale(_shrift); + + oled.setCursorXY(_x, _y); + + oled.print(tmpStr); + } + + ~Oled128(){}; +}; + +void *getAPI_Oled128(String subtype, String param) { + if (subtype == F("Oled128")) { + return new Oled128(param); + } else { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/display/Oled128/modinfo.json b/src/modules/display/Oled128/modinfo.json new file mode 100644 index 00000000..5a59dc57 --- /dev/null +++ b/src/modules/display/Oled128/modinfo.json @@ -0,0 +1,89 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "name": "OLED экран 128*64", + "type": "Reading", + "subtype": "Oled128", + "id": "oled", + "widget": "", + "page": "", + "descr": "T", + "descr1": "C", + "int": 1, + "addr": "0x3C", + "coord": "0,10", + "id2show": "id датчика", + "shrift": "2" + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Ilya Belyakov @Biveraxe", + "moduleName": "Oled128", + "moduleVersion": "1.0", + "moduleDesc": "Позволяет выводить на матричные Oled экраны по указанным позициям значения других элементов конфигурации.", + "usedRam": 15, + "propInfo": { + "int": "Период времени в секундах обновления информации на экране по конкретному элементу.", + "addr": "Адрес устройства на шине, обычно 0x3c.", + "coord": "Координата позиции для вывода данных элемента конфигурации.", + "id2show": "id элемента конфигурации.", + "shrift": "Шрифт текста от 1 до 4 " + }, + "funcInfo": [ + { + "name": "x", + "descr": "Устанавливает первую координату", + "params": [ + "Номер строки первого символа" + ] + }, + { + "name": "y", + "descr": "Устанавливает вторую координату", + "params": [ + "Номер столбца первого символа" + ] + }, + { + "name": "descr", + "descr": "Задает приставку слева от значения, если none значит пусто", + "params": [ + "Строка" + ] + }, + { + "name": "descr1", + "descr": "Задает приставку справа от значения. Если descr none , то не выводится", + "params": [ + "Строка" + ] + }, + { + "name": "id2show", + "descr": "Задает ИД элемента, значение которого хотим отображать на экране", + "params": [ + "Имя элемента конфигурации" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "gyverlibs/GyverOLED @ 1.4" + ], + "esp32_4mb3f": [ + "gyverlibs/GyverOLED @ 1.4" + ], + "esp32_16mb": [ + "gyverlibs/GyverOLED @ 1.4" + ], + "esp8266_4mb": [ + "gyverlibs/GyverOLED @ 1.4" + ] + } +} \ No newline at end of file diff --git a/src/modules/display/Oled64/Oled64.cpp b/src/modules/display/Oled64/Oled64.cpp new file mode 100644 index 00000000..1a69e073 --- /dev/null +++ b/src/modules/display/Oled64/Oled64.cpp @@ -0,0 +1,134 @@ + + +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include +#include +#include + +#define OLED_RESET 0 +Adafruit_SSD1306 display(OLED_RESET); + +class Oled64 : public IoTItem { + private: + unsigned int _x; + unsigned int _y; + String _id2show, _prefix = "", _postfix = ""; + String _size = "1"; + String _addr; + + int _prevStrSize; + + bool _isShow = true; + + public: + Oled64(String parameters) : IoTItem(parameters) { + String size, xy; + _prevStrSize = 0; + + jsonRead(parameters, "addr", _addr); + if (_addr == "") { + scanI2C(); + return; + } + + display.begin(SSD1306_SWITCHCAPVCC, hexStringToUint8(_addr)); + + display.display(); + + display.clearDisplay(); + + jsonRead(parameters, "coord", xy); + _x = selectFromMarkerToMarker(xy, ",", 0).toInt(); + _y = selectFromMarkerToMarker(xy, ",", 1).toInt(); + jsonRead(parameters, "id2show", _id2show); + jsonRead(parameters, "prefix", _prefix); + jsonRead(parameters, "postfix", _postfix); + jsonRead(parameters, "size", _size); + } + + void drawItem(IoTItem *item) { + String tmpStr = _prefix; + tmpStr += item->getValue(); + tmpStr += _postfix; + + display.setRotation(0); + display.setCursor(_x, _y); + display.setTextColor(WHITE, BLACK); + display.setTextSize(_size.toInt()); + + printBlankStr(_prevStrSize); + + display.setCursor(_x, _y); + + display.print(tmpStr); + _prevStrSize = tmpStr.length(); + display.display(); + _prevStrSize = tmpStr.length(); + } + + void setValue(const IoTValue &Value, bool genEvent = true) { + value = Value; + drawItem(this); + IoTItem::setValue(Value, genEvent); + } + + void onRegEvent(IoTItem *eventItem) { + if (!eventItem || _id2show == "") return; + + if (_id2show == eventItem->getID()) { + setValue(eventItem->value, false); + } + } + + IoTValue execute(String command, std::vector ¶m) { + if (command == "display") { + _isShow = true; + } else if (command == "noDisplay") { + _isShow = false; + } else if (command == "x") { + if (param.size()) { + _x = param[0].valD; + } + } else if (command == "y") { + if (param.size()) { + _y = param[0].valD; + } + } else if (command == "prefix") { + if (param.size()) { + _prefix = param[0].valS; + } + } else if (command == "postfix") { + if (param.size()) { + _postfix = param[0].valS; + } + } else if (command == "id2show") { + if (param.size()) { + _id2show = param[0].valS; + } + } + + doByInterval(); + return {}; + } + + // печать пустой строки нужной длинны для затирания предыдущего значения на экране + void printBlankStr(int strSize) { + String tmpStr = ""; + for (int i = 0; i < strSize; i++) tmpStr += " "; + display.setCursor(_x, _y); + display.print(tmpStr); + } + + ~Oled64(){}; +}; + +void *getAPI_Oled64(String subtype, String param) { + if (subtype == F("Oled64")) { + return new Oled64(param); + } else { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/display/Oled64/modinfo.json b/src/modules/display/Oled64/modinfo.json new file mode 100644 index 00000000..39f0395a --- /dev/null +++ b/src/modules/display/Oled64/modinfo.json @@ -0,0 +1,97 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "OLED экран 64 8266", + "type": "Reading", + "subtype": "Oled64", + "id": "Oled", + "widget": "inputTxt", + "page": "screens", + "descr": "OLED Экран", + "addr": "0x3C", + "coord": "0,0", + "size": "1", + "id2show": "", + "prefix": "", + "postfix": "" + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Valentin Khandriga @Valiuhaaa", + "moduleName": "Oled64", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Модуль отображения на экранах OLED 64*48", + "moduleDesc": "Позволяет выводить на OLED экраны по указанным позициям значения других элементов конфигурации.", + "propInfo": { + "addr": "Адрес устройства на шине, обычно 0x3C.", + "coord": "Координата позиции для вывода данных элемента конфигурации.", + "id2show": "id элемента конфигурации.", + "size": "Размер шрифта. Допускается 1, 2, 3, 4" + }, + "funcInfo": [ + { + "name": "x", + "descr": "Устанавливает первую координату", + "params": [ + "Номер строки первого символа" + ] + }, + { + "name": "y", + "descr": "Устанавливает вторую координату", + "params": [ + "Номер столбца первого символа" + ] + }, + { + "name": "descr", + "descr": "Задает приставку слева от значения", + "params": [ + "Строка" + ] + }, + { + "name": "descr1", + "descr1": "Задает приставку справа от значения", + "params": [ + "Строка" + ] + }, + { + "name": "id2show", + "descr": "Задает ИД элемента, значение которого хотим отображать на экране", + "params": [ + "Имя элемента конфигурации" + ] + } + ] + }, + "defActive": true, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED", + "https://github.com/adafruit/Adafruit-GFX-Library" + ], + "esp8266_4mb": [ + "https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED", + "https://github.com/adafruit/Adafruit-GFX-Library" + ], + "esp8266_1mb": [ + "https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED", + "https://github.com/adafruit/Adafruit-GFX-Library" + ], + "esp8266_1mb_ota": [ + "https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED", + "https://github.com/adafruit/Adafruit-GFX-Library" + ] + } +} \ No newline at end of file diff --git a/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.cpp b/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.cpp new file mode 100644 index 00000000..8535ffdb --- /dev/null +++ b/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.cpp @@ -0,0 +1,150 @@ +#include "Modbus_master_for_Smi2_m.h" + +Smi_display::Smi_display() { +} + +// Modbus Master +void Smi_display::modbus_update() { + idle(); +} + +void Smi_display::idle() { + static unsigned int packet_index; + + unsigned int failed_connections = 0; + + unsigned char current_connection; + + do { + if (packet_index == total_no_of_packets) // wrap around to the beginning + packet_index = 0; + + // proceed to the next packet + packet = &packetArray[packet_index]; + + // get the current connection status + current_connection = packet->connection; + + if (!current_connection) { + // If all the connection attributes are false return + // immediately to the main sketch + if (++failed_connections == total_no_of_packets) + return; + } + packet_index++; + + // if a packet has no connection get the next one + } while (!current_connection); + + constructPacket(); +} + +void Smi_display::constructPacket() { + packet->requests++; + frame[0] = packet->id; + frame[1] = packet->function; + frame[2] = packet->address >> 8; // address Hi + frame[3] = packet->address & 0xFF; // address Lo + frame[4] = packet->data >> 8; // MSB + frame[5] = packet->data & 0xFF; // LSB + + unsigned char frameSize; + + // construct the frame according to the modbus function + if (packet->function == PRESET_MULTIPLE_REGISTERS) + frameSize = construct_F16(); + else // else functions 1,2,3,4,5 & 6 is assumed. They all share the exact same request format. + frameSize = 8; // the request is always 8 bytes in size for the above mentioned functions. + + unsigned int crc16 = calculateCRC(frameSize - 2); + frame[frameSize - 2] = crc16 >> 8; // split crc into 2 bytes + frame[frameSize - 1] = crc16 & 0xFF; + sendPacket(frameSize); +} + +unsigned char Smi_display::construct_F16() { + unsigned char no_of_bytes = packet->data * 2; + + // first 6 bytes of the array + no_of_bytes + 2 bytes CRC + frame[6] = no_of_bytes; // number of bytes + unsigned char index = 7; // user data starts at index 7 + unsigned char no_of_registers = packet->data; + /*unsigned*/ int temp; + + for (unsigned char i = 0; i < no_of_registers; i++) { + temp = register_array[packet->local_start_address + i]; // get the data + frame[index] = temp >> 8; + index++; + frame[index] = temp & 0xFF; + index++; + } + unsigned char frameSize = (9 + no_of_bytes); // first 7 bytes of the array + 2 bytes CRC + noOfBytes + return frameSize; +} + +void Smi_display::modbus_configure(HardwareSerial* SerialPort, + long baud, + unsigned char byteFormat, + int rx, + int tx, + unsigned int _TxEnablePin, + Packet* _packets, + unsigned int _total_no_of_packets, + /*unsigned*/ int* _register_array) { + TxEnablePin = _TxEnablePin; + total_no_of_packets = _total_no_of_packets; + packetArray = _packets; + register_array = _register_array; + + ModbusPort = SerialPort; + (*ModbusPort).begin(baud, byteFormat, rx, tx); + + pinMode(TxEnablePin, OUTPUT); + digitalWrite(TxEnablePin, LOW); +} + +void Smi_display::modbus_construct(Packet* _packet, + unsigned char id, + unsigned char function, + unsigned int address, + unsigned int data, + unsigned int local_start_address) { + _packet->id = id; + _packet->function = function; + _packet->address = address; + _packet->data = data; + _packet->local_start_address = local_start_address; + _packet->connection = 1; +} + +unsigned int Smi_display::calculateCRC(unsigned char bufferSize) { + unsigned int temp, temp2, flag; + temp = 0xFFFF; + for (unsigned char i = 0; i < bufferSize; i++) { + temp = temp ^ frame[i]; + for (unsigned char j = 1; j <= 8; j++) { + flag = temp & 0x0001; + temp >>= 1; + if (flag) + temp ^= 0xA001; + } + } + // Reverse byte order. + temp2 = temp >> 8; + temp = (temp << 8) | temp2; + temp &= 0xFFFF; + // the returned value is already swapped + // crcLo byte is first & crcHi byte is last + return temp; +} + +void Smi_display::sendPacket(unsigned char bufferSize) { + digitalWrite(TxEnablePin, HIGH); + + for (unsigned char i = 0; i < bufferSize; i++) + (*ModbusPort).write(frame[i]); + + (*ModbusPort).flush(); + + digitalWrite(TxEnablePin, LOW); +} \ No newline at end of file diff --git a/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.h b/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.h new file mode 100644 index 00000000..1dc2dfbc --- /dev/null +++ b/src/modules/display/Smi2_m/Modbus_master_for_Smi2_m.h @@ -0,0 +1,71 @@ +#pragma once +#include "HardwareSerial.h" + +#define PRESET_MULTIPLE_REGISTERS 16 +#define BUFFER_SIZE 64 + +typedef struct +{ + unsigned char id; + unsigned char function; + unsigned int address; + + unsigned int data; + unsigned int local_start_address; + + unsigned int requests; + unsigned int successful_requests; + unsigned int failed_requests; + unsigned int exception_errors; + unsigned int retries; + + unsigned char connection; + +} Packet; + +class Smi_display { + public: + Smi_display(); + + ~Smi_display(); + + public: + void modbus_update(); + void modbus_construct(Packet* _packet, + unsigned char id, + unsigned char function, + unsigned int address, + unsigned int data, + unsigned _local_start_address); + + void modbus_configure(HardwareSerial* SerialPort, + long baud, + unsigned char byteFormat, + int rx, + int tx, + unsigned int _TxEnablePin, + Packet* _packets, + unsigned int _total_no_of_packets, + /*unsigned*/ int* _register_array); + + private: + void idle(); + void constructPacket(); + unsigned char construct_F16(); + unsigned int calculateCRC(unsigned char bufferSize); + void sendPacket(unsigned char bufferSize); + + private: + unsigned char state; + unsigned char retry_count; + unsigned int TxEnablePin; + unsigned char frame[BUFFER_SIZE]; + unsigned char buffer; + unsigned int T1_5; + unsigned int frameDelay; + unsigned int total_no_of_packets; + Packet* packetArray; + Packet* packet; + /*unsigned*/ int* register_array; + HardwareSerial* ModbusPort; +}; diff --git a/src/modules/display/Smi2_m/Smi2_m.cpp b/src/modules/display/Smi2_m/Smi2_m.cpp new file mode 100644 index 00000000..a93572cc --- /dev/null +++ b/src/modules/display/Smi2_m/Smi2_m.cpp @@ -0,0 +1,71 @@ + +#include "Global.h" +#include "classes/IoTItem.h" + +#include "Modbus_master_for_Smi2_m.h" + +// Общая сумма доступной памяти на ведущем устройстве, чтобы хранить данные +#define TOTAL_NO_OF_REGISTERS 4 + +// This is the easiest way to create new packets +// Add as many as you want. TOTAL_NO_OF_PACKETS +// is automatically updated. +enum { + PACKET1, + TOTAL_NO_OF_PACKETS // leave this last entry +}; + +// Масив пакетов модбус +Packet packets[TOTAL_NO_OF_PACKETS]; + +// Массив хранения содержимого принятых и передающихся регистров +/*unsigned*/ int regs[TOTAL_NO_OF_REGISTERS]; + +class Smi2_m : public IoTItem { + private: + Smi_display* smi; + unsigned int _pin; // номер порта, управляющий передачей по RS-485 + long int _baud; // скорость обмена, бит/с + int _rx, _tx; // номера ножек мк, к которым подключен UART + String _show; + int i = 10; + + public: + public: + Smi2_m(String parameters) : IoTItem(parameters) { + smi = new Smi_display(); + + _pin = jsonReadInt(parameters, "pin"); + _baud = jsonReadLInt(parameters, "baud"); + _rx = jsonReadInt(parameters, "rx"); + _tx = jsonReadInt(parameters, "tx"); + // Настраиваем пакеты + // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет + // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg + // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра. + // Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра. + smi->modbus_construct(&packets[PACKET1], 1, PRESET_MULTIPLE_REGISTERS, 4200, 1, 0); + smi->modbus_configure(&Serial, _baud, SERIAL_8N1, _rx, _tx, _pin, packets, TOTAL_NO_OF_PACKETS, regs); + + jsonRead(parameters, "id2show", _show); + } + + void doByInterval() { + if (smi) { + smi->modbus_update(); + + float tmpStr = getItemValue(_show).toFloat(); + regs[0] = tmpStr * 10; + } + } + + ~Smi2_m(){}; +}; + +void* getAPI_Smi2_m(String subtype, String param) { + if (subtype == F("Smi2_m")) { + return new Smi2_m(param); + } else { + return nullptr; + } +} diff --git a/src/modules/display/Smi2_m/modinfo.json b/src/modules/display/Smi2_m/modinfo.json new file mode 100644 index 00000000..8c1debc0 --- /dev/null +++ b/src/modules/display/Smi2_m/modinfo.json @@ -0,0 +1,59 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "Smi2_m", + "type": "Writing", + "subtype": "Smi2_m", + "id": "Smi", + "widget": "", + "page": "Дисплеи", + "descr": "", + "int": 2, + "pin": 4, + "id2show": "id датчика", + "baud": "9600", + "rx": "16", + "tx": "17" + } + ], + "about": { + "moduleName": "Smi2_m", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "moduleDesc": "Позволяет вывести индикацию на семисегментный индикатор по Modbus (Работает в Slave режиме, Read Holding Registers 0х03)", + "propInfo": { + "int": "Период времени в секундах обновления информации на экране по конкретному элементу.", + "pin": "номер порта, управляющий передачей по RS-485", + "id2show": "id элемента конфигурации.", + "baud": "скорость обмена, бит/с" + }, + "title": "СМИ2-М трёхцветный Modbus-индикатор", + "funcInfo": [ + { + "name": "descr", + "descr": "Задает приставку слева от значения", + "params": [ + "Строка" + ] + }, + { + "name": "id2show", + "descr": "Задает ИД элемента, значение которого хотим отображать на экране", + "params": [ + "Имя элемента конфигурации" + ] + } + ] + }, + "defActive": true, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [] + } +} \ No newline at end of file diff --git a/src/modules/display/TM16XX/TM16XX.cpp b/src/modules/display/TM16XX/TM16XX.cpp new file mode 100644 index 00000000..7a8a1b3d --- /dev/null +++ b/src/modules/display/TM16XX/TM16XX.cpp @@ -0,0 +1,86 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include +#include + + + +class TM16XX : public IoTItem { + private: + TM16xxDisplay *_display = nullptr; + TM16xx *_module = nullptr; + std::vector _ids2show; + + public: + TM16XX(String parameters) : IoTItem(parameters) { + //jsonRead(parameters, "id2show", _id2show); + + int DIO, CLK, STB, chip, numDigits, intensity; + bool onoff; + String id2show; + jsonRead(parameters, "DIO", DIO); + jsonRead(parameters, "CLK", CLK); + jsonRead(parameters, "STB", STB); + jsonRead(parameters, "chip", chip); + jsonRead(parameters, "numDigits", numDigits); + jsonRead(parameters, "intensity", intensity); + jsonRead(parameters, "on", onoff); + + jsonRead(parameters, "id2show", id2show); + if (id2show != "") _ids2show = splitStr(id2show, ","); + + if (chip == 1637) { + _module = new TM1637(DIO, CLK, numDigits); + } else if (chip == 1638) { + _module = new TM1638(DIO, CLK, STB, numDigits); + } + _module->setupDisplay(onoff, intensity); + _display = new TM16xxDisplay(_module, numDigits); + } + + void doByInterval() { + + } + + void setValue(const IoTValue& Value, bool genEvent = true) { + if (_display == nullptr) return; + + value = Value; + _display->println(getValue()); + IoTItem::setValue(Value, genEvent); + } + + void onRegEvent(IoTItem* eventItem) { + if (_display == nullptr) return; + if (!eventItem || _ids2show.size() == 0) return; + + if (strInVector(eventItem->getID(), _ids2show)) { + if (_ids2show.size() == 1) { + _display->println(eventItem->getValue()); + } else { + _display->println(); + for (int i = 0; i < _ids2show.size(); i++) { + IoTItem* item = findIoTItem(_ids2show[i]); + if (item) { + _display->print(item->getValue()); + } + } + } + } + } + + ~TM16XX() { + delete _display; + delete _module; + }; +}; + +void *getAPI_TM16XX(String subtype, String param) { + if (subtype == F("TM16XX")) { + return new TM16XX(param); + } else { + return nullptr; + } +} diff --git a/src/modules/display/TM16XX/modinfo.json b/src/modules/display/TM16XX/modinfo.json new file mode 100644 index 00000000..a7c8c82f --- /dev/null +++ b/src/modules/display/TM16XX/modinfo.json @@ -0,0 +1,156 @@ +{ + "menuSection": "screens", + "configItem": [ + { + "global": 0, + "name": "7 сегментный дисплей TM16XX", + "type": "Writing", + "subtype": "TM16XX", + "id": "tm", + "widget": "inputTxt", + "page": "screens", + "descr": "Экран", + "round": 0, + "chip": 1637, + "numDigits": 4, + "DIO": "13", + "CLK": "14", + "STB": "12", + "intensity": "5", + "on": "1", + "id2show": "" + } + ], + "about": { + "authorName": "Ilya Belyakov", + "authorContact": "https://t.me/Biveraxe", + "authorGit": "https://github.com/biveraxe", + "specialThanks": "", + "moduleName": "TM16XX", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "moduleDesc": "Позволяет выводить на 7 сегментный экран серии TM16XX (TM1637, TM1638). Может быть расширен до поддержки TM1616, TM1620, TM1628, TM1630, TM1637, TM1638, TM1640, TM1650, TM1652 и TM1668", + "propInfo": { + "int": "Период времени в секундах обновления информации на экране по конкретному элементу.", + "chip": "Номер чипа TM1637 или TM1638", + "numDigits": "Число цифр на дисплее", + "DIO": "Номер пина данных", + "CLK": "Номер пина часового сигнала", + "intensity": "Яркость 0-7", + "on": "Вкл/выкл при старте 1/0", + "STB": "Номер пина стекового сигнала - не используется на определенных моделях", + "id2show": "id элемента конфигурации для отображения. Если пустая строка, то дисплей использует свою переменную. Если указать несколько значений через запятую, то все данные будут последовательно выводиться в строку." + }, + "funcInfo": [ + { + "name": "noBacklight", + "descr": "Выключить подсветку", + "params": [] + }, + { + "name": "backlight", + "descr": "Включить подсветку", + "params": [] + }, + { + "name": "noDisplay", + "descr": "Спрятать все данные", + "params": [] + }, + { + "name": "display", + "descr": "Показать данные на экране", + "params": [] + }, + { + "name": "toggle", + "descr": "Переключает видимость значений на экране", + "params": [] + }, + { + "name": "x", + "descr": "Устанавливает первую координату", + "params": [ + "Номер строки первого символа" + ] + }, + { + "name": "y", + "descr": "Устанавливает вторую координату", + "params": [ + "Номер столбца первого символа" + ] + }, + { + "name": "descr", + "descr": "Задает приставку слева от значения", + "params": [ + "Строка" + ] + }, + { + "name": "id2show", + "descr": "Задает ИД элемента, значение которого хотим отображать на экране", + "params": [ + "Имя элемента конфигурации" + ] + } + ] + }, + "defActive": true, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp32_4mb3f": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp32cam_4mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8266_4mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8266_1mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8266_1mb_ota": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8285_1mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8285_1mb_ota": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8266_2mb": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp8266_2mb_ota": [ + "https://github.com/maxint-rd/TM16xx", + "adafruit/Adafruit GFX Library @ ^1.11.5", + "adafruit/Adafruit BusIO @ ^1.13.2" + ] + } +} \ No newline at end of file diff --git a/src/modules/display/Ws2812b/modinfo.json b/src/modules/display/Ws2812b/modinfo.json index 6ff7dd56..bcad5b1e 100644 --- a/src/modules/display/Ws2812b/modinfo.json +++ b/src/modules/display/Ws2812b/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Экраны", + "menuSection": "screens", "configItem": [ { "global": 0, @@ -40,6 +40,7 @@ "max": "Максимальный порог индикатора на который реагировать.", "idshow": "id элемента конфигурации который нужно повесить индикацию." }, + "title": "Адресная светодиодная матрица", "funcInfo": [ { "name": "noShow", @@ -99,6 +100,12 @@ "esp32_4mb": [ "adafruit/Adafruit NeoPixel@^1.10.6" ], + "esp32_4mb3f": [ + "adafruit/Adafruit NeoPixel@^1.10.6" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit NeoPixel@^1.10.6" + ], "esp8266_4mb": [ "adafruit/Adafruit NeoPixel@^1.10.6" ] diff --git a/src/modules/exec/ButtonIn/modinfo.json b/src/modules/exec/ButtonIn/modinfo.json index 690ace99..09759edf 100644 --- a/src/modules/exec/ButtonIn/modinfo.json +++ b/src/modules/exec/ButtonIn/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -46,7 +46,12 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/exec/ButtonOut/ButtonOut.cpp b/src/modules/exec/ButtonOut/ButtonOut.cpp index e5f86172..65b8e42f 100644 --- a/src/modules/exec/ButtonOut/ButtonOut.cpp +++ b/src/modules/exec/ButtonOut/ButtonOut.cpp @@ -3,24 +3,28 @@ extern IoTGpio IoTgpio; - class ButtonOut : public IoTItem { private: - int _pin, _inv; + int _pin; + bool _inv; public: ButtonOut(String parameters): IoTItem(parameters) { jsonRead(parameters, "pin", _pin); jsonRead(parameters, "inv", _inv); _round = 0; - IoTgpio.pinMode(_pin, OUTPUT); IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); + enableDoByInt = false; } void doByInterval() { - //value.valD = IoTgpio.analogRead(_pin); - + int val = _inv?1:0; + IoTgpio.digitalWrite(_pin, val); + // SerialPrint("I", "ButtonOut","single pulse end"); + value.valD = 0; + regEvent(0, "ButtonOut"); + enableDoByInt = false; //regEvent(value.valD, "ButtonOut"); //обязательный вызов хотяб один } @@ -30,24 +34,40 @@ class ButtonOut : public IoTItem { // param - вектор ("массив") значений параметров переданных вместе с командой: ID.Команда("пар1", 22, 33) -> param[0].ValS = "пар1", param[1].ValD = 22 if (command == "change") { - value.valD = 1 - IoTgpio.digitalRead(_pin); - IoTgpio.digitalWrite(_pin, value.valD); + value.valD = 1 - (int)value.valD; + IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); regEvent(value.valD, "ButtonOut"); } - + else if (command == "pulse") { + if (param[0].isDecimal && (param[0].valD != 0)) { + value.valD = !_inv?1:0; + enableDoByInt = true; + // SerialPrint("I", "ButtonOut","single pulse start"); + regEvent((String)(int)!_inv?1:0, "ButtonOut"); + suspendNextDoByInt(param[0].valD); + IoTgpio.digitalWrite(_pin, !_inv?1:0); + } + } return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки } void setValue(const IoTValue& Value, bool genEvent = true) { value = Value; - IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); + if ((value.valD == !_inv?1:0) && (_interval != 0)) { + value.valD = !_inv?1:0; + enableDoByInt = true; + // SerialPrint("I", "ButtonOut","single pulse start"); + suspendNextDoByInt(_interval); + } else { + enableDoByInt = false; + } regEvent((String)(int)value.valD, "ButtonOut", false, genEvent); + IoTgpio.digitalWrite(_pin, _inv?!value.valD:value.valD); } String getValue() { return (String)(int)value.valD; } - //======================================================================================================= ~ButtonOut() {}; }; diff --git a/src/modules/exec/ButtonOut/modinfo.json b/src/modules/exec/ButtonOut/modinfo.json index 35e4d5c7..10a39ded 100644 --- a/src/modules/exec/ButtonOut/modinfo.json +++ b/src/modules/exec/ButtonOut/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -39,13 +39,23 @@ "name": "change", "descr": "Инвертирует значение переключателя", "params": [] + }, + { + "name": "pulse", + "descr": "Генерирует одиночный импульс", + "params": ["Длительность (ms)"] } ] }, "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/exec/Buzzer/Buzzer.cpp b/src/modules/exec/Buzzer/Buzzer.cpp index 5668b549..656b8dc9 100644 --- a/src/modules/exec/Buzzer/Buzzer.cpp +++ b/src/modules/exec/Buzzer/Buzzer.cpp @@ -79,16 +79,7 @@ public: case 1: // for doByIntervals - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } + IoTItem::loop(); break; case 2: diff --git a/src/modules/exec/Buzzer/modinfo.json b/src/modules/exec/Buzzer/modinfo.json index 35f193a2..44b01e2b 100644 --- a/src/modules/exec/Buzzer/modinfo.json +++ b/src/modules/exec/Buzzer/modinfo.json @@ -1,29 +1,29 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Пассивный звуковой извещатель", - "type": "Writing", - "subtype": "Buzzer", - "id": "buzzer", - "widget": "toggle", - "page": "Кнопки", - "descr": "Buzzer", - "int": 4000, - "pin": 14, - "freq": 2000, - "duration": 1000, - "beatLevel": 4, - "tempo": 120, - "tempoCorrection": 1, - "pauseBetween": 0, - "transpose": 0, - "cycle": 0, - "indication": 1, - "val": 0 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Пассивный звуковой извещатель", + "type": "Writing", + "subtype": "Buzzer", + "id": "buzzer", + "widget": "toggle", + "page": "Кнопки", + "descr": "Buzzer", + "int": 4000, + "pin": 14, + "freq": 2000, + "duration": 1000, + "beatLevel": 4, + "tempo": 120, + "tempoCorrection": 1, + "pauseBetween": 0, + "transpose": 0, + "cycle": 0, + "indication": 1, + "val": 0 + } + ], "about": { "authorName": "Alex K", "authorContact": "https://t.me/cmche", @@ -43,11 +43,11 @@ "int": "Количество миллисекунд между повторами одиночного сигнала", "pin": "Управляемый пин", "freq": "Частота сигнала, Hz", - "duration": "Длительность сигнала, ms", + "duration": "Длительность сигнала, ms", "beatLevel": "Долей в такте", - "tempo": "Оригинальный темп мелодии, bpm", - "tempoCorrection": "Коррекция темпа мелодии", - "pauseBetween": "Дополнительная пауза между нот, в долях от длительности ноты", + "tempo": "Оригинальный темп мелодии, bpm", + "tempoCorrection": "Коррекция темпа мелодии", + "pauseBetween": "Дополнительная пауза между нот, в долях от длительности ноты", "transpose": "Транспонирование на количество полутонов. +/-12 - для повышения/понижения на октаву", "cycle": "Повтор мелодии/серии сигналов", "indication": "Индикация в виджет, что идет сигнал, играет мелодия", @@ -57,17 +57,30 @@ { "name": "tone", "descr": "Проигрывание одиночного сигнала (без индикации)", - "params": ["Частота", "Длительность (ms)"] + "params": [ + "Частота", + "Длительность (ms)" + ] }, { "name": "tones", "descr": "Проигрывание серии сигналов, до 128", - "params": ["Частота 1-го сигнала", "Длительность 1-го сигнала (ms)","Частота 2-го сигала", "Длительность 2-го сигнала", "....итд"] + "params": [ + "Частота 1-го сигнала", + "Длительность 1-го сигнала (ms)", + "Частота 2-го сигала", + "Длительность 2-го сигнала", + "....итд" + ] }, { "name": "melody", "descr": "Проигрывание мелодии, до 256 нот. Кодировка 'YYX.ZZZ'. Научная нотация: YY - обозначение ноты (C,CS,D,DS,E,F,FS,G,GS,A,AS,B), X - номер октавы (0-9), ZZZ - длительность в тысячных долях такта (0-999). Обязательно в двойных кавычках. 'AS4.50' - Ля# 4-й октавы, 1/2 такта. На Github лежит Excel файл для перекодировки.", - "params": ["Код 1-ой ноты","Код 2-й ноты"," и тд"] + "params": [ + "Код 1-ой ноты", + "Код 2-й ноты", + " и тд" + ] }, { "name": "notone", @@ -76,21 +89,29 @@ }, { "name": "melodySetting", - "descr": "Перенастройка параметров мелодии: Долей в такте - (обычно 4), Оригинальный темп -(40-208 bpm), Коррекция темпа - в k раз быстрее/медленнее, Пауза между нот (стакато) - доля от длительности, Коррекция тональности (транспонирование) - в k раз выше/ниже, Повтор 1/0. Чтобы не изменялось значение вбить любой текст в ковычках ", - "params": ["Долей в такте", "Оригинальный темп", "Коррекция темпа", "Пауза между нот", "Коррекция тональности", "Повтор мелодии/серии сигналов"] + "descr": "Перенастройка параметров мелодии: Долей в такте - (обычно 4), Оригинальный темп -(40-208 bpm), Коррекция темпа - в k раз быстрее/медленнее, Пауза между нот (стакато) - доля от длительности, Коррекция тональности (транспонирование) - в k раз выше/ниже, Повтор 1/0. Чтобы не изменялось значение вбить любой текст в ковычках ", + "params": [ + "Долей в такте", + "Оригинальный темп", + "Коррекция темпа", + "Пауза между нот", + "Коррекция тональности", + "Повтор мелодии/серии сигналов" + ] } - ] }, - "defActive": true, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], - "esp8285_1mb_ota": [] + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/exec/Enconder/modinfo.json b/src/modules/exec/Enconder/modinfo.json index ce92fb01..5019d20e 100644 --- a/src/modules/exec/Enconder/modinfo.json +++ b/src/modules/exec/Enconder/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -12,8 +12,7 @@ "descr": "Громкость", "needSave": 0, "val": "0", - "round" : 0, - + "round": 0, "step": 1, "stepOnPress": 5, "pins": "4,5,2" @@ -37,7 +36,7 @@ "moduleDesc": "модуль для работы с Энкодером. Кнопочный вариант совместим с модулями Multitouch и ButtonIn", "retInfo": "Значение счетчика", "propInfo": { - "step" : "Размер шага Энкодера, может принимать значение 0.0001 или 1000", + "step": "Размер шага Энкодера, может принимать значение 0.0001 или 1000", "stepOnPress": "Размер шага Энкодера при нажатой кнопке, 0 - отключает учет", "pins": "Подключеные пины (CLK, DT, SW)" } @@ -47,9 +46,18 @@ "esp32_4mb": [ "gyverlibs/EncButton @ ^2.0" ], + "esp32_4mb3f": [ + "gyverlibs/EncButton @ ^2.0" + ], + "esp32cam_4mb": [ + "gyverlibs/EncButton @ ^2.0" + ], "esp8266_4mb": [ "gyverlibs/EncButton @ ^2.0" ], + "esp8266_16mb": [ + "gyverlibs/EncButton @ ^2.0" + ], "esp8266_1mb": [ "gyverlibs/EncButton @ ^2.0" ], diff --git a/src/modules/exec/EspCam/EspCam.cpp b/src/modules/exec/EspCam/EspCam.cpp index 59cd07fd..12a63ea6 100644 --- a/src/modules/exec/EspCam/EspCam.cpp +++ b/src/modules/exec/EspCam/EspCam.cpp @@ -3,64 +3,108 @@ #include "NTP.h" #include "esp_camera.h" -#include "soc/soc.h" // Disable brownour problems -#include "soc/rtc_cntl_reg.h" // Disable brownour problems +#include "soc/soc.h" // Disable brownour problems +#include "soc/rtc_cntl_reg.h" // Disable brownour problems -#include "FS.h" // SD Card ESP32 -#include "SD_MMC.h" // SD Card ESP32 +#include "FS.h" // SD Card ESP32 +#include "SD_MMC.h" // SD Card ESP32 void handleGetPic(); // =================== // Select camera model // =================== -//#define CAMERA_MODEL_WROVER_KIT // Has PSRAM -//#define CAMERA_MODEL_ESP_EYE // Has PSRAM -//#define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM -//#define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM -//#define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM -//#define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM -//#define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM -//#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM +// #define CAMERA_MODEL_WROVER_KIT // Has PSRAM +// #define CAMERA_MODEL_ESP_EYE // Has PSRAM +// #define CAMERA_MODEL_ESP32S3_EYE // Has PSRAM +// #define CAMERA_MODEL_M5STACK_PSRAM // Has PSRAM +// #define CAMERA_MODEL_M5STACK_V2_PSRAM // M5Camera version B Has PSRAM +// #define CAMERA_MODEL_M5STACK_WIDE // Has PSRAM +// #define CAMERA_MODEL_M5STACK_ESP32CAM // No PSRAM +// #define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM #define CAMERA_MODEL_AI_THINKER // Has PSRAM -//#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM -//#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM -// ** Espressif Internal Boards ** -//#define CAMERA_MODEL_ESP32_CAM_BOARD -//#define CAMERA_MODEL_ESP32S2_CAM_BOARD -//#define CAMERA_MODEL_ESP32S3_CAM_LCD +// #define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM +// #define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM +// ** Espressif Internal Boards ** +// #define CAMERA_MODEL_ESP32_CAM_BOARD +// #define CAMERA_MODEL_ESP32S2_CAM_BOARD +// #define CAMERA_MODEL_ESP32S3_CAM_LCD - -#define LED_LEDC_CHANNEL 2 //Using different ledc channel/timer than camera +#define LED_LEDC_CHANNEL 2 // Using different ledc channel/timer than camera #define CONFIG_LED_MAX_INTENSITY 255 #include "camera_pins.h" - -IoTItem* globalItem = nullptr; - - -class EspCam : public IoTItem { - private: + IoTItem *_camItem = nullptr; +camera_fb_t *frame = NULL; +class EspCam : public IoTItem +{ +private: bool _useLed, _ticker, _webTicker, _initSD; - public: +public: bool isUsedLed() { return _useLed; } bool isWebTicker() { return _webTicker; } - EspCam(String parameters): IoTItem(parameters) { - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector + EspCam(String parameters) : IoTItem(parameters) + { - jsonRead(parameters, "useLed", _useLed); // используем = 1 или нет = 0 подсветку (вспышку) - if (_useLed) { + jsonRead(parameters, "ticker", _ticker); // тикать = 1 - сообщаем всем, что сделали снимок и он готов + jsonRead(parameters, "webTicker", _webTicker); // сообщать всем, что через веб попросили отдать картинку с камеры + jsonRead(parameters, "flashOn", _useLed); // используем = 1 или нет = 0 подсветку (вспышку) + // globalItem = this; // выносим адрес переменной экземпляра для доступа к данным из обработчика событий веб + _camItem = this; + initCam(); + + HTTP.on("/getpic", HTTP_GET, handleGetPic); + + initSD(); + } + + void doByInterval() + { + // save_picture(); + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "save") + { + if (param.size() == 1) + save_picture(param[0].valS); + else + save_picture(); + } + else if (command == "flashOn") + { ledcSetup(LED_LEDC_CHANNEL, 5000, 8); ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL); + _useLed = true; + } + else if (command == "flashOff") + { + _useLed = false; + } + else if (command == "sendFoto") + { + sendFoto(); + } + else if (command == "editFoto") + { + editFoto(); } - jsonRead(parameters, "ticker", _ticker); // тикать = 1 - сообщаем всем, что сделали снимок и он готов - jsonRead(parameters, "webTicker", _webTicker); // сообщать всем, что через веб попросили отдать картинку с камеры - globalItem = this; // выносим адрес переменной экземпляра для доступа к данным из обработчика событий веб - + return {}; + } + /* + IoTItem *getCAMDriver() + { + return this; + } +*/ + void initCam() + { + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; @@ -83,110 +127,141 @@ class EspCam : public IoTItem { config.xclk_freq_hz = 20000000; config.frame_size = FRAMESIZE_UXGA; config.pixel_format = PIXFORMAT_JPEG; // for streaming - //config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition + // config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; config.fb_location = CAMERA_FB_IN_PSRAM; config.jpeg_quality = 12; config.fb_count = 1; - + // if PSRAM IC present, init with UXGA resolution and higher JPEG quality // for larger pre-allocated frame buffer. - if(psramFound()){ + if (psramFound()) + { config.jpeg_quality = 10; config.fb_count = 2; config.grab_mode = CAMERA_GRAB_LATEST; - } else { + } + else + { // Limit the frame size when PSRAM is not available config.frame_size = FRAMESIZE_SVGA; config.fb_location = CAMERA_FB_IN_DRAM; } - - #if defined(CAMERA_MODEL_ESP_EYE) + +#if defined(CAMERA_MODEL_ESP_EYE) pinMode(13, INPUT_PULLUP); pinMode(14, INPUT_PULLUP); - #endif +#endif // camera init esp_err_t err = esp_camera_init(&config); - if (err != ESP_OK) { + if (err != ESP_OK) + { Serial.printf("Camera init failed with error 0x%x", err); return; } - sensor_t * s = esp_camera_sensor_get(); + sensor_t *s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated - if (s->id.PID == OV3660_PID) { - s->set_vflip(s, 1); // flip it back - s->set_brightness(s, 1); // up the brightness just a bit + if (s->id.PID == OV3660_PID) + { + s->set_vflip(s, 1); // flip it back + s->set_brightness(s, 1); // up the brightness just a bit s->set_saturation(s, -2); // lower the saturation } - #if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM) + if (_useLed) + { + ledcSetup(LED_LEDC_CHANNEL, 5000, 8); + ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL); + } + +#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM) s->set_vflip(s, 1); s->set_hmirror(s, 1); - #endif +#endif - #if defined(CAMERA_MODEL_ESP32S3_EYE) +#if defined(CAMERA_MODEL_ESP32S3_EYE) s->set_vflip(s, 1); - #endif - - HTTP.on("/getpic", HTTP_GET, handleGetPic); +#endif + } + void initSD() + { // Start Micro SD card _initSD = true; Serial.println("Starting SD Card"); - if(!SD_MMC.begin("/sdcard", true)){ + if (!SD_MMC.begin("/sdcard", true)) + { Serial.println("SD Card Mount Failed"); _initSD = false; return; } - + uint8_t cardType = SD_MMC.cardType(); - if(cardType == CARD_NONE){ + if (cardType == CARD_NONE) + { Serial.println("No SD Card attached"); _initSD = false; return; } - + fs::FS &fs = SD_MMC; - fs.mkdir("/photos/"); + fs.mkdir("/photos"); } - void save_picture(String path = "") { - // if (_useLed) digitalWrite(4, HIGH); //Turn on the flash + void sendFoto() + { + if (tlgrmItem) + { + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash + frame = esp_camera_fb_get(); + if (!frame) + { + SerialPrint("E", F("Esp-Cam to Telegram"), "Fail esp-cam initialization"); + return; + } + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, 0); + (tlgrmItem)->sendFoto((byte *)frame->buf, frame->len, "photo.jpg"); + esp_camera_fb_return(frame); + SerialPrint("i", F("Esp-Cam to Telegram"), "esp_CAM send foto"); + } + } - // // Take Picture with Camera - // fb = esp_camera_fb_get(); - // if(!fb || fb->len >= PICBUF_SIZE) { - // if (fb) { - // Serial.printf("Camera capture failed size=%d\n", fb->len); - // esp_camera_fb_return(fb); - // } else Serial.printf("Camera capture failed\n"); - // return; - // } - - // // if (value.extBinInfoSize < fb->len) { - // // if (value.extBinInfo) free(value.extBinInfo); - // // value.extBinInfo = (uint8_t*)malloc(sizeof(uint8_t) * fb->len); - // // } - // memcpy(value.extBinInfo, fb->buf, fb->len); - // value.extBinInfoSize = fb->len; - - // Serial.printf("try send pic by size=%d", fb->len); - - // if (_useLed) digitalWrite(4, LOW); - // if (_ticker) regEvent("shot", "EspCam"); - // esp_camera_fb_return(fb); + void editFoto() + { + if (tlgrmItem) + { + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash + frame = esp_camera_fb_get(); + if (!frame) + { + SerialPrint("E", F("Esp-Cam to Telegram"), "Fail esp-cam initialization"); + return; + } + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, 0); + (tlgrmItem)->editFoto((byte *)frame->buf, frame->len, "photo.jpg"); + esp_camera_fb_return(frame); + SerialPrint("i", F("Esp-Cam to Telegram"), "esp_CAM edit foto"); + } + } + void save_picture(String path = "") + { // Save picture to microSD card fs::FS &fs = SD_MMC; - if (path == "") { + if (path == "") + { path = "/photos/"; - path += getTodayDateDotFormated(); - path += "/"; + path += getTodayDateDotFormated(); + // path += "/"; fs.mkdir(path.c_str()); char buf[32]; @@ -196,79 +271,78 @@ class EspCam : public IoTItem { } Serial.println(path); - // Take Picture with Camera - camera_fb_t * fb = esp_camera_fb_get(); + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash - if(!fb) { + // Take Picture with Camera + frame = esp_camera_fb_get(); + if (_useLed) + ledcWrite(LED_LEDC_CHANNEL, 0); // Turn on the flash + + if (!frame) + { Serial.println("Camera capture failed"); return; } - + File file = fs.open(path.c_str(), FILE_WRITE); - if(!file){ + if (!file) + { Serial.println("Failed to open file in writing mode"); - } - else { - file.write(fb->buf, fb->len); // payload (image), payload length + } + else + { + file.write(frame->buf, frame->len); // payload (image), payload length Serial.printf("Saved file to path: %s\n", path.c_str()); } file.close(); - - //return the frame buffer back to the driver for reuse - esp_camera_fb_return(fb); + + // return the frame buffer back to the driver for reuse + esp_camera_fb_return(frame); } - void doByInterval() { - //save_picture(); - } - - IoTValue execute(String command, std::vector ¶m) { - if (command == "save") { - if (param.size() == 1) - save_picture(param[0].valS); - else - save_picture(); - } else if (command == "ledOn" && param.size() == 1) { - ledcSetup(LED_LEDC_CHANNEL, 5000, 8); - ledcAttachPin(LED_GPIO_NUM, LED_LEDC_CHANNEL); - ledcWrite(LED_LEDC_CHANNEL, param[0].valD); - } else if (command == "ledOff") { - ledcWrite(LED_LEDC_CHANNEL, 0); - } - - return {}; - } - - ~EspCam() { - //free(value.extBinInfo); - globalItem = nullptr; + ~EspCam() + { + // free(value.extBinInfo); + _camItem = nullptr; }; }; -void handleGetPic() { - if (!globalItem) return; +void handleGetPic() +{ + if (!_camItem) + return; - if (((EspCam*)globalItem)->isUsedLed()) ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); //Turn on the flash + if (((EspCam *)_camItem)->isUsedLed()) + ledcWrite(LED_LEDC_CHANNEL, CONFIG_LED_MAX_INTENSITY); // Turn on the flash - camera_fb_t* fb = NULL; - fb = esp_camera_fb_get(); - if (!fb) { + // camera_fb_t *fb = NULL; + frame = esp_camera_fb_get(); + + if (((EspCam *)_camItem)->isUsedLed()) + ledcWrite(LED_LEDC_CHANNEL, 0); + + if (!frame) + { HTTP.send(200, "text/json", F("Item EspCam not prepared yet or camera hasn't taken a picture yet")); return; } - - HTTP.send_P(200, "image/jpeg", (char *)fb->buf, fb->len); - if (((EspCam*)globalItem)->isUsedLed()) ledcWrite(LED_LEDC_CHANNEL, 0); - if (((EspCam*)globalItem)->isWebTicker()) globalItem->regEvent("webTakesPhoto", "EspCam"); - esp_camera_fb_return(fb); + HTTP.send_P(200, "image/jpeg", (char *)frame->buf, frame->len); + + if (((EspCam *)_camItem)->isWebTicker()) + _camItem->regEvent("webTakesPhoto", "EspCam"); + esp_camera_fb_return(frame); } - -void* getAPI_EspCam(String subtype, String param) { - if (subtype == F("EspCam")) { +void *getAPI_EspCam(String subtype, String param) +{ + if (subtype == F("EspCam")) + { return new EspCam(param); - } else { + } + else + { return nullptr; } } diff --git a/src/modules/exec/EspCam/modinfo.json b/src/modules/exec/EspCam/modinfo.json index 429beddf..27984669 100644 --- a/src/modules/exec/EspCam/modinfo.json +++ b/src/modules/exec/EspCam/modinfo.json @@ -1,38 +1,37 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Camera OV2640 (ESPcam)", - "type": "Reading", - "subtype": "EspCam", - "id": "EspCam", - "widget": "", - "page": "", - "descr": "", - - "int": 60, - "useLed": 0, - "ticker": 0, - "webTicker": 0 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Camera OV2640 (ESPcam)", + "type": "Reading", + "subtype": "EspCam", + "id": "EspCam", + "widget": "", + "page": "", + "descr": "", + "int": 60, + "flashOn": 0, + "ticker": 0, + "webTicker": 0 + } + ], "about": { - "authorName": "Ilya Belyakov", + "authorName": "Ilya Belyakov, Mikhail Bubnov", "authorContact": "https://t.me/Biveraxe", "authorGit": "https://github.com/biveraxe", "specialThanks": "", "moduleName": "EspCam", - "moduleVersion": "2.1", + "moduleVersion": "3.0", "usedRam": { "esp32_4mb": 15, "esp8266_4mb": 15 }, "title": "Camera OV2640 (ESPcam)", - "moduleDesc": "Предназначен для специальной платы esp32 со встроенной камерой. Добавляет в прошивку функцию создания фото и сохранения на SD при наличии. По адресу /getpic можно получить текущее фото (работает в том числе без SD карты).", + "moduleDesc": "Предназначен для специальной платы esp32 со встроенной камерой. Добавляет в прошивку функцию создания фото и сохранения на SD при наличии. По адресу /getpic можно получить текущее фото (работает в том числе без SD карты). Отправка фото в телеграмм через модуль Telegram_v2", "propInfo": { "int": "Пауза в секундах во время постоянной фотосъемки.", - "useLed": "использовать диод подсветки при съемке.", + "flashOn": "использовать диод подсветки при съемке. используем = 1 или нет = 0 подсветку (вспышку)", "ticker": "Генерировать(1) или нет(0) событие с интервалом int", "webTicker": "Генерировать(1) или нет(0) событие при обращении через веб-страницу по адресу /getpic." }, @@ -42,23 +41,31 @@ "descr": "Сохранить снимок на SD", "params": [] }, + { + "name": "sendFoto", + "descr": "Отправить фото с esp-CAM в телеграмм", + "params": [""] + }, + { + "name": "editFoto", + "descr": "Отредактировать последнее отправленное фото в телеграмм", + "params": [""] + }, { - "name": "ledOn", - "descr": "Включить подсветку", - "params": ["Яркость 0-255"] + "name": "flashOn", + "descr": "Включить вспышку", + "params": [] }, { - "name": "ledOff", - "descr": "Отключить подсветку", + "name": "flashOff", + "descr": "Отключить вспышку", "params": [] } ] }, - "defActive": false, - "usedLibs": { - "esp32_4mb": [ + "esp32cam_4mb": [ "espressif/esp32-camera @ ^2.0.0" ] } diff --git a/src/modules/exec/Ftp/Ftp.cpp b/src/modules/exec/Ftp/Ftp.cpp new file mode 100644 index 00000000..fb0cdc57 --- /dev/null +++ b/src/modules/exec/Ftp/Ftp.cpp @@ -0,0 +1,97 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include + +#define DEFAULT_STORAGE_TYPE_ESP32 STORAGE_LITTLEFS + +class FTPModule : public IoTItem +{ +private: + String login; + String pass; + FtpServer ftpSrv; // set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial +public: + FTPModule(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, F("login"), login); + jsonRead(parameters, F("pass"), pass); + ftpSrv.setCallback(FTPModule::_callback); + ftpSrv.setTransferCallback(FTPModule::_transferCallback); + ftpSrv.begin(login.c_str(), pass.c_str(), "Welcome IoTManager FTP server"); // username, password for ftp. (default 21, 50009 for PASV) + SerialPrint("I", "FtpServer " + (String)_id, "begin"); + } + + void loop() + { + ftpSrv.handleFTP(); + } + + static void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace) + { + switch (ftpOperation) + { + case FTP_CONNECT: + SerialPrint("i", "FTP", F("Connected!")); + + break; + case FTP_DISCONNECT: + SerialPrint("i", "FTP", F("Disconnected!")); + break; + case FTP_FREE_SPACE_CHANGE: + SerialPrint("i", "FTP", "Free space change, free " + (String)freeSpace + " of " + (String)totalSpace); + break; + default: + break; + } + } + + static void _transferCallback(FtpTransferOperation ftpOperation, const char *name, unsigned int transferredSize) + { + switch (ftpOperation) + { + case FTP_UPLOAD_START: + SerialPrint("i","FTP", F("Upload start!")); + break; + case FTP_UPLOAD: + SerialPrint("i","FTP", "Upload of file " + (String)name + " byte " + (String)transferredSize); + break; + case FTP_TRANSFER_STOP: + SerialPrint("i","FTP", F("Finish transfer!")); + break; + case FTP_TRANSFER_ERROR: + SerialPrint("E","FTP", F("Transfer error!")); + break; + default: + break; + } + + /* FTP_UPLOAD_START = 0, + * FTP_UPLOAD = 1, + * + * FTP_DOWNLOAD_START = 2, + * FTP_DOWNLOAD = 3, + * + * FTP_TRANSFER_STOP = 4, + * FTP_DOWNLOAD_STOP = 4, + * FTP_UPLOAD_STOP = 4, + * + * FTP_TRANSFER_ERROR = 5, + * FTP_DOWNLOAD_ERROR = 5, + * FTP_UPLOAD_ERROR = 5 + */ + } + + ~FTPModule(){}; +}; + +void *getAPI_FTPModule(String subtype, String param) +{ + if (subtype == F("ftp")) + { + return new FTPModule(param); + } + //} + + return nullptr; +} diff --git a/src/modules/exec/Ftp/modinfo.json b/src/modules/exec/Ftp/modinfo.json new file mode 100644 index 00000000..a4049ee5 --- /dev/null +++ b/src/modules/exec/Ftp/modinfo.json @@ -0,0 +1,45 @@ +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "FTP сервер", + "type": "Reading", + "subtype": "ftp", + "id": "ftp", + "widget": "nil", + "page": "", + "descr": "FTP сервер", + "login": "admin", + "pass": "admin" + } + ], + "about": { + "authorName": "Bubnov Mikhail", + "authorContact": "https://t.me/Mit4bmw", + "authorGit": "https://github.com/Mit4el", + "specialThanks": "", + "moduleName": "FTPModule", + "moduleVersion": "0.1", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "FTP-сервер", + "moduleDesc": "Запускает FTP-сервер на плате esp", + "propInfo": { + "login": "Логин FTP сервера", + "pass": "Пароль FTP сервера" + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp8266_4mb": [], + "esp8266_16mb": [] + } +} \ No newline at end of file diff --git a/src/modules/exec/HttpGet/HttpGet.cpp b/src/modules/exec/HttpGet/HttpGet.cpp index 8c53c10c..269cedd5 100644 --- a/src/modules/exec/HttpGet/HttpGet.cpp +++ b/src/modules/exec/HttpGet/HttpGet.cpp @@ -1,18 +1,12 @@ #include "Global.h" #include "classes/IoTItem.h" -class HttpGet : public IoTItem -{ -public: - HttpGet(String parameters) : IoTItem(parameters) - { - } - - void sendHttpPOST(String url, String msg) - { - if (WiFi.status() == WL_CONNECTED) - { +class HttpGet : public IoTItem { + public: + HttpGet(String parameters) : IoTItem(parameters) {} + void sendHttpPOST(String url, String msg) { + if (isNetworkActive()) { WiFiClient client; HTTPClient http; http.begin(client, url); @@ -23,26 +17,23 @@ public: SerialPrint("<-", F("HttpPOST"), "URL: " + url + ", msg: " + msg); SerialPrint("->", F("HttpPOST"), "URL: " + url + ", server: " + httpResponseCode); - if (httpResponseCode > 0) - { + if (httpResponseCode > 0) { value.valS = payload; + value.isDecimal = false; SerialPrint("->", F("HttpPOST"), "msg from server: " + (String)payload.c_str()); - value.valS = payload; regEvent(value.valS, "HttpGet"); } http.end(); } } - void sendHttpGET(String url) - { + + void sendHttpGET(String url) { WiFiClient client; HTTPClient http; #if defined ESP8266 - if (!http.begin(client, url)) - { + if (!http.begin(client, url)) { #elif defined ESP32 - if (!http.begin(url)) - { + if (!http.begin(url)) { #endif SerialPrint("I", F("HttpGet"), "connection failed "); @@ -52,31 +43,23 @@ public: String payload = http.getString(); SerialPrint("<-", F("HttpGET"), "URL: " + url); SerialPrint("->", F("HttpGET"), "URL: " + url + ", server: " + httpResponseCode); - if (httpResponseCode > 0) - { + if (httpResponseCode > 0) { value.valS = payload; + value.isDecimal = false; SerialPrint("->", F("HttpGET"), "msg from server: " + (String)payload.c_str()); - value.valS = payload; regEvent(value.valS, "HttpGet"); } http.end(); } - IoTValue execute(String command, std::vector ¶m) - { - if (param.size() > 0) - { - if (command == "get") - { - if (param.size()) - { + IoTValue execute(String command, std::vector ¶m) { + if (param.size() > 0) { + if (command == "get") { + if (param.size()) { sendHttpGET(param[0].valS); } - } - else if (command == "post") - { - if (param.size()) - { + } else if (command == "post") { + if (param.size()) { sendHttpPOST(param[0].valS, param[1].valS); } } @@ -87,14 +70,10 @@ public: ~HttpGet(){}; }; -void *getAPI_HttpGet(String subtype, String param) -{ - if (subtype == F("HttpGet")) - { +void *getAPI_HttpGet(String subtype, String param) { + if (subtype == F("HttpGet")) { return new HttpGet(param); - } - else - { + } else { return nullptr; } } \ No newline at end of file diff --git a/src/modules/exec/HttpGet/modinfo.json b/src/modules/exec/HttpGet/modinfo.json index 56c4d57b..d8bcda2c 100644 --- a/src/modules/exec/HttpGet/modinfo.json +++ b/src/modules/exec/HttpGet/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -41,7 +41,8 @@ "name": "post", "descr": "Отправить http запрос методом POST.", "params": [ - "URL","message" + "URL", + "message" ] } ] @@ -49,6 +50,9 @@ "defActive": false, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32s2_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/exec/IoTServo/IoTServo.cpp b/src/modules/exec/IoTServo/IoTServo.cpp index 3cd672cf..d9e95974 100644 --- a/src/modules/exec/IoTServo/IoTServo.cpp +++ b/src/modules/exec/IoTServo/IoTServo.cpp @@ -9,37 +9,41 @@ extern IoTGpio IoTgpio; class IoTServo : public IoTItem { private: Servo servObj; - int _apin, _oldValue; - int _locmap1, _locmap2, _locmap3, _locmap4; + // int _apin, _oldValue; + int _oldValue; + // int _locmap1, _locmap2, _locmap3, _locmap4; public: IoTServo(String parameters): IoTItem(parameters) { - int pin; + int pin, minPulseWidth, maxPulseWidth, neutralPulseWidth; jsonRead(parameters, "pin", pin); - servObj.attach(pin); + jsonRead(parameters, "minPulseWidth", minPulseWidth); + jsonRead(parameters, "maxPulseWidth", maxPulseWidth); + jsonRead(parameters, "neutralPulseWidth", neutralPulseWidth); + servObj.attach(pin, minPulseWidth, maxPulseWidth, neutralPulseWidth); - jsonRead(parameters, "apin", _apin); - if (_apin >= 0) IoTgpio.pinMode(_apin, INPUT); + // jsonRead(parameters, "apin", _apin); + // if (_apin >= 0) IoTgpio.pinMode(_apin, INPUT); - String map; - jsonRead(parameters, F("amap"), map, false); - if (map != "") { - _locmap1 = selectFromMarkerToMarker(map, ",", 0).toInt(); - _locmap2 = selectFromMarkerToMarker(map, ",", 1).toInt(); - _locmap3 = selectFromMarkerToMarker(map, ",", 2).toInt(); - _locmap4 = selectFromMarkerToMarker(map, ",", 3).toInt(); - } + // String map; + // jsonRead(parameters, F("amap"), map, false); + // if (map != "") { + // _locmap1 = selectFromMarkerToMarker(map, ",", 0).toInt(); + // _locmap2 = selectFromMarkerToMarker(map, ",", 1).toInt(); + // _locmap3 = selectFromMarkerToMarker(map, ",", 2).toInt(); + // _locmap4 = selectFromMarkerToMarker(map, ",", 3).toInt(); + // } } - void doByInterval() { - if (_apin >= 0) { - value.valD = map(IoTgpio.analogRead(_apin), _locmap1, _locmap2, _locmap3, _locmap4); - if (abs(_oldValue - value.valD) > 5) { - _oldValue = value.valD; - servObj.write(_oldValue); - } - } - } + // void doByInterval() { + // if (_apin >= 0) { + // value.valD = map(IoTgpio.analogRead(_apin), _locmap1, _locmap2, _locmap3, _locmap4); + // if (abs(_oldValue - value.valD) > 5) { + // _oldValue = value.valD; + // servObj.write(_oldValue); + // } + // } + // } IoTValue execute(String command, std::vector ¶m) { if (command == "rotate") { diff --git a/src/modules/exec/IoTServo/modinfo.json b/src/modules/exec/IoTServo/modinfo.json index b9a16e1b..5557aeb4 100644 --- a/src/modules/exec/IoTServo/modinfo.json +++ b/src/modules/exec/IoTServo/modinfo.json @@ -1,22 +1,22 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Сервопривод", - "type": "Writing", - "subtype": "IoTServo", - "id": "servo", - "widget": "range", - "page": "servo", - "descr": "угол", - - "int": 1, - "pin": 12, - "apin": -1, - "amap": "0, 4096, 0, 180" - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Сервопривод", + "type": "Writing", + "subtype": "IoTServo", + "id": "servo", + "widget": "rangeServo", + "page": "servo", + "descr": "угол", + "pin": 12, + "minPulseWidth": 544, + "maxPulseWidth": 2400, + "neutralPulseWidth": 1500, + "trackingID": "" + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -31,26 +31,33 @@ "title": "Сервопривод", "moduleDesc": "Предназначен для управления сервоприводом по уровню аналогово сигнала.", "propInfo": { - "int": "Пауза в секундах между опросами аналогового входа. Если 0, то читаем постоянно", "pin": "Пин, к которому подключен сервопривод", - "apin": "Номер GPIO аналогового пина. Если -1, то функция отключена.", - "amap": "Настройки преобразования значений аналога в нужный диапазон сервы, имеет смысл, если аналог включен." + "minPulseWidth": "Минимальная длина импульса", + "maxPulseWidth": "Максимальная длина импульса", + "neutralPulseWidth": "Нейтральная длина импульса", + "trackingID": "Идентификатор отслеживания значения другого элемента конфигурации, например, чтение аналога" }, "retInfo": "Содержит текущее значение поворота", "funcInfo": [ { "name": "rotate", "descr": "Повернуть привод на значение", - "params": ["Числовое значение"] + "params": [ + "Числовое значение" + ] } ] }, - "defActive": true, - "usedLibs": { "esp32_4mb": [ - "https://github.com/RoboticsBrno/ServoESP32" + "https://github.com/RoboticsBrno/ServoESP32#v1.0.3" + ], + "esp32_4mb3f": [ + "https://github.com/RoboticsBrno/ServoESP32#v1.0.3" + ], + "esp32cam_4mb": [ + "https://github.com/RoboticsBrno/ServoESP32#v1.0.3" ], "esp8266_4mb": [] } diff --git a/src/modules/exec/Mcp23008/modinfo.json b/src/modules/exec/Mcp23008/modinfo.json index d04c12f7..7554b884 100644 --- a/src/modules/exec/Mcp23008/modinfo.json +++ b/src/modules/exec/Mcp23008/modinfo.json @@ -1,21 +1,20 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Расширитель портов Mcp23008", - "type": "Reading", - "subtype": "Mcp23008", - "id": "Mcp", - "widget": "", - "page": "", - "descr": "", - - "int": "0", - "addr": "0x20", - "index": 1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Расширитель портов Mcp23008", + "type": "Reading", + "subtype": "Mcp23008", + "id": "Mcp", + "widget": "", + "page": "", + "descr": "", + "int": "0", + "addr": "0x20", + "index": 1 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -35,14 +34,20 @@ "index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д." } }, - "defActive": false, - "usedLibs": { "esp32_4mb": [ "adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0", "adafruit/Adafruit BusIO @ ^1.13.2" ], + "esp32_4mb3f": [ + "adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], "esp8266_4mb": [ "adafruit/Adafruit Mcp23017 Arduino Library@^2.1.0", "adafruit/Adafruit BusIO @ ^1.13.2" diff --git a/src/modules/exec/Mcp23017/modinfo.json b/src/modules/exec/Mcp23017/modinfo.json index ad20b6ca..53534b90 100644 --- a/src/modules/exec/Mcp23017/modinfo.json +++ b/src/modules/exec/Mcp23017/modinfo.json @@ -1,21 +1,20 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Расширитель портов Mcp23017", - "type": "Reading", - "subtype": "Mcp23017", - "id": "Mcp", - "widget": "", - "page": "", - "descr": "", - - "int": "0", - "addr": "0x20", - "index": 1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Расширитель портов Mcp23017", + "type": "Reading", + "subtype": "Mcp23017", + "id": "Mcp", + "widget": "", + "page": "", + "descr": "", + "int": "0", + "addr": "0x20", + "index": 1 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -35,14 +34,20 @@ "index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д." } }, - "defActive": true, - "usedLibs": { "esp32_4mb": [ "adafruit/Adafruit MCP23017 Arduino Library@^2.1.0", "adafruit/Adafruit BusIO @ ^1.13.2" ], + "esp32_4mb3f": [ + "adafruit/Adafruit MCP23017 Arduino Library@^2.1.0", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit MCP23017 Arduino Library@^2.1.0", + "adafruit/Adafruit BusIO @ ^1.13.2" + ], "esp8266_4mb": [ "adafruit/Adafruit MCP23017 Arduino Library@^2.1.0", "adafruit/Adafruit BusIO @ ^1.13.2" diff --git a/src/modules/exec/Mp3/modinfo.json b/src/modules/exec/Mp3/modinfo.json index efd4f6f8..6896d77b 100644 --- a/src/modules/exec/Mp3/modinfo.json +++ b/src/modules/exec/Mp3/modinfo.json @@ -1,21 +1,20 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "MP3 плеер", - "type": "Reading", - "subtype": "Mp3", - "id": "mp3", - "widget": "", - "page": "", - "descr": "", - - "int": 1, - "pins": "14,12", - "volume": 20 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "MP3 плеер", + "type": "Reading", + "subtype": "Mp3", + "id": "mp3", + "widget": "", + "page": "", + "descr": "", + "int": 1, + "pins": "14,12", + "volume": 20 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -59,12 +58,17 @@ { "name": "volume", "descr": "Установить громкость", - "params": ["Значение громкости"] + "params": [ + "Значение громкости" + ] }, { "name": "playFolder", "descr": "Проиграть файл из папки", - "params": ["Номер папки", "Номер файла"] + "params": [ + "Номер папки", + "Номер файла" + ] }, { "name": "play", @@ -83,13 +87,17 @@ } ] }, - "defActive": true, - "usedLibs": { "esp32_4mb": [ "dfrobot/DFRobotDFPlayerMini @ ^1.0.5" ], + "esp32_4mb4f": [ + "dfrobot/DFRobotDFPlayerMini @ ^1.0.5" + ], + "esp32cam_4mb": [ + "dfrobot/DFRobotDFPlayerMini @ ^1.0.5" + ], "esp8266_4mb": [ "dfrobot/DFRobotDFPlayerMini @ ^1.0.5" ] diff --git a/src/modules/exec/Multitouch/modinfo.json b/src/modules/exec/Multitouch/modinfo.json index d596113e..c08f35e7 100644 --- a/src/modules/exec/Multitouch/modinfo.json +++ b/src/modules/exec/Multitouch/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -9,7 +9,7 @@ "id": "impulse", "widget": "anydataDef", "page": "Кнопки", - "descr": "Количество нажаний", + "descr": "Количество нажатий", "needSave": 0, "int": 300, "inv": 1, @@ -44,10 +44,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], - "esp8285_1mb_ota": [] + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/exec/MySensors/MySensorsGate.cpp b/src/modules/exec/MySensors/MySensorsGate.cpp index 717c0954..5e147812 100644 --- a/src/modules/exec/MySensors/MySensorsGate.cpp +++ b/src/modules/exec/MySensors/MySensorsGate.cpp @@ -53,16 +53,11 @@ String parseToString(const MyMessage& message) { class MySensorsGate : public IoTItem { private: public: - MySensorsGate(String parameters) : IoTItem(parameters) { - SerialPrint("i", "MySensors", "Gate initialized"); - } + MySensorsGate(String parameters) : IoTItem(parameters) { SerialPrint("i", "MySensors", "Gate initialized"); } - void doByInterval() { - } + void doByInterval() {} - void loop() { - loopMySensorsExecute(); - } + void loop() { loopMySensorsExecute(); } ~MySensorsGate(){}; @@ -351,6 +346,10 @@ class MySensorsNode : public IoTItem { int _minutesPassed = 0; String json = "{}"; bool dataFromNode = false; + // временное решение + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; public: MySensorsNode(String parameters) : IoTItem(parameters) { @@ -388,9 +387,7 @@ class MySensorsNode : public IoTItem { } // событие когда пользователь подключается приложением или веб интерфейсом к усройству - void onMqttWsAppConnectEvent() { - setNewWidgetAttributes(); - } + void onMqttWsAppConnectEvent() { setNewWidgetAttributes(); } void setNewWidgetAttributes() { if (dataFromNode) { @@ -426,4 +423,4 @@ void* getAPI_MySensorsGate(String subtype, String param) { } else { return nullptr; } -} +} \ No newline at end of file diff --git a/src/modules/exec/MySensors/modinfo.json b/src/modules/exec/MySensors/modinfo.json index b611a1d7..75afbfe8 100644 --- a/src/modules/exec/MySensors/modinfo.json +++ b/src/modules/exec/MySensors/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Исполнительные устройства", + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -49,6 +49,8 @@ }, "defActive": false, "usedLibs": { - "esp32_4mb": [] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [] } } \ No newline at end of file diff --git a/src/modules/exec/Pcf8574/Pcf8574.cpp b/src/modules/exec/Pcf8574/Pcf8574.cpp index f7b5454a..cba9282e 100644 --- a/src/modules/exec/Pcf8574/Pcf8574.cpp +++ b/src/modules/exec/Pcf8574/Pcf8574.cpp @@ -1,28 +1,23 @@ #include "Global.h" #include "classes/IoTItem.h" #include "classes/IoTGpio.h" - #include #include -#define PCF8574_I2CADDR_DEFAULT 0x20 ///< DS3502 default I2C address +#define PCF8574_I2CADDR_DEFAULT 0x20 ///< DS3502 стандартный I2C адрес class Adafruit_PCF8574_mod { public: - Adafruit_PCF8574_mod() {}; + Adafruit_PCF8574_mod() : _pinConfig(0xFF) {}; // По умолчанию все пины настроены как входы + bool begin(uint8_t i2c_address = PCF8574_I2CADDR_DEFAULT, TwoWire *wire = &Wire) { i2c_dev = new Adafruit_I2CDevice(i2c_address, wire); - - if (!i2c_dev->begin()) { - return false; - } - - return true; + return i2c_dev->begin(); } bool digitalWriteByte(uint8_t d) { _writebuf = d; - return i2c_dev->write(&_writebuf, 1); + return updateRegister(); } uint8_t digitalReadByte(void) { @@ -32,20 +27,20 @@ class Adafruit_PCF8574_mod { bool digitalWrite(int pinnum, bool val) { if (val) { - _writebuf |= 1 << pinnum; + _writebuf |= (1 << pinnum); } else { _writebuf &= ~(1 << pinnum); } - return i2c_dev->write(&_writebuf, 1); + return updateRegister(); // Обновляем регистр после изменения состояния пина } bool pinMode(int pinnum, uint8_t val) { if ((val == INPUT) || (val == INPUT_PULLUP)) { - _writebuf |= 1 << pinnum; + _pinConfig |= (1 << pinnum); } else { - _writebuf &= ~(1 << pinnum); + _pinConfig &= ~(1 << pinnum); } - return i2c_dev->write(&_writebuf, 1); + return updateRegister(); // Обновляем регистр после изменения конфигурации пина } bool digitalRead(int pinnum) { @@ -55,6 +50,12 @@ class Adafruit_PCF8574_mod { private: uint8_t _readbuf = 0, _writebuf = 0; + uint8_t _pinConfig; // Конфигурация пинов (вход/выход) + + bool updateRegister() { + uint8_t outputValue = (_writebuf & ~_pinConfig) | (_pinConfig); + return i2c_dev->write(&outputValue, 1); // Отправляем обновленное значение регистра на устройство + } Adafruit_I2CDevice *i2c_dev; }; @@ -66,7 +67,7 @@ class Pcf8574Driver : public IoTGpio { public: Pcf8574Driver(int index, String addr) : IoTGpio(index) { if (!_pcf.begin(hexStringToUint8(addr), &Wire)) { - Serial.println("PCF8574 Init Error."); + Serial.println("Ошибка инициализации PCF8574."); // Переводим на русский } } @@ -85,11 +86,8 @@ class Pcf8574Driver : public IoTGpio { void digitalInvert(int pin) { _pcf.digitalWrite(pin, 1 - _pcf.digitalRead(pin)); } - - ~Pcf8574Driver() {}; }; - class Pcf8574 : public IoTItem { private: Pcf8574Driver* _driver; @@ -108,7 +106,7 @@ class Pcf8574 : public IoTItem { int index; jsonRead(parameters, "index", index); if (index > 4) { - Serial.println("Pcf8574 wrong index. Must be 0 - 4"); + Serial.println("Неправильный индекс Pcf8574. Должен быть 0 - 4."); // Переводим на русский return; } @@ -122,14 +120,13 @@ class Pcf8574 : public IoTItem { } } - //возвращает ссылку на экземпляр класса Pcf8574Driver IoTGpio* getGpioDriver() { return _driver; } ~Pcf8574() { delete _driver; - }; + } }; void* getAPI_Pcf8574(String subtype, String param) { diff --git a/src/modules/exec/Pcf8574/modinfo.json b/src/modules/exec/Pcf8574/modinfo.json index c3bfd7f4..45f008ce 100644 --- a/src/modules/exec/Pcf8574/modinfo.json +++ b/src/modules/exec/Pcf8574/modinfo.json @@ -1,20 +1,20 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Расширитель портов Pcf8574", - "type": "Reading", - "subtype": "Pcf8574", - "id": "Pcf", - "widget": "", - "page": "", - "descr": "", - "int": "0", - "addr": "0x20", - "index": 1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Расширитель портов Pcf8574", + "type": "Reading", + "subtype": "Pcf8574", + "id": "Pcf", + "widget": "", + "page": "", + "descr": "", + "int": "0", + "addr": "0x20", + "index": 1 + } + ], "about": { "authorName": "Serghei Crasnicov", "authorContact": "https://t.me/Serghei63", @@ -27,15 +27,20 @@ "int": "Не используется", "addr": "Адрес устройства на шине, обычно 0x20", "index": "Значения от 1 до 4, где при выборе 1 будет нумерация pin 100-115, при выборе 2 200-215 и т.д." - } + }, + "title": "Расширитель портов Pcf8574" }, - "defActive": true, - "usedLibs": { "esp32_4mb": [ "adafruit/Adafruit BusIO @ ^1.13.2" ], + "esp32_4mb3f": [ + "adafruit/Adafruit BusIO @ ^1.13.2" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit BusIO @ ^1.13.2" + ], "esp8266_4mb": [ "adafruit/Adafruit BusIO @ ^1.13.2" ], diff --git a/src/modules/exec/Pcf8591/Pcf8591.cpp b/src/modules/exec/Pcf8591/Pcf8591.cpp new file mode 100644 index 00000000..ff7d45bc --- /dev/null +++ b/src/modules/exec/Pcf8591/Pcf8591.cpp @@ -0,0 +1,71 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include "Wire.h" +#include + +// Make sure that this is set to the value in volts of VCC +#define ADC_REFERENCE_VOLTAGE 3.3 + +class Pcf8591 : public IoTItem { + int _pin; + bool _isRaw; + bool _isInited = false; + Adafruit_PCF8591 pcf = Adafruit_PCF8591(); + + public: + Pcf8591(String parameters) : IoTItem(parameters) { + String tmp; + jsonRead(parameters, "pin", tmp); + _pin = tmp.toInt(); + + jsonRead(parameters, "mode", tmp); + _isRaw = tmp == "raw"; + + if (!pcf.begin()) { + Serial.println("# Adafruit PCF8591 not found!"); + _isInited = false; + } else + + _isInited = true; + + Serial.println("# Adafruit PCF8591 found"); + + pcf.enableDAC(true); + + Serial.println("AIN0, AIN1, AIN2, AIN3"); + + } + + uint8_t dac_counter = 0; + + void doByInterval() { + + // Make a triangle wave on the DAC output + pcf.analogWrite(dac_counter++); + + if (_isInited) { + if (_isRaw) + value.valD = pcf.analogRead(_pin); // Чтение АЦП нулевого канала (Вольты) + else + value.valD = (int_to_volts(pcf.analogRead(_pin), 8, ADC_REFERENCE_VOLTAGE)); + regEvent(value.valD, "PCF8591"); + } + +} + + float int_to_volts(uint16_t dac_value, uint8_t bits, float logic_level) { + return (((float)dac_value / ((1 << bits) - 1)) * logic_level); + + } + + ~Pcf8591(){}; +}; + +void *getAPI_Pcf8591(String subtype, String param) { + if (subtype == F("Pcf8591")) { + return new Pcf8591(param); + } else { + return nullptr; + } +} diff --git a/src/modules/exec/Pcf8591/modinfo.json b/src/modules/exec/Pcf8591/modinfo.json new file mode 100644 index 00000000..c3bef508 --- /dev/null +++ b/src/modules/exec/Pcf8591/modinfo.json @@ -0,0 +1,75 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Расширитель портов PCF8591", + "type": "Reading", + "subtype": "Pcf8591", + "id": "Pcf85", + "widget": "anydataVlt", + "page": "PCF8591", + "descr": "PCF_0", + "pin": "0", + "mode": "volt", + "map": "1,255,1,100", + "plus": 0, + "multiply": 1, + "round": 2, + "int": 7 + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "", + "moduleName": "Pcf8591", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Расширитель 4-х аналоговых портов PCF8591", + "moduleDesc": "Позволяет получить относительную величину напряжения на понижающем трансформаторе.", + "propInfo": { + "pin": "Номер AN, к которому подключен датчик. Допускается 0, 1, 2, 3", + "mode": "Режим работы. volt - вывод в вольтах , raw - значения от 0 до 255", + "int": "Количество секунд между опросами датчика." + } + }, + "defActive": false, + "usedLibs": { + + "esp32_4mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp32_16mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_4mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_16mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_1mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_1mb_ota": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8285_1mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8285_1mb_ota": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_2mb": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ], + "esp8266_2mb_ota": [ + "https://github.com/adafruit/Adafruit_PCF8591" + ] + } +} \ No newline at end of file diff --git a/src/modules/exec/Pwm32/modinfo.json b/src/modules/exec/Pwm32/modinfo.json index dda8786b..ce789a19 100644 --- a/src/modules/exec/Pwm32/modinfo.json +++ b/src/modules/exec/Pwm32/modinfo.json @@ -1,24 +1,24 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "PWM ESP32", - "type": "Writing", - "subtype": "Pwm32", - "id": "pwm", - "widget": "range", - "page": "Кнопки", - "descr": "PWM", - "int": 0, - "pin": 2, - "freq": 5000, - "ledChannel": 2, - "PWM_resolution": 10, - "val": 0, - "apin": -1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "PWM ESP32", + "type": "Writing", + "subtype": "Pwm32", + "id": "pwm", + "widget": "range", + "page": "Кнопки", + "descr": "PWM", + "int": 0, + "pin": 2, + "freq": 5000, + "ledChannel": 2, + "PWM_resolution": 10, + "val": 0, + "apin": -1 + } + ], "about": { "authorName": "Avaks", "authorContact": "https://t.me/Avaks", @@ -42,10 +42,10 @@ "freq": "Частота" } }, - "defActive": true, - "usedLibs": { - "esp32_4mb": [] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [] } } \ No newline at end of file diff --git a/src/modules/exec/Pwm8266/modinfo.json b/src/modules/exec/Pwm8266/modinfo.json index dfd53149..f446db57 100644 --- a/src/modules/exec/Pwm8266/modinfo.json +++ b/src/modules/exec/Pwm8266/modinfo.json @@ -1,22 +1,22 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "PWM ESP8266", - "type": "Writing", - "subtype": "Pwm8266", - "id": "pwm", - "widget": "range", - "page": "Кнопки", - "descr": "PWM", - "int": 0, - "pin": 15, - "freq": 5000, - "val": 0, - "apin": -1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "PWM ESP8266", + "type": "Writing", + "subtype": "Pwm8266", + "id": "pwm", + "widget": "range", + "page": "Кнопки", + "descr": "PWM", + "int": 0, + "pin": 15, + "freq": 5000, + "val": 0, + "apin": -1 + } + ], "about": { "authorName": "Avaks", "authorContact": "https://t.me/Avaks", @@ -38,9 +38,7 @@ "freq": "Частота" } }, - "defActive": true, - "usedLibs": { "esp8266_4mb": [], "esp8266_1mb": [], diff --git a/src/modules/exec/SDcard/modinfo.json b/src/modules/exec/SDcard/modinfo.json index ef917963..5f10bce6 100644 --- a/src/modules/exec/SDcard/modinfo.json +++ b/src/modules/exec/SDcard/modinfo.json @@ -1,19 +1,18 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "SD карта", - "type": "Writing", - "subtype": "SDcard", - "id": "sd", - "widget": "", - "page": "", - "descr": "", - - "int": 1 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "SD карта", + "type": "Writing", + "subtype": "SDcard", + "id": "sd", + "widget": "", + "page": "", + "descr": "", + "int": 1 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -31,12 +30,16 @@ "int": "Не используется." } }, - "defActive": false, - "usedLibs": { "esp32_4mb": [ "espressif/esp32-camera @ ^2.0.0" + ], + "esp32_4mb3f": [ + "espressif/esp32-camera @ ^2.0.0" + ], + "esp32cam_4mb": [ + "espressif/esp32-camera @ ^2.0.0" ] } } \ No newline at end of file diff --git a/src/modules/exec/SysExt/modinfo.json b/src/modules/exec/SysExt/modinfo.json index 3f4313dc..1cd552ac 100644 --- a/src/modules/exec/SysExt/modinfo.json +++ b/src/modules/exec/SysExt/modinfo.json @@ -1,18 +1,18 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Доп. функции системы", - "type": "Reading", - "subtype": "SysExt", - "id": "SysExt", - "widget": "", - "page": "", - "descr": "", - "int": 15 - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Доп. функции системы", + "type": "Reading", + "subtype": "SysExt", + "id": "SysExt", + "widget": "", + "page": "", + "descr": "", + "int": 15 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -30,11 +30,11 @@ "int": "Не используется" } }, - "defActive": false, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [] } } \ No newline at end of file diff --git a/src/modules/exec/Telegram/modinfo.json b/src/modules/exec/Telegram/modinfo.json index b28a2478..2910ace1 100644 --- a/src/modules/exec/Telegram/modinfo.json +++ b/src/modules/exec/Telegram/modinfo.json @@ -1,23 +1,22 @@ -{ - "menuSection": "Исполнительные устройства", - - "configItem": [{ - "global": 0, - "name": "Телеграм-Бот", - "type": "Writing", - "subtype": "Telegram", - "id": "tg", - "widget": "", - "page": "", - "descr": "", - "int": 10, - - "token": "", - "autos": 1, - "receiveMsg": 0, - "chatID": "" - }], - +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "Телеграм-Бот", + "type": "Writing", + "subtype": "Telegram", + "id": "tg", + "widget": "", + "page": "", + "descr": "", + "int": 10, + "token": "", + "autos": 1, + "receiveMsg": 0, + "chatID": "" + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -38,25 +37,36 @@ "chatID": "ИД диалога с контактом. Необходим для отправки сообщений именно вам." }, "funcInfo": [ - { + { "name": "sendMsg", "descr": "Отправить сообщение без повторений.", - "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + "params": [ + "Сообщение, может быть строкой, числом или ИД другого элемента для получения значения" + ] }, - { + { "name": "sendOftenMsg", "descr": "Отправить сообщение в любом случае, даж если отправляли такое ранее.", - "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + "params": [ + "Сообщение, может быть строкой, числом или ИД другого элемента для получения значения" + ] } - ] + ] }, - "defActive": false, - "usedLibs": { "esp32_4mb": [ "CTBot @2.1.9" ], + "esp32_4mb3f": [ + "CTBot @2.1.9" + ], + "esp32cam_4mb": [ + "CTBot @2.1.9" + ], + "esp32_16mb": [ + "CTBot @2.1.9" + ], "esp8266_4mb": [ "CTBot @2.1.9" ] diff --git a/src/modules/exec/TelegramLT/TelegramLT.cpp b/src/modules/exec/TelegramLT/TelegramLT.cpp index 2fb3c14a..eee738e0 100644 --- a/src/modules/exec/TelegramLT/TelegramLT.cpp +++ b/src/modules/exec/TelegramLT/TelegramLT.cpp @@ -13,7 +13,7 @@ class TelegramLT : public IoTItem { } void sendTelegramMsg(bool often, String msg) { - if (WiFi.status() == WL_CONNECTED && (often || !often && _prevMsg != msg)) { + if (isNetworkActive() && (often || !often && _prevMsg != msg)) { WiFiClient client; HTTPClient http; http.begin(client, "http://live-control.com/iotm/telegram.php"); diff --git a/src/modules/exec/TelegramLT/modinfo.json b/src/modules/exec/TelegramLT/modinfo.json index e718fd62..4f2ce19d 100644 --- a/src/modules/exec/TelegramLT/modinfo.json +++ b/src/modules/exec/TelegramLT/modinfo.json @@ -1,6 +1,5 @@ { - "menuSection": "Исполнительные устройства", - + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -15,7 +14,6 @@ "chatID": "" } ], - "about": { "authorName": "AVAKS", "authorContact": "https://t.me/@avaks_dev", @@ -51,12 +49,15 @@ } ] }, - "defActive": true, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], @@ -64,4 +65,4 @@ "esp8266_2mb": [], "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/exec/Telegram_v2/Telegram_v2.cpp b/src/modules/exec/Telegram_v2/Telegram_v2.cpp new file mode 100644 index 00000000..ecaaaa4e --- /dev/null +++ b/src/modules/exec/Telegram_v2/Telegram_v2.cpp @@ -0,0 +1,361 @@ +#include "Global.h" +#include "classes/IoTItem.h" +// #define FB_NO_UNICODE +// #define FB_NO_URLENCODE +// #define FB_NO_OTA +// #define FB_DYNAMIC +// #include +// #include +// #include "esp_camera.h" + +#include +#include + +// FastBot _myBot; +FastBot *_myBot = nullptr; +FastBot *instanceBot() +{ + if (!_myBot) + { + _myBot = new FastBot(); + // ot->begin(); + } + return _myBot; +} + +String _token; +String _chatID; +bool _autos; +bool _initSD; + +class Telegram_v2 : public IoTItem +{ +private: + bool _receiveMsg; + String _prevMsg = ""; + bool _useLed = false; + +public: + Telegram_v2(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, "token", _token); + jsonRead(parameters, "autos", _autos); + jsonRead(parameters, "receiveMsg", _receiveMsg); + jsonRead(parameters, "chatID", _chatID); + instanceBot(); + _myBot->attach(telegramMsgParse); + +#ifdef ESP32 + // _myBot->useDNS(true); +#endif + + _myBot->setToken(_token); + // _myBot->enableUTF8Encoding(true); + _myBot->setChatID(_chatID); + // _myBot->showMenuText("help","help",false); + } + + void loop() + { + if (_receiveMsg && isNetworkActive()) + { + _myBot->tick(); + } + // Далее вызов doByInterval для обработки комманд + IoTItem::loop(); + } + + void doByInterval() + { + } + + IoTValue execute(String command, std::vector ¶m) + { + if (!isNetworkActive()) return {}; + if (command == "sendMsg") + { + if (param.size()) + { + String strTmp; + if (param[0].isDecimal) + strTmp = param[0].valD; + else + strTmp = param[0].valS; + sendTelegramMsg(false, strTmp); + } + } + else if (command == "sendOftenMsg") + { + if (param.size()) + { + String strTmp; + if (param[0].isDecimal) + strTmp = param[0].valD; + else + strTmp = param[0].valS; + sendTelegramMsg(true, strTmp); + } + } + else if (command == "sendPinMsg") + { + if (param.size()) + { + String strTmp; + if (param[0].isDecimal) + strTmp = param[0].valD; + else + strTmp = param[0].valS; + _myBot->sendMessage(strTmp, _chatID); + _myBot->pinMessage(_myBot->lastBotMsg()); + + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ",pin msg: " + strTmp); + } + } + else if (command == "editMsg") + { + if (param.size()) + { + String strTmp; + if (param[0].isDecimal) + strTmp = param[0].valD; + else + strTmp = param[0].valS; + _myBot->editMessage(_myBot->lastBotMsg(), strTmp); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ",edit msg: " + strTmp); + } + } + else if (command == "sendFile") + { + if (param.size() && !param[0].isDecimal) + { + // String path = filepath(filename); + auto file = FileFS.open(param[0].valS, FILE_READ); + if (!file) + { + SerialPrint("E", F("Telegram"), "Fail send file: " + param[0].valS); + return {}; + } + // File file = LittleFS.open(param[0].valS, "r"); // /test.png + // selectToMarkerLast(msg.text, "_") + uint8_t res = _myBot->sendFile(file, (FB_FileType)param[1].valD, selectToMarkerLast(param[0].valS, "/"), _chatID); + file.close(); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", sendFile: " + param[0].valS + " res: " + String(res)); + } + } + + else if (command == "editFile") + { + if (param.size() && !param[0].isDecimal) + { + // String path = filepath(filename); + auto file = FileFS.open(param[0].valS, FILE_READ); + if (!file) + { + SerialPrint("E", F("Telegram"), "Fail edit file: " + param[0].valS); + return {}; + } + // File file = LittleFS.open(param[0].valS, "r"); // /test.png + // selectToMarkerLast(msg.text, "_") + uint8_t res = _myBot->editFile(file, (FB_FileType)param[1].valD, selectToMarkerLast(param[0].valS, "/"), _myBot->lastBotMsg(), _chatID); + file.close(); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", editFile: " + param[0].valS + " res: " + String(res)); + } + } + return {}; + } + + void static telegramMsgParse(FB_msg &msg) + { + // FB_msg msg; + SerialPrint("->", F("Telegram"), "chat ID: " + msg.chatID + ", msg: " + msg.text); + // _myBot->setChatID(_chatID); + if (_autos) + { + _chatID = msg.chatID; + } + if (msg.text.indexOf("set") != -1) + { + msg.text = deleteBeforeDelimiter(msg.text, "_"); + generateOrder(selectToMarker(msg.text, "_"), selectToMarkerLast(msg.text, "_")); + _myBot->replyMessage("order done", msg.messageID, _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + String(msg.text)); + } + else if (msg.text.indexOf("get") != -1) + { + msg.text = deleteBeforeDelimiter(msg.text, "_"); + IoTItem *item = findIoTItem(msg.text); + if (item) + { + _myBot->replyMessage(item->getValue(), msg.messageID, _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + String(msg.text)); + } + } + + else if (msg.text.indexOf("all") != -1) + { + // String list = returnListOfParams(); + String out; + std::vector vctr; + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) + { + if ((*it)->iAmLocal) + { + if (it == IoTItems.begin()) + { + out = "get_" + (*it)->getID(); + } + else + { + out = out + " \n " + "get_" + (*it)->getID(); + } + vctr.push_back(atoff((*it)->getValue().c_str())); + // _myBot->sendMessage((*it)->getID() + ": " + (*it)->getValue(), _chatID); + } + } + _myBot->showMenuText("select Id", out, true); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + "\n" + out); + // _myBot->sendMessage(CharPlot(&vctr[0], vctr.size(), 5), _chatID); + // SerialPrint("<-", F("Telegram"), CharPlot(&vctr[0], vctr.size(), 10)); + } + + else if (msg.text.indexOf("file") != -1 && msg.chatID == _chatID) + { + msg.text = deleteBeforeDelimiter(msg.text, "_"); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", get file: " + String(msg.text)); + auto file = FileFS.open(selectToMarker(msg.text, "_"), FILE_READ); // /test.png + if (!file) + { + SerialPrint("E", F("Telegram"), "Fail send file: " + selectToMarker(msg.text, "_")); + return; + } + int type = atoi(selectToMarkerLast(msg.text, "_").c_str()); + _myBot->sendFile(file, (FB_FileType)type, selectToMarker(msg.text, "_"), _chatID); + file.close(); + } + else if (msg.isFile) + { + if (msg.text.indexOf("download") != -1 && msg.chatID == _chatID) + { + downloadFile(msg); + } + else if (msg.text.indexOf("nextion") != -1 && msg.chatID == _chatID) + { + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { + if ((*it)->getSubtype() == "NextionUpload") { + (*it)->uploadNextionTlgrm(msg.fileUrl); + } + } + } + } + else if (msg.text.indexOf("help") != -1) + { + _myBot->sendMessage("ID: " + chipId, _chatID); + _myBot->sendMessage("chatID: " + _chatID, _chatID); + _myBot->sendMessage(F("Wrong order, use /all to get all values, /get_id to get value, /set_id_value to set value, or /file_name_type or send file msg=download"), _chatID); + } + else + { + // setValue(msg.text); + } + } + + void sendTelegramMsg(bool often, String msg) + { + if (!isNetworkActive()) return; + if (often) + { + _myBot->sendMessage(msg, _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + msg); + } + else + { + if (_prevMsg != msg) + { + _prevMsg = msg; + _myBot->sendMessage(msg, _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", msg: " + msg); + } + } + } + + void sendFoto(uint8_t *buf, uint32_t length, const String &name) + { + if (!isNetworkActive()) return; + _myBot->sendFile(buf, length, FB_PHOTO, name, _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", send foto from esp-cam"); + } + + void editFoto(uint8_t *buf, uint32_t length, const String &name) + { + if (!isNetworkActive()) return; + _myBot->editFile(buf, length, FB_PHOTO, name, _myBot->lastBotMsg(), _chatID); + SerialPrint("<-", F("Telegram"), "chat ID: " + _chatID + ", edit foto from esp-cam"); + } + + int static downloadFile(FB_msg &msg) + { + int _size = 0; + String path = '/' + msg.fileName; // вида /filename.xxx + auto file = FileFS.open(path, FILE_WRITE); // открываем для записи + // _myBot->sendMessage("Downloading from: " + _chatID + ", file: " + String(msg.fileName), _chatID); + if (file) + { // файл открылся/создался + HTTPClient http; + +#ifdef ESP8266 // esp8266 требует SSl + BearSSL::WiFiClientSecure client; + client.setInsecure(); + http.begin(client, msg.fileUrl); // пингуем файл +#else // esp32 сама умеет SSL + http.begin(msg.fileUrl); // пингуем файл +#endif + + if (http.GET() == HTTP_CODE_OK) + { // файл доступен + // загружаем в память. Результат > 0 - успешно + _size = http.writeToStream(&file); + } + http.end(); // закрываем соединение + file.close(); // закрываем файл + + if (_size == 0) + { + SerialPrint("E", F("Telegram"), "download error file url: " + msg.fileUrl); + _myBot->sendMessage(F("Download Fail"), _chatID); + } + else + { + SerialPrint("<-", F("Telegram"), "download from: " + _chatID + ", file: " + msg.fileName + " size = " + String(_size) + " byte"); + _myBot->sendMessage("Download Ok, size = " + String(_size) + " byte", _chatID); + } + } + else + { + SerialPrint("E", F("Telegram"), F("file write error")); + _myBot->sendMessage(F("file write error"), _chatID); + } + return _size; + } + + IoTItem *getTlgrmDriver() + { + return this; + } + + ~Telegram_v2() + { + tlgrmItem = nullptr; + }; +}; + +void *getAPI_Telegram_v2(String subtype, String param) +{ + if (subtype == F("Telegram_v2")) + { + return new Telegram_v2(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/exec/Telegram_v2/modinfo.json b/src/modules/exec/Telegram_v2/modinfo.json new file mode 100644 index 00000000..a87f130b --- /dev/null +++ b/src/modules/exec/Telegram_v2/modinfo.json @@ -0,0 +1,96 @@ +{ + "menuSection": "executive_devices", + + "configItem": [{ + "global": 0, + "name": "Телеграм-Бот v2", + "type": "Writing", + "subtype": "Telegram_v2", + "id": "tg", + "widget": "", + "page": "", + "descr": "", + "int": 10, + + "token": "", + "autos": 1, + "receiveMsg": 0, + "chatID": "" + }], + + "about": { + "authorName": "Mikhail Bubnov", + "authorContact": "https://t.me/Mit4bmw", + "authorGit": "https://github.com/Mit4el", + "specialThanks": "", + "moduleName": "Telegram_v2", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 37, + "esp8266_4mb": 37 + }, + "title": "Телеграм-Бот v2", + "moduleDesc": "Добавляет возможность отправлять сообщения от имени бота контакту в Телеграм-чате и получать команды.", + "propInfo": { + "token": "Токен для авторизации бота в системе Telegram", + "autos": "Автоматически(1) или нет(0) запоминать ChatID по входящим сообщениям. Т.е. бот будет информировать тех, кто последний прислал сообщение.", + "receiveMsg": "Обрабатывать(1) или нет(0) входящие сообщения.", + "chatID": "ИД диалога с контактом. Необходим для отправки сообщений именно вам." + }, + "funcInfo": [ + { + "name": "sendMsg", + "descr": "Отправить сообщение без повторений.", + "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + }, + { + "name": "sendOftenMsg", + "descr": "Отправить сообщение в любом случае, даж если отправляли такое ранее.", + "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + }, + { + "name": "sendPinMsg", + "descr": "Отправить закрепленное сообщение в любом случае, даж если отправляли такое ранее.", + "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + }, + { + "name": "editMsg", + "descr": "Отредактировать последнее отправленное ботом сообщение.", + "params": ["Сообщение, может быть строкой, числом или ИД другого элемента для получения значения"] + }, + { + "name": "sendFile", + "descr": "Отправить файл в телеграмм, с указанием типа файла: 0-фото, 1-аудио, 2-документ, 3-видео, 4-анимация, 5-голос", + "params": ["Путь к файлу (/test.png)", "Тип файла/информации (число)"] + }, + { + "name": "editFile", + "descr": "Отредактировать последний отправленный файл, с указанием типа файла: 0-фото, 1-аудио, 2-документ, 3-видео, 4-анимация, 5-голос", + "params": ["Путь к файлу (/test.png)", "Тип файла/информации (число)"] + } + ] + }, + + "defActive": false, + + "usedLibs": { + "esp32_4mb": [ + "gyverlibs/FastBot" + ], + "esp32_4mb3f": [ + "gyverlibs/FastBot" + ], + "esp32cam_4mb": [ + "gyverlibs/FastBot" + ], + "esp32s2_4mb": [ + "gyverlibs/FastBot" + ], + "esp32_16mb": [ + "gyverlibs/FastBot" + ], + "esp8266_4mb": [ + "gyverlibs/FastBot" + ] + } +} \ No newline at end of file diff --git a/src/modules/exec/Thermostat/Thermostat.cpp b/src/modules/exec/Thermostat/Thermostat.cpp index bcac8274..8d3c9260 100644 --- a/src/modules/exec/Thermostat/Thermostat.cpp +++ b/src/modules/exec/Thermostat/Thermostat.cpp @@ -150,8 +150,9 @@ public: class ThermostatPID : public IoTItem { private: - String _set_id; // заданная температура - String _term_id; // термометр + String _set_id; // заданная температура + String _term_id; // термометр + String _term_rezerv_id; // резервный термометр float _int, _KP, _KI, _KD, sp, pv, pv_last = 0, // предыдущая температура ierr = 0, // интегральная погрешность @@ -168,6 +169,7 @@ public: { jsonRead(parameters, "set_id", _set_id); jsonRead(parameters, "term_id", _term_id); + jsonRead(parameters, "term_rezerv_id", _term_rezerv_id); jsonRead(parameters, "int", _int); jsonRead(parameters, "KP", _KP); jsonRead(parameters, "KI", _KI); @@ -237,15 +239,40 @@ protected: interim = tmp->getValue(); pv = ::atof(interim.c_str()); } + if (pv < -40 && pv > 120 && !pv) // Решаем что ошибка датчика + { + if (_term_rezerv_id != "") + { + tmp = findIoTItem(_term_rezerv_id); // используем резервный + if (tmp) + { + interim = tmp->getValue(); + pv = ::atof(interim.c_str()); + if (pv < -40 && pv > 120 && !pv) + pv = 0; + } + else + pv = 0; + } + else + pv = 0; + } if (sp && pv) { - value.valD = pid(sp, pv, pv_last, ierr, _int); - value.valS = (String)(int)pid(sp, pv, pv_last, ierr, _int); - regEvent(value.valS, "ThermostatPID", false, true); - } + // value.valD = pid(sp, pv, pv_last, ierr, _int); + // value.valS = (String)(int)value.valD; + regEvent(pid(sp, pv, pv_last, ierr, _int), "ThermostatPID", false, true); + } + else + regEvent(0, "ThermostatPID", false, true); pv_last = pv; } + // временное решение + unsigned long currentMillis; + unsigned long prevMillis; + unsigned long difference; + void loop() { if (enableDoByInt) @@ -327,8 +354,8 @@ private: public: ThermostatETK(String parameters) : IoTItem(parameters) { - jsonRead(parameters, "set_id", _set_id); - jsonRead(parameters, "term_id", _term_id); + // jsonRead(parameters, "set_id", _set_id); + // jsonRead(parameters, "term_id", _term_id); jsonRead(parameters, "iv_k", _iv_k); jsonRead(parameters, "outside_id", _outside_id); } diff --git a/src/modules/exec/Thermostat/modinfo.json b/src/modules/exec/Thermostat/modinfo.json index 9c356ea3..2e7bd938 100644 --- a/src/modules/exec/Thermostat/modinfo.json +++ b/src/modules/exec/Thermostat/modinfo.json @@ -1,6 +1,5 @@ { - "menuSection": "Исполнительные устройства", - + "menuSection": "executive_devices", "configItem": [ { "global": 0, @@ -35,6 +34,7 @@ "map": "1,100,1,100", "set_id": "", "term_id": "", + "term_rezerv_id": "", "rele": "", "KP": 5.0, "KI": 50, @@ -73,7 +73,6 @@ "outside_id": "" } ], - "about": { "authorName": "AVAKS", "authorContact": "https://t.me/@avaks_dev", @@ -106,30 +105,38 @@ { "name": "enable", "descr": "включить / выключить термостатирование (режим AUTO) применим к PID и Гистере́зис ", - "params": ["thermostat.enable(1) - вкл, thermostat.enable(0) - выкл, "] + "params": [ + "thermostat.enable(1) - вкл, thermostat.enable(0) - выкл, " + ] }, { "name": "KP", "descr": "Пропорциональный коэффициент PID .", - "params": ["thermostat.KP(1) - задает значение коэффициента"] + "params": [ + "thermostat.KP(1) - задает значение коэффициента" + ] }, { "name": "KI", "descr": "Интегральный коэффициент PID .", - "params": ["thermostat.KI(1) - задает значение коэффициента"] + "params": [ + "thermostat.KI(1) - задает значение коэффициента" + ] }, { "name": "KD", "descr": "Дифференциальный коэффициент PID .", - "params": ["thermostat.KD(1) - задает значение коэффициента"] + "params": [ + "thermostat.KD(1) - задает значение коэффициента" + ] } ] }, - "defActive": false, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], @@ -138,4 +145,4 @@ "esp8266_2mb": [], "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/sceninfo.json b/src/modules/sceninfo.json index fb472e38..551f6d0e 100644 --- a/src/modules/sceninfo.json +++ b/src/modules/sceninfo.json @@ -69,6 +69,11 @@ "descr": "Отправить значение в топик MQTT", "params": ["Топик", "Значение"] }, + { + "name": "mqttIsConnect", + "descr": "Получить состояние подключения к MQTT", + "params": [] + }, { "name": "getHours", "descr": "Получить текущее число часов. Если время не получено из сети Интернет или внешнего RTC, то условие пропускается", diff --git a/src/modules/sensors/A02Distance/modinfo.json b/src/modules/sensors/A02Distance/modinfo.json index 2bee177d..3ce35fc3 100644 --- a/src/modules/sensors/A02Distance/modinfo.json +++ b/src/modules/sensors/A02Distance/modinfo.json @@ -1,9 +1,9 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "name": "A02 Дальность", - "type": "Reading", + "type": "Reading", "subtype": "A02Distance", "id": "dist", "widget": "anydataCm", @@ -15,16 +15,17 @@ ], "about": { "authorName": "Bubnov Mikhail", - "authorContact": "https://t.me/Mitchel", + "authorContact": "https://t.me/Mit4bmw", "authorGit": "https://github.com/Mit4el", "exampleURL": "https://iotmanager.org/wiki", "specialThanks": "", "moduleName": "A02Distance", "moduleVersion": "0.1", - "moduleDesc": "A0221AU, A02YYUW Ультразвуковой датчик. Позволяет получить дальность с ультрозвуковых датчиков A0221AU, A02YYUW", + "moduleDesc": "Позволяет получить дальность с ультрозвуковых датчиков A0221AU, A02YYUW", "propInfo": { "int": "Количество секунд между опросами датчика." - } + }, + "title": "A0221AU, A02YYUW Ультразвуковой датчик дальности" }, "defActive": true, "usedLibs": { diff --git a/src/modules/sensors/Acs712/modinfo.json b/src/modules/sensors/Acs712/modinfo.json index a3540924..2f68ee40 100644 --- a/src/modules/sensors/Acs712/modinfo.json +++ b/src/modules/sensors/Acs712/modinfo.json @@ -1,9 +1,9 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "name": "Acs712 Ток", - "type": "Reading", + "type": "Reading", "subtype": "Acs712", "id": "amp", "widget": "anydataAmp", @@ -13,15 +13,15 @@ "pin": 39, "int": 5, "rms": 1, - "vref": 5000, + "vref": 5000, "sens": 100, - "adczero" : 512, - "btn-setZero": "nil" + "adczero": 512, + "btn-setZero": "nil" } ], "about": { "authorName": "Bubnov Mikhail", - "authorContact": "https://t.me/Mitchel", + "authorContact": "https://t.me/Mit4bmw", "authorGit": "https://github.com/Mit4el", "exampleURL": "https://iotmanager.org/wiki", "specialThanks": "", @@ -34,13 +34,17 @@ "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" - } + "adczero": "Переменная калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Для ESP8266 - 512, Для ESP32 -2048, это 2.5В = 0А (1,65 с делителем) для Acs712 20A и 30A при стабильном токе 5В", + "btn-setZero": "Кнопка калибровки нулевого значения отсчетов АЦП при нулевой нагрузке. Нагрузка в момент калибровки должна быть отключена! После перезагрузки будет установлено в значение по умолчанию adczero. Для сохранение смотрим лог, и изменияем adczero" + }, + "title": "Acs712 Датчик тока" }, "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32s2_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/Ads1115/modinfo.json b/src/modules/sensors/Ads1115/modinfo.json index 6395fbff..dccbbc28 100644 --- a/src/modules/sensors/Ads1115/modinfo.json +++ b/src/modules/sensors/Ads1115/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -44,6 +44,12 @@ "esp32_4mb": [ "adafruit/Adafruit ADS1X15 @ ^2.3.0" ], + "esp32_4mb3f": [ + "adafruit/Adafruit ADS1X15 @ ^2.3.0" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit ADS1X15 @ ^2.3.0" + ], "esp8266_4mb": [ "adafruit/Adafruit ADS1X15 @ ^2.3.0" ] diff --git a/src/modules/sensors/AhtXX/modinfo.json b/src/modules/sensors/AhtXX/modinfo.json index 7b180bec..b621e931 100644 --- a/src/modules/sensors/AhtXX/modinfo.json +++ b/src/modules/sensors/AhtXX/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -12,7 +12,7 @@ "descr": "AHTXX Температура", "int": 15, "addr": "0x38", - "shtType":1, + "shtType": 1, "round": 1 }, { @@ -26,7 +26,7 @@ "descr": "AHTXX Влажность", "int": 15, "addr": "0x38", - "shtType":1, + "shtType": 1, "round": 1 } ], @@ -59,8 +59,26 @@ "esp32_4mb": [ "https://github.com/enjoyneering/AHTxx.git" ], + "esp32_4mb3f": [ + "https://github.com/enjoyneering/AHTxx.git" + ], + "esp32cam_4mb": [ + "https://github.com/enjoyneering/AHTxx.git" + ], "esp8266_4mb": [ "https://github.com/enjoyneering/AHTxx.git" + ], + "esp8266_1mb": [ + "https://github.com/enjoyneering/AHTxx.git" + ], + "esp8266_1mb_ota": [ + "https://github.com/enjoyneering/AHTxx.git" + ], + "esp8285_1mb": [ + "https://github.com/enjoyneering/AHTxx.git" + ], + "esp8285_1mb_ota": [ + "https://github.com/enjoyneering/AHTxx.git" ] } } \ No newline at end of file diff --git a/src/modules/sensors/AnalogAdc/AnalogAdc.cpp b/src/modules/sensors/AnalogAdc/AnalogAdc.cpp index 1e90f30d..510d5723 100644 --- a/src/modules/sensors/AnalogAdc/AnalogAdc.cpp +++ b/src/modules/sensors/AnalogAdc/AnalogAdc.cpp @@ -64,13 +64,7 @@ class AnalogAdc : public IoTItem { _avgSumm = _avgSumm + IoTgpio.analogRead(_pin); _avgCount++; } - - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) { - prevMillis = millis(); - this->doByInterval(); - } + IoTItem::loop(); } ~AnalogAdc(){}; diff --git a/src/modules/sensors/AnalogAdc/modinfo.json b/src/modules/sensors/AnalogAdc/modinfo.json index a9a55c17..873701ff 100644 --- a/src/modules/sensors/AnalogAdc/modinfo.json +++ b/src/modules/sensors/AnalogAdc/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -42,6 +42,8 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/BH_1750/modinfo.json b/src/modules/sensors/BH_1750/modinfo.json index 832e4c5c..9177a22f 100644 --- a/src/modules/sensors/BH_1750/modinfo.json +++ b/src/modules/sensors/BH_1750/modinfo.json @@ -1,19 +1,19 @@ -{ - "menuSection": "Сенсоры", - - "configItem": [{ - "global": 0, - "name": "Cенсор освещенность Bh1750", - "type": "Reading", - "subtype": "Bh1750", - "id": "Bh1750", - "widget": "anydata", - "page": "Сенсоры", - "descr": "Освещённость", - "round": 1, - "int": 15 - }], - +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Cенсор освещенность Bh1750", + "type": "Reading", + "subtype": "Bh1750", + "id": "Bh1750", + "widget": "anydata", + "page": "Сенсоры", + "descr": "Освещённость", + "round": 1, + "int": 15 + } + ], "about": { "authorName": "Ilya Belyakov", "authorContact": "https://t.me/Biveraxe", @@ -37,6 +37,12 @@ "esp32_4mb": [ "BH1750" ], + "esp32_4mb3f": [ + "BH1750" + ], + "esp32cam_4mb": [ + "BH1750" + ], "esp8266_4mb": [ "BH1750" ] diff --git a/src/modules/sensors/Ble/Ble.cpp b/src/modules/sensors/Ble/Ble.cpp index 8632fd97..c9713e57 100644 --- a/src/modules/sensors/Ble/Ble.cpp +++ b/src/modules/sensors/Ble/Ble.cpp @@ -1,27 +1,196 @@ #include "Global.h" #include "classes/IoTItem.h" #include -#ifdef ESP32 #include +#define BLE_PART1 +#define BLE_PART2 #include +#include // Создаем переменную для хранения данных с датчиков bluetooth -StaticJsonDocument BLEbuffer; -JsonObject extBLEdata = BLEbuffer.to(); +// StaticJsonDocument BLEbuffer; +// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4); +// JsonObject extBLEdata = BLEbuffer.to(); +class BleSens; +std::vector BleSensArray; -BLEScan *pBLEScan; -TheengsDecoder decoder; -StaticJsonDocument<512> doc; +class BleSens : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + String _MAC; + String _sensor; + int timeRecv = 0; + int _minutesPassed = 0; + String json = "{}"; + int orange = 0; + int red = 0; + int offline = 0; + int _int; + bool dataFromNode = false; + +public: + String whoIAm(/*String &mac, String &sens*/) + { + // mac = _MAC; + // sens = _sensor; + return _MAC; + } + + void setBLEdata(JsonObject extBLEdata) + { + if (_sensor == "last") + { + timeRecv = extBLEdata[_sensor].as(); + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + else + { + String valStr = extBLEdata[_sensor].as(); + if (valStr != "null") + { + if (value.isDecimal == isDigitDotCommaStr(valStr)) + { + value.isDecimal = 1; + value.valD = valStr.toFloat(); + regEvent(value.valD, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.isDecimal = 0; + value.valS = valStr; + regEvent(value.valS, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + } + } + } + char *TimeToString(unsigned long t) + { + static char str[12]; + long h = t / 3600; + t = t % 3600; + int m = t / 60; + int s = t % 60; + sprintf(str, "%02ld:%02d:%02d", h, m, s); + return str; + } + + void doByInterval() + { + if (_sensor == "last") + { + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + _minutesPassed++; + setNewWidgetAttributes(); + } + void onMqttWsAppConnectEvent() + { + setNewWidgetAttributes(); + } + void setNewWidgetAttributes() + { + + int minutes_ = _minutesPassed * _int / 60; + jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_)); + if (dataFromNode) + { + if (orange != 0 && red != 0 && offline != 0) + { + if (minutes_ < orange) + { + jsonWriteStr(json, F("color"), ""); + } + if (minutes_ >= orange && minutes_ < red) + { + jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым + } + if (minutes_ >= red && minutes_ < offline) + { + jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным + } + if (minutes_ >= offline) + { + jsonWriteStr(json, F("info"), F("offline")); + } + } + } + else + { + jsonWriteStr(json, F("info"), F("awaiting")); + } + sendSubWidgetsValues(_id, json); + } + + BleSens(String parameters) : IoTItem(parameters) + { + _MAC = jsonReadStr(parameters, "MAC"); + _sensor = jsonReadStr(parameters, "sensor"); + jsonRead(parameters, F("orange"), orange); + jsonRead(parameters, F("red"), red); + jsonRead(parameters, F("offline"), offline); + jsonRead(parameters, F("int"), _int); + dataFromNode = false; + BleSensArray.push_back(this); + } + + ~BleSens(){}; +}; + +//======================================================================================================= + +/** Callback to process the results of the last scan or restart it */ +void scanEndedCB(NimBLEScanResults results) +{ + int count = results.getCount(); + SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count)); + // pBLEScan->clearResults(); +} class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks { private: - //описание параметров передаваемых из настроек датчика из веба + // описание параметров передаваемых из настроек датчика из веба int _scanDuration; String _filter; + bool _debug; + + StaticJsonDocument<512> doc; + BLEScan *pBLEScan; + TheengsDecoder decoder; public: - //======================================================================================================= std::string convertServiceData(std::string deviceServiceData) { int serviceDataLength = (int)deviceServiceData.length(); @@ -67,169 +236,91 @@ public: if (decoder.decodeBLEJson(BLEdata)) { - - BLEdata.remove("manufacturerdata"); - BLEdata.remove("servicedata"); - - String mac_address = BLEdata["id"].as(); + String mac_address = BLEdata["mac"].as(); + if (mac_address == "") + { + BLEdata["mac"] = BLEdata["id"]; + mac_address = BLEdata["id"].as(); + } mac_address.replace(":", ""); - if (_filter != "") + if (_debug < 2) { - if (BLEdata[_filter]) + BLEdata.remove("manufacturerdata"); + BLEdata.remove("servicedata"); + BLEdata.remove("type"); + BLEdata.remove("cidc"); + BLEdata.remove("acts"); + BLEdata.remove("cont"); + BLEdata.remove("track"); + BLEdata.remove("id"); + } + // дописываем время прихода пакета данных + BLEdata["last"] = millis(); + if (_debug) + { + if ((_filter != "" && BLEdata[_filter]) || _filter == "") { - for (JsonPair kv : BLEdata) + // for (JsonPair kv : BLEdata) + // { + // String val = BLEdata.as(); + String output; + if (_debug < 2) { - extBLEdata[mac_address][kv.key()] = BLEdata[kv.key()]; + BLEdata.remove("servicedatauuid"); } + serializeJson(BLEdata, output); + SerialPrint("i", F("BLE"), mac_address + " " + output); + //} + } - // дописываем время прихода пакета данных - extBLEdata[mac_address]["last"] = millis(); - } + SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as())); } - else + + // Перебираем все зарегистрированные сенсоры BleSens + for (std::vector::iterator it = BleSensArray.begin(); + it != BleSensArray.end(); ++it) { - for (JsonPair kv : BLEdata) - { - extBLEdata[mac_address][kv.key()] = BLEdata[kv.key()]; - } - // дописываем время прихода пакета данных - extBLEdata[mac_address]["last"] = millis(); + // Если это данные для нужного сенсора (по его МАКУ) + if ((*it)->whoIAm() == mac_address) + // то передаем ему json, дальше он сам разберется + (*it)->setBLEdata(BLEdata); } - }; + } } BleScan(String parameters) : IoTItem(parameters) { _scanDuration = jsonReadInt(parameters, "scanDuration"); _filter = jsonReadStr(parameters, "filter"); + jsonRead(parameters, "debug", _debug); + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); // create new scan + pBLEScan->setAdvertisedDeviceCallbacks(this); + pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value + pBLEScan->setMaxResults(0); // do not store the scan results, use callback only. + } + + // doByInterval() + void doByInterval() + { if (pBLEScan->isScanning() == false) { - SerialPrint("i", F("BLE"), "Start Scanning..."); - BLEDevice::init(""); - pBLEScan = BLEDevice::getScan(); // create new scan - pBLEScan->setAdvertisedDeviceCallbacks(this); - pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster - pBLEScan->setInterval(100); - pBLEScan->setWindow(99); // less or equal setInterval value + if (_scanDuration > 0) + { + SerialPrint("i", F("BLE"), "Start Scanning..."); + pBLEScan->start(_scanDuration, scanEndedCB, false); + } } } - //======================================================================================================= - - // doByInterval() - void doByInterval() - { - - if (_scanDuration > 0) - { - BLEScanResults foundDevices = pBLEScan->start(_scanDuration, true); - int count = foundDevices.getCount(); - SerialPrint("i", F("BLE"), "Devices found: " + String(count)); - SerialPrint("i", F("BLE"), "Scan done!"); - pBLEScan->clearResults(); - } - for (JsonPair kv : extBLEdata) - { - String val = extBLEdata[kv.key()].as(); - SerialPrint("i", F("BLE"), _id + " " + kv.key().c_str() + " " + val); - } - } - - //======================================================================================================= - - ~BleScan(){}; + ~BleScan() { BleSensArray.clear(); }; }; -class BleSens : public IoTItem -{ -private: - //описание параметров передаваемых из настроек датчика из веба - String _MAC; - String _sensor; - -public: - //======================================================================================================= - char *TimeToString(unsigned long t) - { - static char str[12]; - long h = t / 3600; - t = t % 3600; - int m = t / 60; - int s = t % 60; - sprintf(str, "%02ld:%02d:%02d", h, m, s); - return str; - } - - BleSens(String parameters) : IoTItem(parameters) - { - _MAC = jsonReadStr(parameters, "MAC"); - _sensor = jsonReadStr(parameters, "sensor"); - } - - //======================================================================================================= - - // doByInterval() - void doByInterval() - { - if (_sensor == "last") - { - int valInt = extBLEdata[_MAC][_sensor].as(); - char *s; - s = TimeToString(millis() / 1000 - valInt / 1000); - value.isDecimal = 0; - if (valInt > 0) - { - value.valS = s; - } - else - { - value.valS = ""; - } - regEvent(value.valS, _id); - } - else - { - String valStr = extBLEdata[_MAC][_sensor].as(); - if (valStr != "null") - { - if (value.isDecimal = isDigitDotCommaStr(valStr)) - { - value.isDecimal = 1; - value.valD = valStr.toFloat(); - regEvent(value.valD, _id); - } - else - { - value.isDecimal = 0; - value.valS = valStr; - regEvent(value.valS, _id); - } - } - else - { - value.isDecimal = 0; - value.valS = ""; - regEvent(value.valS, _id); - } - } - } - //======================================================================================================= - - ~BleSens(){}; -}; -#endif - -// Заглушка для ESP8266 -#ifdef ESP8266 -class Ble : public IoTItem -{ -private: -public: - Ble(String parameters) : IoTItem(parameters) {} -}; -#endif +//======================================================================================================= void *getAPI_Ble(String subtype, String param) { @@ -245,4 +336,4 @@ void *getAPI_Ble(String subtype, String param) { return nullptr; } -} +} \ No newline at end of file diff --git a/src/modules/sensors/Ble/modinfo.json b/src/modules/sensors/Ble/modinfo.json index fda2ea7a..ac7cd8fc 100644 --- a/src/modules/sensors/Ble/modinfo.json +++ b/src/modules/sensors/Ble/modinfo.json @@ -1,9 +1,8 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "name": "bluetooth сканер", - "num": 1, "type": "Reading", "subtype": "BleScan", "id": "BleScan", @@ -12,11 +11,11 @@ "descr": "", "int": 135, "scanDuration": 10, - "filter": "servicedatauuid" + "filter": "servicedatauuid", + "debug": 1 }, { "name": "bluetooth датчик", - "num": 1, "type": "Reading", "subtype": "BleSens", "id": "BleSens", @@ -24,32 +23,41 @@ "page": "Сенсоры", "descr": "", "needSave": 0, + "int": 30, "global": 0, "round": 1, - "int": 60, + "orange": 60, + "red": 120, + "offline": 180, "MAC": "", "sensor": "" } ], "about": { - "authorName": "AVAKS", - "authorContact": "https://t.me/@avaks_dev", - "authorGit": "https://github.com/avaksru", + "authorName": "AVAKS, v3 - Mit4bmw", + "authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw", + "authorGit": "https://github.com/avaksru, https://github.com/Mit4el", "specialThanks": "@Serghei63", "moduleName": "Ble", - "moduleVersion": "1.0", + "moduleVersion": "3.1", "usedRam": { - "esp32_4mb": 1261449, + "esp32_4mb": 314692, "esp8266_4mb": 0 }, - "subTypes": ["BleSens", "BleScan"], + "subTypes": [ + "BleSens", + "BleScan" + ], "title": "Сканер Bluetooth", - "moduleDesc": "Позволяет получить данные с Bluetooth часов и термометров Mijia, Xiaomi, Cleargrass, ...", + "moduleDesc": "Позволяет получить данные с Bluetooth часов и термометров и.т.д. Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h Здесь полный перечень датчиков, для уменьшения размера модуля рекомендуется использовать модули Ble_part1 и Ble_part2", "propInfo": { "round": "Округление после запятой.", - "int": "Интервал сканирования BLE окружения (BleScan) / Интервал отправки собранной телеметрии в MQTT (BleSens)", + "orange": "количество минут после которого окрасить виджет в оранжевый цвет", + "red": "количество минут после которого окрасить виджет в красный цвет", + "offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится", + "int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления", "scanDuration": "Длительность сканирования ", - "filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Данные будут считываться только с датчиков у которых есть передаваемый параметр указанный в фильтре", + "filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире", "MAC": "MAC адрес беспроводного датчика", "sensor": "Тип сенсора: температура / влажность / время / ... " } @@ -57,8 +65,22 @@ "defActive": false, "usedLibs": { "esp32_4mb": [ - "https://github.com/h2zero/NimBLE-Arduino.git", - "https://github.com/avaksru/decoder.git" + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_4mb3f": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32cam_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32s3_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32c3m_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" ] } -} +} \ No newline at end of file diff --git a/src/modules/sensors/Ble_part1/Ble_p1.cpp b/src/modules/sensors/Ble_part1/Ble_p1.cpp new file mode 100644 index 00000000..7ead4a2c --- /dev/null +++ b/src/modules/sensors/Ble_part1/Ble_p1.cpp @@ -0,0 +1,338 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#include +#define BLE_PART1 +// #define BLE_PART2 +#include +#include + +// Создаем переменную для хранения данных с датчиков bluetooth +// StaticJsonDocument BLEbuffer; +// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4); +// JsonObject extBLEdata = BLEbuffer.to(); +class BleSens; +std::vector BleSensArray; + +class BleSens : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + String _MAC; + String _sensor; + int timeRecv = 0; + int _minutesPassed = 0; + String json = "{}"; + int orange = 0; + int red = 0; + int offline = 0; + int _int; + bool dataFromNode = false; + +public: + String whoIAm(/*String &mac, String &sens*/) + { + // mac = _MAC; + // sens = _sensor; + return _MAC; + } + + void setBLEdata(JsonObject extBLEdata) + { + if (_sensor == "last") + { + timeRecv = extBLEdata[_sensor].as(); + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + else + { + String valStr = extBLEdata[_sensor].as(); + if (valStr != "null") + { + if (value.isDecimal == isDigitDotCommaStr(valStr)) + { + value.isDecimal = 1; + value.valD = valStr.toFloat(); + regEvent(value.valD, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.isDecimal = 0; + value.valS = valStr; + regEvent(value.valS, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + } + } + } + char *TimeToString(unsigned long t) + { + static char str[12]; + long h = t / 3600; + t = t % 3600; + int m = t / 60; + int s = t % 60; + sprintf(str, "%02ld:%02d:%02d", h, m, s); + return str; + } + + void doByInterval() + { + if (_sensor == "last") + { + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + _minutesPassed++; + setNewWidgetAttributes(); + } + void onMqttWsAppConnectEvent() + { + setNewWidgetAttributes(); + } + void setNewWidgetAttributes() + { + + int minutes_ = _minutesPassed * _int / 60; + jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_)); + if (dataFromNode) + { + if (orange != 0 && red != 0 && offline != 0) + { + if (minutes_ < orange) + { + jsonWriteStr(json, F("color"), ""); + } + if (minutes_ >= orange && minutes_ < red) + { + jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым + } + if (minutes_ >= red && minutes_ < offline) + { + jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным + } + if (minutes_ >= offline) + { + jsonWriteStr(json, F("info"), F("offline")); + } + } + } + else + { + jsonWriteStr(json, F("info"), F("awaiting")); + } + sendSubWidgetsValues(_id, json); + } + + BleSens(String parameters) : IoTItem(parameters) + { + _MAC = jsonReadStr(parameters, "MAC"); + _sensor = jsonReadStr(parameters, "sensor"); + jsonRead(parameters, F("orange"), orange); + jsonRead(parameters, F("red"), red); + jsonRead(parameters, F("offline"), offline); + jsonRead(parameters, F("int"), _int); + dataFromNode = false; + BleSensArray.push_back(this); + } + + ~BleSens(){}; +}; + +//======================================================================================================= + +/** Callback to process the results of the last scan or restart it */ +void scanEndedCB(NimBLEScanResults results) +{ + int count = results.getCount(); + SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count)); + // pBLEScan->clearResults(); +} + +class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks +{ +private: + // описание параметров передаваемых из настроек датчика из веба + int _scanDuration; + String _filter; + bool _debug; + + StaticJsonDocument<512> doc; + BLEScan *pBLEScan; + TheengsDecoder decoder; + +public: + std::string convertServiceData(std::string deviceServiceData) + { + int serviceDataLength = (int)deviceServiceData.length(); + char spr[2 * serviceDataLength + 1]; + for (int i = 0; i < serviceDataLength; i++) + sprintf(spr + 2 * i, "%.2x", (unsigned char)deviceServiceData[i]); + spr[2 * serviceDataLength] = 0; + return spr; + } + + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + JsonObject BLEdata = doc.to(); + String mac_adress_ = advertisedDevice->getAddress().toString().c_str(); + mac_adress_.toUpperCase(); + BLEdata["id"] = (char *)mac_adress_.c_str(); + + if (advertisedDevice->haveName()) + { + BLEdata["name"] = (char *)advertisedDevice->getName().c_str(); + } + if (advertisedDevice->haveManufacturerData()) + { + char *manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t *)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length()); + BLEdata["manufacturerdata"] = manufacturerdata; + free(manufacturerdata); + } + if (advertisedDevice->haveRSSI()) + BLEdata["rssi"] = (int)advertisedDevice->getRSSI(); + if (advertisedDevice->haveTXPower()) + BLEdata["txpower"] = (int8_t)advertisedDevice->getTXPower(); + if (advertisedDevice->haveServiceData()) + { + int serviceDataCount = advertisedDevice->getServiceDataCount(); + for (int j = 0; j < serviceDataCount; j++) + { + std::string service_data = convertServiceData(advertisedDevice->getServiceData(j)); + BLEdata["servicedata"] = (char *)service_data.c_str(); + std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString(); + BLEdata["servicedatauuid"] = (char *)serviceDatauuid.c_str(); + } + } + + if (decoder.decodeBLEJson(BLEdata)) + { + String mac_address = BLEdata["mac"].as(); + if (mac_address == "") + { + BLEdata["mac"] = BLEdata["id"]; + mac_address = BLEdata["id"].as(); + } + mac_address.replace(":", ""); + if (_debug < 2) + { + BLEdata.remove("manufacturerdata"); + BLEdata.remove("servicedata"); + BLEdata.remove("type"); + BLEdata.remove("cidc"); + BLEdata.remove("acts"); + BLEdata.remove("cont"); + BLEdata.remove("track"); + BLEdata.remove("id"); + } + // дописываем время прихода пакета данных + BLEdata["last"] = millis(); + if (_debug) + { + if ((_filter != "" && BLEdata[_filter]) || _filter == "") + { + // for (JsonPair kv : BLEdata) + // { + // String val = BLEdata.as(); + String output; + if (_debug < 2) + { + BLEdata.remove("servicedatauuid"); + } + serializeJson(BLEdata, output); + SerialPrint("i", F("BLE"), mac_address + " " + output); + //} + } + + SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as())); + } + + // Перебираем все зарегистрированные сенсоры BleSens + for (std::vector::iterator it = BleSensArray.begin(); + it != BleSensArray.end(); ++it) + { + // Если это данные для нужного сенсора (по его МАКУ) + if ((*it)->whoIAm() == mac_address) + // то передаем ему json, дальше он сам разберется + (*it)->setBLEdata(BLEdata); + } + } + } + + BleScan(String parameters) : IoTItem(parameters) + { + _scanDuration = jsonReadInt(parameters, "scanDuration"); + _filter = jsonReadStr(parameters, "filter"); + jsonRead(parameters, "debug", _debug); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); // create new scan + pBLEScan->setAdvertisedDeviceCallbacks(this); + pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value + pBLEScan->setMaxResults(0); // do not store the scan results, use callback only. + } + + // doByInterval() + void doByInterval() + { + if (pBLEScan->isScanning() == false) + { + if (_scanDuration > 0) + { + SerialPrint("i", F("BLE"), "Start Scanning..."); + pBLEScan->start(_scanDuration, scanEndedCB, false); + } + } + } + + ~BleScan() { BleSensArray.clear(); }; +}; + +//======================================================================================================= + +void *getAPI_Ble_part1(String subtype, String param) +{ + if (subtype == F("BleScan_p1")) + { + return new BleScan(param); + } + else if (subtype == F("BleSens_p1")) + { + return new BleSens(param); + } + else + { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/sensors/Ble_part1/modinfo.json b/src/modules/sensors/Ble_part1/modinfo.json new file mode 100644 index 00000000..9e990fbd --- /dev/null +++ b/src/modules/sensors/Ble_part1/modinfo.json @@ -0,0 +1,86 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "name": "bluetooth сканер", + "type": "Reading", + "subtype": "BleScan_p1", + "id": "BleScan", + "widget": "na", + "page": "", + "descr": "", + "int": 135, + "scanDuration": 10, + "filter": "servicedatauuid", + "debug": 1 + }, + { + "name": "bluetooth датчик", + "type": "Reading", + "subtype": "BleSens_p1", + "id": "BleSens", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "", + "needSave": 0, + "int": 30, + "global": 0, + "round": 1, + "orange": 60, + "red": 120, + "offline": 180, + "MAC": "", + "sensor": "" + } + ], + "about": { + "authorName": "AVAKS, v3 - Mit4bmw", + "authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw", + "authorGit": "https://github.com/avaksru, https://github.com/Mit4el", + "specialThanks": "@Serghei63", + "moduleName": "Ble_part1", + "moduleVersion": "3.1", + "usedRam": { + "esp32_4mb": 262796, + "esp8266_4mb": 0 + }, + "subTypes": [ + "BleSens_p1", + "BleScan_p1" + ], + "title": "Сканер Bluetooth, часть 1", + "moduleDesc": "Часть 1 популярных Bluetooth датчиков. Позволяет получить данные с Bluetooth часов и термометров Mijia, Xiaomi, Cleargrass, Qingping, Inkbird. Разделение на части сделано для уменьшения размера модуля. Обе части вместе не использовать! для всех датчиков модуль Ble! Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h", + "propInfo": { + "round": "Округление после запятой.", + "orange": "количество минут после которого окрасить виджет в оранжевый цвет", + "red": "количество минут после которого окрасить виджет в красный цвет", + "offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится", + "int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления", + "scanDuration": "Длительность сканирования ", + "filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире", + "MAC": "MAC адрес беспроводного датчика", + "sensor": "Тип сенсора: температура / влажность / время / ... " + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_4mb3f": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32cam_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32s3_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32c3m_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Ble_part2/Ble_p2.cpp b/src/modules/sensors/Ble_part2/Ble_p2.cpp new file mode 100644 index 00000000..0c8de171 --- /dev/null +++ b/src/modules/sensors/Ble_part2/Ble_p2.cpp @@ -0,0 +1,339 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#include +// #define BLE_PART1 +#define BLE_PART2 +#include +#include + +// Создаем переменную для хранения данных с датчиков bluetooth +// StaticJsonDocument BLEbuffer; +// DynamicJsonDocument extBLEdata(JSON_BUFFER_SIZE * 4); +// JsonObject extBLEdata = BLEbuffer.to(); +class BleSens; +std::vector BleSensArray; + +class BleSens : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + String _MAC; + String _sensor; + int timeRecv = 0; + int _minutesPassed = 0; + String json = "{}"; + int orange = 0; + int red = 0; + int offline = 0; + int _int; + bool dataFromNode = false; + +public: + String whoIAm(/*String &mac, String &sens*/) + { + // mac = _MAC; + // sens = _sensor; + return _MAC; + } + + void setBLEdata(JsonObject extBLEdata) + { + if (_sensor == "last") + { + timeRecv = extBLEdata[_sensor].as(); + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + else + { + String valStr = extBLEdata[_sensor].as(); + if (valStr != "null") + { + if (value.isDecimal == isDigitDotCommaStr(valStr)) + { + value.isDecimal = 1; + value.valD = valStr.toFloat(); + regEvent(value.valD, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + else + { + value.isDecimal = 0; + value.valS = valStr; + regEvent(value.valS, _id); + dataFromNode = true; + _minutesPassed = 0; + setNewWidgetAttributes(); + } + } + } + } + char *TimeToString(unsigned long t) + { + static char str[12]; + long h = t / 3600; + t = t % 3600; + int m = t / 60; + int s = t % 60; + sprintf(str, "%02ld:%02d:%02d", h, m, s); + return str; + } + + void doByInterval() + { + if (_sensor == "last") + { + char *s; + s = TimeToString(millis() / 1000 - timeRecv / 1000); + value.isDecimal = 0; + if (timeRecv > 0) + { + value.valS = s; + } + else + { + value.valS = ""; + } + regEvent(value.valS, _id); + } + _minutesPassed++; + setNewWidgetAttributes(); + } + void onMqttWsAppConnectEvent() + { + setNewWidgetAttributes(); + } + void setNewWidgetAttributes() + { + + int minutes_ = _minutesPassed * _int / 60; + jsonWriteStr(json, F("info"), prettyMinutsTimeout(minutes_)); + if (dataFromNode) + { + if (orange != 0 && red != 0 && offline != 0) + { + if (minutes_ < orange) + { + jsonWriteStr(json, F("color"), ""); + } + if (minutes_ >= orange && minutes_ < red) + { + jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым + } + if (minutes_ >= red && minutes_ < offline) + { + jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным + } + if (minutes_ >= offline) + { + jsonWriteStr(json, F("info"), F("offline")); + } + } + } + else + { + jsonWriteStr(json, F("info"), F("awaiting")); + } + sendSubWidgetsValues(_id, json); + } + + BleSens(String parameters) : IoTItem(parameters) + { + _MAC = jsonReadStr(parameters, "MAC"); + _sensor = jsonReadStr(parameters, "sensor"); + jsonRead(parameters, F("orange"), orange); + jsonRead(parameters, F("red"), red); + jsonRead(parameters, F("offline"), offline); + jsonRead(parameters, F("int"), _int); + dataFromNode = false; + BleSensArray.push_back(this); + } + + ~BleSens(){}; +}; + +//======================================================================================================= + +/** Callback to process the results of the last scan or restart it */ +void scanEndedCB(NimBLEScanResults results) +{ + int count = results.getCount(); + SerialPrint("i", F("BLE"), "Scan done! "); // +"Devices found: " + String(count)); + // pBLEScan->clearResults(); +} + +class BleScan : public IoTItem, BLEAdvertisedDeviceCallbacks +{ +private: + // описание параметров передаваемых из настроек датчика из веба + int _scanDuration; + String _filter; + bool _debug; + + StaticJsonDocument<512> doc; + BLEScan *pBLEScan; + TheengsDecoder decoder; + +public: + std::string convertServiceData(std::string deviceServiceData) + { + int serviceDataLength = (int)deviceServiceData.length(); + char spr[2 * serviceDataLength + 1]; + for (int i = 0; i < serviceDataLength; i++) + sprintf(spr + 2 * i, "%.2x", (unsigned char)deviceServiceData[i]); + spr[2 * serviceDataLength] = 0; + return spr; + } + + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + JsonObject BLEdata = doc.to(); + String mac_adress_ = advertisedDevice->getAddress().toString().c_str(); + mac_adress_.toUpperCase(); + BLEdata["id"] = (char *)mac_adress_.c_str(); + + if (advertisedDevice->haveName()) + { + BLEdata["name"] = (char *)advertisedDevice->getName().c_str(); + } + if (advertisedDevice->haveManufacturerData()) + { + char *manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t *)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length()); + BLEdata["manufacturerdata"] = manufacturerdata; + free(manufacturerdata); + } + if (advertisedDevice->haveRSSI()) + BLEdata["rssi"] = (int)advertisedDevice->getRSSI(); + if (advertisedDevice->haveTXPower()) + BLEdata["txpower"] = (int8_t)advertisedDevice->getTXPower(); + if (advertisedDevice->haveServiceData()) + { + int serviceDataCount = advertisedDevice->getServiceDataCount(); + for (int j = 0; j < serviceDataCount; j++) + { + std::string service_data = convertServiceData(advertisedDevice->getServiceData(j)); + BLEdata["servicedata"] = (char *)service_data.c_str(); + std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString(); + BLEdata["servicedatauuid"] = (char *)serviceDatauuid.c_str(); + } + } + + if (decoder.decodeBLEJson(BLEdata)) + { + String mac_address = BLEdata["mac"].as(); + if (mac_address == "") + { + BLEdata["mac"] = BLEdata["id"]; + mac_address = BLEdata["id"].as(); + } + mac_address.replace(":", ""); + if (_debug < 2) + { + BLEdata.remove("manufacturerdata"); + BLEdata.remove("servicedata"); + BLEdata.remove("type"); + BLEdata.remove("cidc"); + BLEdata.remove("acts"); + BLEdata.remove("cont"); + BLEdata.remove("track"); + BLEdata.remove("id"); + } + + // дописываем время прихода пакета данных + BLEdata["last"] = millis(); + if (_debug) + { + if ((_filter != "" && BLEdata[_filter]) || _filter == "") + { + // for (JsonPair kv : BLEdata) + // { + // String val = BLEdata.as(); + String output; + if (_debug < 2) + { + BLEdata.remove("servicedatauuid"); + } + serializeJson(BLEdata, output); + SerialPrint("i", F("BLE"), mac_address + " " + output); + //} + } + + SerialPrint("i", F("BLE"), "found: " + String(BLEdata["mac"].as())); + } + + // Перебираем все зарегистрированные сенсоры BleSens + for (std::vector::iterator it = BleSensArray.begin(); + it != BleSensArray.end(); ++it) + { + // Если это данные для нужного сенсора (по его МАКУ) + if ((*it)->whoIAm() == mac_address) + // то передаем ему json, дальше он сам разберется + (*it)->setBLEdata(BLEdata); + } + } + } + + BleScan(String parameters) : IoTItem(parameters) + { + _scanDuration = jsonReadInt(parameters, "scanDuration"); + _filter = jsonReadStr(parameters, "filter"); + jsonRead(parameters, "debug", _debug); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); // create new scan + pBLEScan->setAdvertisedDeviceCallbacks(this); + pBLEScan->setActiveScan(false); // active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value + pBLEScan->setMaxResults(0); // do not store the scan results, use callback only. + } + + // doByInterval() + void doByInterval() + { + if (pBLEScan->isScanning() == false) + { + if (_scanDuration > 0) + { + SerialPrint("i", F("BLE"), "Start Scanning..."); + pBLEScan->start(_scanDuration, scanEndedCB, false); + } + } + } + + ~BleScan() { BleSensArray.clear(); }; +}; + +//======================================================================================================= + +void *getAPI_Ble_part2(String subtype, String param) +{ + if (subtype == F("BleScan_p2")) + { + return new BleScan(param); + } + else if (subtype == F("BleSens_p2")) + { + return new BleSens(param); + } + else + { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/sensors/Ble_part2/modinfo.json b/src/modules/sensors/Ble_part2/modinfo.json new file mode 100644 index 00000000..6c7d1634 --- /dev/null +++ b/src/modules/sensors/Ble_part2/modinfo.json @@ -0,0 +1,86 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "name": "bluetooth сканер", + "type": "Reading", + "subtype": "BleScan_p2", + "id": "BleScan", + "widget": "na", + "page": "", + "descr": "", + "int": 135, + "scanDuration": 10, + "filter": "servicedatauuid", + "debug": 1 + }, + { + "name": "bluetooth датчик", + "type": "Reading", + "subtype": "BleSens_p2", + "id": "BleSens", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "", + "needSave": 0, + "int": 30, + "global": 0, + "round": 1, + "orange": 60, + "red": 120, + "offline": 180, + "MAC": "", + "sensor": "" + } + ], + "about": { + "authorName": "AVAKS, v3 - Mit4bmw", + "authorContact": "https://t.me/@avaks, https://t.me/Mit4bmw", + "authorGit": "https://github.com/avaksru, https://github.com/Mit4el", + "specialThanks": "@Serghei63", + "moduleName": "Ble_part2", + "moduleVersion": "3.1", + "usedRam": { + "esp32_4mb": 288972, + "esp8266_4mb": 0 + }, + "subTypes": [ + "BleSens_p2", + "BleScan_p2" + ], + "title": "Сканер Bluetooth, часть 2", + "moduleDesc": "Часть 2 Bluetooth датчиков. Позволяет получить данные с Bluetooth датчиков, кроме Mijia, Xiaomi, Cleargrass, Qingping, Inkbird. Обе части вместе не использовать! для всех датчиков модуль Ble! Полный список (учитывать отстование нашей версии библиотеки) https://decoder.theengs.io/devices/devices.html Наш перечень в файле lib/decoder/devices.h", + "propInfo": { + "round": "Округление после запятой.", + "orange": "количество минут после которого окрасить виджет в оранжевый цвет", + "red": "количество минут после которого окрасить виджет в красный цвет", + "offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится", + "int": "Интервал сканирования BLE окружения (BleScan) / В BleSens темп обновления времнени поступления данных, сами даные обновляются по мене сканирования/поступления", + "scanDuration": "Длительность сканирования ", + "filter": "Позволяет установить фильтр по параметру передаваемому датчиком. Влияет только на вывод лога при debug=1, что бы было легче найти датчики, если много устройств в эфире", + "MAC": "MAC адрес беспроводного датчика", + "sensor": "Тип сенсора: температура / влажность / время / ... " + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32_4mb3f": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32cam_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32s3_16mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ], + "esp32c3m_4mb": [ + "https://github.com/Mit4el/NimBLE-Arduino.git" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Bme280/modinfo.json b/src/modules/sensors/Bme280/modinfo.json index 5bb6f9b2..c8b114da 100644 --- a/src/modules/sensors/Bme280/modinfo.json +++ b/src/modules/sensors/Bme280/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -83,6 +83,12 @@ "esp32_4mb": [ "adafruit/Adafruit BME280 Library" ], + "esp32_4mb3f": [ + "adafruit/Adafruit BME280 Library" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit BME280 Library" + ], "esp8266_4mb": [ "adafruit/Adafruit BME280 Library" ], diff --git a/src/modules/sensors/Bmp280/modinfo.json b/src/modules/sensors/Bmp280/modinfo.json index 4ce96b66..ad796c25 100644 --- a/src/modules/sensors/Bmp280/modinfo.json +++ b/src/modules/sensors/Bmp280/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -55,6 +55,12 @@ "esp32_4mb": [ "adafruit/Adafruit BMP280 Library" ], + "esp32_4mb3f": [ + "adafruit/Adafruit BMP280 Library" + ], + "esp32cam_4mb": [ + "adafruit/Adafruit BMP280 Library" + ], "esp8266_4mb": [ "adafruit/Adafruit BMP280 Library" ], @@ -64,9 +70,6 @@ "esp8266_1mb_ota": [ "adafruit/Adafruit BMP280 Library" ], - "esp8266_2mb": [ - "adafruit/Adafruit BMP280 Library" - ], "esp8285_1mb": [ "adafruit/Adafruit BMP280 Library" ], diff --git a/src/modules/sensors/DS2401/modinfo.json b/src/modules/sensors/DS2401/modinfo.json index 8364b4f0..c718c39d 100644 --- a/src/modules/sensors/DS2401/modinfo.json +++ b/src/modules/sensors/DS2401/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -36,6 +36,8 @@ "defActive": false, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/Dht1122/modinfo.json b/src/modules/sensors/Dht1122/modinfo.json index 7e5dc5be..802cd8fd 100644 --- a/src/modules/sensors/Dht1122/modinfo.json +++ b/src/modules/sensors/Dht1122/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -55,6 +55,12 @@ "esp32_4mb": [ "beegee-tokyo/DHT sensor library for ESPx" ], + "esp32_4mb3f": [ + "beegee-tokyo/DHT sensor library for ESPx" + ], + "esp32cam_4mb": [ + "beegee-tokyo/DHT sensor library for ESPx" + ], "esp8266_4mb": [ "beegee-tokyo/DHT sensor library for ESPx" ] diff --git a/src/modules/sensors/Ds18b20/modinfo.json b/src/modules/sensors/Ds18b20/modinfo.json index 9a4519a4..7bb5e566 100644 --- a/src/modules/sensors/Ds18b20/modinfo.json +++ b/src/modules/sensors/Ds18b20/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -42,6 +42,12 @@ "esp32_4mb": [ "https://github.com/milesburton/Arduino-Temperature-Control-Library" ], + "esp32_4mb3f": [ + "https://github.com/milesburton/Arduino-Temperature-Control-Library" + ], + "esp32cam_4mb": [ + "https://github.com/milesburton/Arduino-Temperature-Control-Library" + ], "esp8266_4mb": [ "https://github.com/milesburton/Arduino-Temperature-Control-Library" ], diff --git a/src/modules/sensors/Ds2423/Ds2423.cpp b/src/modules/sensors/Ds2423/Ds2423.cpp new file mode 100644 index 00000000..1ec384f7 --- /dev/null +++ b/src/modules/sensors/Ds2423/Ds2423.cpp @@ -0,0 +1,178 @@ + +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include +#include + +typedef uint8_t DeviceAddressDS2423[8]; +// глобальные списки необходимы для хранения объектов об активных линиях 1-wire используемых разными датчиками из модуля. Ключ - номер пина +std::map oneWireDS2423Array; + +// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку +OneWire *instanceOneWire_DS2423(uint8_t ONE_WIRE_PIN) +{ + // учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась + if (oneWireDS2423Array.find(ONE_WIRE_PIN) == oneWireDS2423Array.end()) + oneWireDS2423Array[ONE_WIRE_PIN] = new OneWire((uint8_t)ONE_WIRE_PIN); + return oneWireDS2423Array[ONE_WIRE_PIN]; +} + +// Определяем адрес. +bool getDeviceAddressDS2423(uint8_t pin, uint8_t *deviceAddress, int index) +{ + OneWire *_wire = instanceOneWire_DS2423(pin); + uint8_t depth = 0; + _wire->reset_search(); + while (depth <= index && _wire->search(deviceAddress)) + { + if (depth == index && _wire->crc8((uint8_t *)deviceAddress, 7) == deviceAddress[7]) + return true; + depth++; + } + return false; +} + + +class Ds2423a : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + String _addr; + int _pin; + int _index; + DS2423 *ds2423; + DeviceAddressDS2423 _deviceAddress; + +public: + Ds2423a(String parameters) : IoTItem(parameters) + { + + jsonRead(parameters, "pin", _pin); + jsonRead(parameters, "index", _index, false); + jsonRead(parameters, "addr", _addr, false); + + // Определяем адрес. Если параметр addr не установлен, то узнаем адрес по индексу + if (_addr == "") + { + if (getDeviceAddressDS2423(_pin, _deviceAddress, _index)) + { + char addrStr[20] = ""; + hex2string(_deviceAddress, 8, addrStr); + SerialPrint("I", "Sensor " + (String)_id, "index: " + (String)_index + " addr: " + String(addrStr)); + } + else + { + SerialPrint("E", "Sensor " + (String)_id, "index: " + (String)_index + " addres not search"); + } + } + else + { + string2hex(_addr.c_str(), _deviceAddress); + } + + ds2423 = new DS2423(instanceOneWire_DS2423(_pin), _deviceAddress); + ds2423->begin(DS2423_COUNTER_A | DS2423_COUNTER_B); + + } + + void doByInterval() + { + ds2423->update(); + if (ds2423->isError()) + { + Serial.println("Error reading counter"); + } + else + { + value.valD = ds2423->getCount(DS2423_COUNTER_A); + // if (value.valD != -127) + regEvent(value.valD, "Ds2423a"); // обязательный вызов для отправки результата работы + // else + // SerialPrint("E", "Sensor Ds2423a", "Error"); + } + } + //======================================================================================================= + + ~Ds2423a(){}; +}; + +class Ds2423b : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + String _addr; + int _pin; + int _index; + DS2423 *ds2423; + DeviceAddressDS2423 _deviceAddress; + +public: + Ds2423b(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, "pin", _pin); + jsonRead(parameters, "index", _index, false); + jsonRead(parameters, "addr", _addr, false); + + // Определяем адрес. Если параметр addr не установлен, то узнаем адрес по индексу + if (_addr == "") + { + if (getDeviceAddressDS2423(_pin, _deviceAddress, _index)) + { + char addrStr[20] = ""; + hex2string(_deviceAddress, 8, addrStr); + SerialPrint("I", "Sensor " + (String)_id, "index: " + (String)_index + " addr: " + String(addrStr)); + } + else + { + SerialPrint("E", "Sensor " + (String)_id, "index: " + (String)_index + " addres not search"); + } + } + else + { + string2hex(_addr.c_str(), _deviceAddress); + } + + ds2423 = new DS2423(instanceOneWire_DS2423(_pin), _deviceAddress); + ds2423->begin(DS2423_COUNTER_A | DS2423_COUNTER_B); + } + + void doByInterval() + { + ds2423->update(); + + if (ds2423->isError()) + { + Serial.println("Error reading counter"); + } + else + { + // запускаем опрос измерений у всех датчиков на линии + value.valD = ds2423->getCount(DS2423_COUNTER_B); + // if (value.valD != -127) + regEvent(value.valD, "Ds2423b"); // обязательный вызов для отправки результата работы + // else + // SerialPrint("E", "Sensor Ds2423b", "Error"); + } + } + //======================================================================================================= + + ~Ds2423b(){}; +}; + +void *getAPI_Ds2423(String subtype, String param) +{ + if (subtype == F("Ds2423a")) + { + return new Ds2423a(param); + } + else if (subtype == F("Ds2423b")) + { + return new Ds2423b(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/Ds2423/modinfo.json b/src/modules/sensors/Ds2423/modinfo.json new file mode 100644 index 00000000..958541a4 --- /dev/null +++ b/src/modules/sensors/Ds2423/modinfo.json @@ -0,0 +1,80 @@ +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "DS2423 счетчик 1", + "type": "Reading", + "subtype": "Ds2423a", + "id": "dscounta", + "widget": "anydataDef", + "page": "Счетчики", + "descr": "DS1 V", + "plus": 0, + "multiply": 1, + "pin": 4, + "index": 0, + "addr": "", + "int": 10, + "round": 0, + "needSave": 0 + }, + { + "global": 0, + "name": "DS2423 счетчик 2", + "type": "Reading", + "subtype": "Ds2423b", + "id": "dscountb", + "widget": "anydataDef", + "page": "Счетчики", + "descr": "DS2 V", + "plus": 0, + "multiply": 1, + "pin": 4, + "index": 0, + "addr": "", + "int": 10, + "round": 0, + "needSave": 0 + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Bubnov Mikhail @Mit4bmw", + "moduleName": "Ds2423", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Cчетчик ds2423", + "moduleDesc": "Позволяет получить значения с Ds2423.", + "propInfo": { + "pin": "GPIO номер, к которому подключена шина данных датчиков.", + "index": "Порядковый номер датчика на шине.", + "addr": "Адрес датчика на шине для точной идентификации. Если оставить пустым, то попробует найти по индексу и пину и Можно скопировать из консоли.", + "int": "Количество секунд между опросами датчика." + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/jbechter/arduino-onewire-DS2423", + "paulstoffregen/OneWire @ ^2.3.7" + ], + "esp32_4mb3f": [ + "https://github.com/jbechter/arduino-onewire-DS2423", + "paulstoffregen/OneWire @ ^2.3.7" + ], + "esp32cam_4mb": [ + "https://github.com/jbechter/arduino-onewire-DS2423", + "paulstoffregen/OneWire @ ^2.3.7" + ], + "esp8266_4mb": [ + "https://github.com/jbechter/arduino-onewire-DS2423", + "paulstoffregen/OneWire @ ^2.3.7" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Emon/modinfo.json b/src/modules/sensors/Emon/modinfo.json index e254359f..f984a65c 100644 --- a/src/modules/sensors/Emon/modinfo.json +++ b/src/modules/sensors/Emon/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -62,6 +62,12 @@ "esp32_4mb": [ "openenergymonitor/EmonLib@1.1.0" ], + "esp32_4mb3f": [ + "openenergymonitor/EmonLib@1.1.0" + ], + "esp32cam_4mb": [ + "openenergymonitor/EmonLib@1.1.0" + ], "esp8266_4mb": [ "openenergymonitor/EmonLib@1.1.0" ] diff --git a/src/modules/sensors/ExampleModule/ExampleModule.cpp b/src/modules/sensors/ExampleModule/ExampleModule.cpp new file mode 100644 index 00000000..58be642b --- /dev/null +++ b/src/modules/sensors/ExampleModule/ExampleModule.cpp @@ -0,0 +1,243 @@ +//======================================================================================================= +// Это файл сенсора, в нем осуществляется чтение сенсора. +// для добавления сенсора вам нужно скопировать этот файл и заменить в нем текст ExamleModule на название вашего сенсора +// Название должно быть уникальным, коротким и отражать суть сенсора. + +// Обязательные библиотеки из ядра IoTM +#include "Global.h" +#include "classes/IoTItem.h" + +//! Здесь подключаем стороннюю библиотеку при необходимости (ExternalLibrary заменить) +// #include +#include "ExternalLibrary.h" //удалить, только для примера. Внешние библиотеки правильно в <> + +//! Объяевляем класс IoTGpio для работы с GPIO +extern IoTGpio IoTgpio; + +//========================================================================================================= +//========================================================================================================= +// Объявление сторонней библиотекит с использованием глобавльных объектов +//======================================================================================================= +//! Объявляем стороннюю библиотеку при необходимости (ExternalLibrary заменить) +// !!! ЗДЕСЬ И ДАЛЕЕ libXX НАЗВАТЬ уникальным именем) +ExternalLibrary *libXX = nullptr; + +// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку +// instanceLibXX НАЗВАТЬ по наименованию модуля (instanceДатчикХ) +// ПРИ НЕОБХДИМОСТИ передаем любые нужные параметры для инициализации библиотеки (в данном случае PIN) +// +// !!! ВЫзвать данную функцию нужно хотябы один раз, +// но в каждом конструкторе класса модуля ExampleModule_A, ExampleModule_B и т.д. +// или можно вывывать постоянно при обращении к библиотеке, типа: instanceLibXX().READ_LIB_DATA_OTHER(); +// +// !!!!!! В деструкторах ~ExampleModule_B() и ~ExampleModule_A() надо УДАЛЯТЬ объект libXX, ЕСЛИ в функцию instanceLibXX чтото передается. +// Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку. +// P.S. Не для всех, если используется map или vector то лучше не надо. + +ExternalLibrary *instanceLibXX(int pin) +{ + if (!libXX) + { // Если библиотека ранее инициализировалась, то просто вернем указатель + // Инициализируем библиотеку + libXX = new ExternalLibrary(); + libXX->begin(pin); // При необходимости делаем begin библиотеке + } + return libXX; +} + +//========================================================================================================= +//========================================================================================================= +// Первый класс модуля для определения 1-го элемента (параметра) +// Служит для запроса и отображения парметра/элемента датчика +// IoTManager система модульная: один парметр - один элемент (класс) +// +//========================================================================================================= +//========================================================================================================= +// ребенок - родитель +class ExampleModule_A : public IoTItem +{ +private: + //======================================================================================================= + // Секция переменных. + // Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы + // впоследствии использовать их в loop и setup + unsigned int _pin; + // пользовательская переменная, в данном случае для считывания аналогового сигнала + int adc; + +public: + //======================================================================================================= + // setup() + // это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора. + // Такие как ...begin и подставлять в них параметры полученные из web интерфейса. + // Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции: + // jsonReadStr, jsonReadBool, jsonReadInt + ExampleModule_A(String parameters) : IoTItem(parameters) + { + // Читаем пользовательскую переменную PIN, должна быть объявлена в в modeinfo.json + _pin = jsonReadInt(parameters, "pin"); + // другой вариант чтения парметров модуля + jsonRead(parameters, F("int"), _interval, false); + } + + //======================================================================================================= + // doByInterval() - основная функция периодической работы + // это аналог loop из arduino, но вызываемый каждые int секунд, заданные в настройках. Здесь вы должны выполнить чтение вашего сенсора + // а затем выполнить regEvent - это регистрация произошедшего события чтения + // здесь так же доступны все переменные из секции переменных, и полученные в setup + // если у сенсора несколько величин то делайте несколько regEvent + // не используйте delay - помните, что данный loop общий для всех модулей. Если у вас планируется длительная операция, постарайтесь разбить ее на порции + // и выполнить за несколько тактов + void doByInterval() + { + // Пример получения данных из библиотеки, где READ_LIB_DATA_OTHER - функция Вашей библиотеки + value.valD = instanceLibXX(_pin)->READ_LIB_DATA_OTHER(); + // Здесь Наменование произвольным но понятным к какому модулю относится + regEvent(value.valD, "ExampleModule"); // обязательный вызов хотяб один для регистрации события в ядре IoTM + } + + //======================================================================================================= + // loop(), если не нужно переопределять, удалить. + // полный аналог loop() из arduino. Нужно помнить, что все модули имеют равный поочередный доступ к центральному loop(), поэтому, необходимо следить + // за задержками в алгоритме и не создавать пауз. Кроме того, данная версия перегружает родительскую, поэтому doByInterval() отключается, если + // не повторить механизм расчета интервалов. + void loop() + { + // Пример получения данных с аналоговым датчиком + adc = IoTgpio.analogRead(_pin); + + // Блок вызова doByInterval, так как если определили loop, то сам он не вызовится + IoTItem::loop(); + } + + ~ExampleModule_A() + { + // Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку. + delete libXX; + libXX = nullptr; + }; +}; + +//========================================================================================================= +//========================================================================================================= +// Второй класс модуля для определения 2-го элемента (параметра) +// Делается по аналогии с первым классом, служит для запроса и отображения другого парметра если их несколько с одного датчика +// IoTManager система модульная: один парметр - один элемент (класс) +// +// Содержит описание дополнительных методов onModuleOrder и execute +//========================================================================================================= +//========================================================================================================= +class ExampleModule_B : public IoTItem +{ +private: + // Пользовательские переменные + unsigned int _pin; + +public: + ExampleModule_B(String parameters) : IoTItem(parameters) + { + // Читаем пользовательскую переменную PIN, должна быть объявлена в в modeinfo.json + _pin = jsonReadInt(parameters, "pin"); + // Можно инициализировать библиотеку один раз, а потом используем указатель + instanceLibXX(_pin); + libXX->READ_LIB_DATA_OTHER(); + } + + void doByInterval() + { + // Пример получения данных из библиотеки, где READ_LIB_DATA_OTHER - функция Вашей библиотеки + value.valD = libXX->READ_LIB_DATA_OTHER(); + // Здесь Наменование произвольным но понятным к какому модулю относится + regEvent(value.valD, "ExampleModule"); // обязательный вызов хотяб один для регистрации события в ядре IoTM + } + + //================ обработка кнопок из конфигурации =================== + // Хук (переопределение виртуальной функции) для обработки кнопки (в value будут данные с собственной панели ввода) + // Что бы кнопка была без поля ввода, нужно в modeinfo.json указать "btn-Example": nil + void onModuleOrder(String &key, String &value) + { + if (key == "Example") // название кнопки btn-Example + { + SerialPrint("i", F("Sensor ExampleModule"), "User run calibration " + value); + // ЧТО ТО Делаем + } + } + + //================ обработка команд из сценария=================== + // Хук (переопределение виртуальной функции) для обработки команды из сценария (в param будут даныые переданные в функции в сценарии) + IoTValue execute(String command, std::vector ¶m) + { + if (command == "expampleFunc") + { + if (param.size()) + { + // Забираем данные из другого модуля по его ID + // Если в сценарии передадим id модуля + String value = getItemValue(param[0].valS); + // Что то делаем с этим параметром + return {}; + } + } + else if (command == "expample2") + { + if (param.size() == 2) + { + SerialPrint("i", F("Sensor ExampleModule"), "expample2: " + param[0].valS + ", " + param[1].valS); + // Передаем полученные данные на дальнейшую обработку + // foo(param[0].valS, param[1].valS); + return {}; + } + } + else if (command == "expampleAny") + { + if (param.size() >= 1) + { + int sizeOfParam = param.size(); + for (unsigned int i = 0; i < sizeOfParam; i++) + { + SerialPrint("i", F("Sensor ExampleModule"), "expampleAny: " + param[i].valS); + // Что то делаем с каждым принятым значением + // foo(param[i].valD); + } + } + } + return {}; + } + + void foo(String logid, String value) + { + // Прсото пример кокой-то функции + } + + ~ExampleModule_B() + { + // Удаляем класс библиотеки, а то при переконфигурации в нем не поменяются PIN и дугие параметры передаваемые в библиотеку. + delete libXX; + libXX = nullptr; + }; +}; + +//========================================================================================================= +//========================================================================================================= +// Функция для связи модуля с ядром IoTM +// !!! ЗДЕСЬ getAPI_ИМЯ ИМЯ должно совпадать с "moduleName" из modeinfo.json +// после замены названия сенсора, на функцию можно не обращать внимания +// если сенсор предполагает использование общего объекта библиотеки для нескольких экземпляров сенсора, то в данной функции необходимо предусмотреть +// создание и контроль соответствующих глобальных переменных +//========================================================================================================= +//========================================================================================================= +void *getAPI_ExampleModule(String subtype, String param) +{ + if (subtype == F("ExampleModule_A")) + { // !!! ЗДЕСЬ subtype ДОЛЖЕН СОВПАДАТЬ С subtype из modeinfo.json + return new ExampleModule_A(param); + } + else if (subtype == F("ExampleModule_B")) + { // !!! ЗДЕСЬ subtype ДОЛЖЕН СОВПАДАТЬ С subtype из modeinfo.json + return new ExampleModule_B(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/ExampleModule/ExternalLibrary.h b/src/modules/sensors/ExampleModule/ExternalLibrary.h new file mode 100644 index 00000000..f19737de --- /dev/null +++ b/src/modules/sensors/ExampleModule/ExternalLibrary.h @@ -0,0 +1,38 @@ +#pragma once + +/******************************************************************************* + ** ** + ** УДАЛИТЬ ЭТО ПРОСТО ПРИМЕР БИБЛИОТЕКИ ** + ** ** + *******************************************************************************/ + +class ExternalLibrary +{ +private: + /* data */ +public: + ExternalLibrary(/* args */); + + ~ExternalLibrary(); + void begin (int pin); + float READ_LIB_DATA_OTHER(); +}; + +ExternalLibrary::ExternalLibrary(/* args */) +{ +} + +ExternalLibrary::~ExternalLibrary() +{ +} + +void ExternalLibrary::begin(int pin) +{ + +} + +float ExternalLibrary::READ_LIB_DATA_OTHER() +{ + static float f = 0; + return f++; +} \ No newline at end of file diff --git a/src/modules/sensors/ExampleModule/modinfo.json b/src/modules/sensors/ExampleModule/modinfo.json new file mode 100644 index 00000000..5e2a5dd2 --- /dev/null +++ b/src/modules/sensors/ExampleModule/modinfo.json @@ -0,0 +1,90 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Пример датчика А", + "type": "Reading", + "subtype": "ExampleModule_A", + "id": "Tmp", + "widget": "anydataTmp", + "page": "Сенсоры", + "descr": "Температура", + "int": 15, + "pin": "32", + "round": 1 + }, + { + "global": 0, + "name": "Пример датчика Б", + "type": "Reading", + "subtype": "ExampleModule_B", + "id": "Press", + "widget": "anydataMm", + "page": "Сенсоры", + "descr": "Давление", + "int": 15, + "pin": "32", + "round": 1, + "btn-Example": 100 + } + ], + "about": { + "authorName": "NAME", + "authorContact": "https://t.me/NAME", + "authorGit": "https://github.com/NAME", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "ExampleModule", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Название модуля", + "moduleDesc": "Описание модуля. Что позволяет получить. Особенности работы", + "propInfo": { + "pin": "Аналоговый GPIO номер, к которому подключен датчик.", + "int": "Количество секунд между опросами датчика", + "btn-Example": "Кнопка Example. В поле указать ......" + }, + "funcInfo": [ + { + "name": "expampleFunc", + "descr": "Пример функции вызываемой из сценария. Принимает Id другого модуля и смотрит его значение", + "params": [ + "ID стороннего модуля" + ] + }, + { + "name": "expample2", + "descr": "Второй Пример функции вызываемой из сценария.", + "params": [ + "Описание педедаваемого параметра", + "параметр 2" + ] + }, + { + "name": "expampleAny", + "descr": "Третий Пример функции вызываемой из сценария. С неограниченным числом параметров", + "params": [ + "Описание педедаваемых параметров" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32s2_4mb": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} \ No newline at end of file diff --git a/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp b/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp index 3ad82a54..744381b6 100644 --- a/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp +++ b/src/modules/sensors/ExternalMQTT/ExternalMQTT.cpp @@ -13,16 +13,28 @@ private: int red = 0; int offline = 0; bool dataFromNode = false; + String _topic = ""; + bool _isJson; + bool _debug; + bool sendOk = false; public: ExternalMQTT(String parameters) : IoTItem(parameters) { - _MAC = jsonReadStr(parameters, "MAC"); _sensor = jsonReadStr(parameters, "sensor"); jsonRead(parameters, F("orange"), orange); jsonRead(parameters, F("red"), red); jsonRead(parameters, F("offline"), offline); + _topic = jsonReadStr(parameters, "topic"); + jsonRead(parameters, "isJson", _isJson); + // jsonRead(parameters, "addPrefix", _addPrefix); + jsonRead(parameters, "debug", _debug); dataFromNode = false; + if (mqttIsConnect()) + { + sendOk = true; + mqttSubscribeExternal(_topic); + } } char *TimeToString(unsigned long t) { @@ -38,46 +50,71 @@ public: { if (msg.indexOf("HELLO") == -1) { - - // SerialPrint("i", "onMqttRecive", "Прилетело " + topic); - // SerialPrint("i", "onMqttRecive", "Прилетело " + msg); String dev = selectToMarkerLast(topic, "/"); dev.toUpperCase(); dev.replace(":", ""); - if (_MAC == "") + if (_topic != topic) { - SerialPrint("i", "onMqttRecive", dev + " --> " + msg); - } - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - DeserializationError error = deserializeJson(doc, msg); - if (error) - { - SerialPrint("E", F("onMqttRecive"), error.f_str()); - } - JsonObject jsonObject = doc.as(); - - for (JsonPair kv : jsonObject) - { - String key = kv.key().c_str(); - String val = kv.value(); - if (_MAC == dev && _sensor == key) + if (_debug) { - dataFromNode = true; - _minutesPassed = 0; - setValue(val); - // setNewWidgetAttributes(); + SerialPrint("i", "ExternalMQTT", _id + " not equal: " + topic + " msg: " + msg); } + return; + } - // Serial.println("Key: " + key); - // Serial.println("Value: " + val); + if (_isJson) + { + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + DeserializationError error = deserializeJson(doc, msg); + if (error) + { + SerialPrint("E", F("ExternalMQTT"), error.f_str()); + } + JsonObject jsonObject = doc.as(); + + for (JsonPair kv : jsonObject) + { + String key = kv.key().c_str(); + String val = kv.value(); + if (_debug) + { + SerialPrint("i", "ExternalMQTT", "Received MAC: " + dev + " key=" + key + " val=" + val); + } + if (_sensor == key) + { + dataFromNode = true; + _minutesPassed = 0; + setValue(val); + } + } + } + else + { + if (_debug) + { + SerialPrint("i", "ExternalMQTT", "Received MAC: " + dev + " val=" + msg); + } + dataFromNode = true; + _minutesPassed = 0; + setValue(msg); } } } + String getMqttExterSub() + { + return _topic; + } + void doByInterval() { _minutesPassed++; setNewWidgetAttributes(); + if (mqttIsConnect() && !sendOk) + { + sendOk = true; + mqttSubscribeExternal(_topic); + } } void onMqttWsAppConnectEvent() { @@ -107,6 +144,7 @@ public: if (_minutesPassed >= offline) { jsonWriteStr(json, F("info"), F("offline")); + SerialPrint("i", "ExternalMQTT", _id + " - offline"); } } } @@ -116,7 +154,6 @@ public: } sendSubWidgetsValues(_id, json); } - ~ExternalMQTT(){}; }; diff --git a/src/modules/sensors/ExternalMQTT/modinfo.json b/src/modules/sensors/ExternalMQTT/modinfo.json index 02c41fb3..b1ed1b11 100644 --- a/src/modules/sensors/ExternalMQTT/modinfo.json +++ b/src/modules/sensors/ExternalMQTT/modinfo.json @@ -1,6 +1,5 @@ { - "menuSection": "Сенсоры", - + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -11,23 +10,24 @@ "widget": "", "page": "", "descr": "", - "MAC": "", "sensor": "", + "topic": "", + "isJson": 1, "round": "", "orange": 60, "red": 120, "offline": 180, - "int": 60 + "int": 60, + "debug": 0 } ], - "about": { "authorName": "AVAKS", - "authorContact": "https://t.me/@avaks_dev", + "authorContact": "https://t.me/@avaks", "authorGit": "https://github.com/avaksru", "specialThanks": "", "moduleName": "ExternalMQTT", - "moduleVersion": "1", + "moduleVersion": "1.2", "usedRam": { "esp32_4mb": 15, "esp8266_4mb": 15 @@ -40,15 +40,20 @@ "orange": "количество минут после которого окрасить виджет в оранжевый цвет", "red": "количество минут после которого окрасить виджет в красный цвет", "offline": "количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится", - "MAC": "MAC адрес беспроводного датчика", - "sensor": "Тип сенсора: температура / влажность / время / ... " + "sensor": "Тип сенсора: температура / влажность / время / ... Он же ключ в json пакете ", + "topic":"Подписаться на произвольный топик (полный путь), в модуле данный топик буде проверяться с отправителем, например homed/fd/zigbee/temp", + "addPrefix":"1 (число), будет добавлен стандартный префикс из настроек, 0 - не добавлять префикс", + "isJson":"1 - ожидаем в топике json, 0 - в топике просто значение (при 0 поле sensor заполнять не требуется)", + "debug":"1 - выводить дополнительный лог в сериал" } + }, - "defActive": false, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32s2_4mb": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], @@ -57,4 +62,4 @@ "esp8266_2mb": [], "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/sensors/FreqMeter/modinfo.json b/src/modules/sensors/FreqMeter/modinfo.json index 91eb8c82..73fb8007 100644 --- a/src/modules/sensors/FreqMeter/modinfo.json +++ b/src/modules/sensors/FreqMeter/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -87,6 +87,12 @@ "esp32_4mb": [ "kosme/arduinoFFT@^1.5.6" ], + "esp32_4mb3f": [ + "kosme/arduinoFFT@^1.5.6" + ], + "esp32cam_4mb": [ + "kosme/arduinoFFT@^1.5.6" + ], "esp8266_4mb": [ "kosme/arduinoFFT@^1.5.6" ] diff --git a/src/modules/sensors/GY21/modinfo.json b/src/modules/sensors/GY21/modinfo.json index affd1e44..aa80c3d4 100644 --- a/src/modules/sensors/GY21/modinfo.json +++ b/src/modules/sensors/GY21/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -52,6 +52,12 @@ "esp32_4mb": [ "https://github.com/JonasGMorsch/GY-21.git" ], + "esp32_4mb3f": [ + "https://github.com/JonasGMorsch/GY-21.git" + ], + "esp32cam_4mb": [ + "https://github.com/JonasGMorsch/GY-21.git" + ], "esp8266_4mb": [ "https://github.com/JonasGMorsch/GY-21.git" ] diff --git a/src/modules/sensors/Hdc1080/modinfo.json b/src/modules/sensors/Hdc1080/modinfo.json index 5b27c795..574a8331 100644 --- a/src/modules/sensors/Hdc1080/modinfo.json +++ b/src/modules/sensors/Hdc1080/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -55,6 +55,12 @@ "esp32_4mb": [ "ClosedCube HDC1080" ], + "esp32_4mb3f": [ + "ClosedCube HDC1080" + ], + "esp32cam_4mb": [ + "ClosedCube HDC1080" + ], "esp8266_4mb": [ "ClosedCube HDC1080" ] diff --git a/src/modules/sensors/Hx710/modinfo.json b/src/modules/sensors/Hx710/modinfo.json index 985c4457..4a2bf5c7 100644 --- a/src/modules/sensors/Hx710/modinfo.json +++ b/src/modules/sensors/Hx710/modinfo.json @@ -1,22 +1,22 @@ -{ - "menuSection": "Сенсоры", - - "configItem": [{ - "name": "HX710 Cенсор давления", - "type": "Reading", - "subtype": "Hx710", - "id": "hxp", - "widget": "anydataDef", - "page": "Давление", - "descr": "HX press", - "int": 15, - "plus": 0, - "multiply": 1, - "round": 1, - "data": 14, - "clock": 15 - }], - +{ + "menuSection": "sensors", + "configItem": [ + { + "name": "HX710 Cенсор давления", + "type": "Reading", + "subtype": "Hx710", + "id": "hxp", + "widget": "anydataDef", + "page": "Давление", + "descr": "HX press", + "int": 15, + "plus": 0, + "multiply": 1, + "round": 1, + "data": 14, + "clock": 15 + } + ], "about": { "authorName": "Serghei Crasnicov", "authorContact": "https://t.me/Serghei63", @@ -44,13 +44,17 @@ } ] }, - "defActive": false, - "usedLibs": { "esp32_4mb": [ "https://github.com/kurimawxx00/hx710B_pressure_sensor" ], + "esp32_4mb3f": [ + "https://github.com/kurimawxx00/hx710B_pressure_sensor" + ], + "esp32cam_4mb": [ + "https://github.com/kurimawxx00/hx710B_pressure_sensor" + ], "esp8266_4mb": [ "https://github.com/kurimawxx00/hx710B_pressure_sensor" ] diff --git a/src/modules/sensors/Hx711/modinfo.json b/src/modules/sensors/Hx711/modinfo.json index 4627711a..d3713615 100644 --- a/src/modules/sensors/Hx711/modinfo.json +++ b/src/modules/sensors/Hx711/modinfo.json @@ -1,24 +1,24 @@ -{ - "menuSection": "Сенсоры", - - "configItem": [{ - "name": "HX711 Cенсор весов", - "type": "Reading", - "subtype": "Hx711", - "id": "hx", - "widget": "anydataDef", - "page": "Весы", - "descr": "HX вес", - "int": 15, - "map": "1024,1024,1,100", - "plus": 0, - "multiply": 1, - "round": 1, - "data": 3, - "clock": 2, - "chan": 2 - }], - +{ + "menuSection": "sensors", + "configItem": [ + { + "name": "HX711 Cенсор весов", + "type": "Reading", + "subtype": "Hx711", + "id": "hx", + "widget": "anydataDef", + "page": "Весы", + "descr": "HX вес", + "int": 15, + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 1, + "data": 3, + "clock": 2, + "chan": 2 + } + ], "about": { "authorName": "Serghei Crasnicov", "authorContact": "https://t.me/Serghei63", @@ -48,7 +48,9 @@ { "name": "sleepMode", "descr": "Перевести в режим сна", - "params": ["=1 режим сна, =0 проснуться"] + "params": [ + "=1 режим сна, =0 проснуться" + ] }, { "name": "read", @@ -57,13 +59,17 @@ } ] }, - "defActive": false, - "usedLibs": { "esp32_4mb": [ "GyverHX711@1.2" ], + "esp32_4mb3f": [ + "GyverHX711@1.2" + ], + "esp32cam_4mb": [ + "GyverHX711@1.2" + ], "esp8266_4mb": [ "GyverHX711@1.2" ] diff --git a/src/modules/sensors/Impulse/modinfo.json b/src/modules/sensors/Impulse/modinfo.json index e35ecf29..4fa98b2a 100644 --- a/src/modules/sensors/Impulse/modinfo.json +++ b/src/modules/sensors/Impulse/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -41,6 +41,8 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/Ina219/Ina219.cpp b/src/modules/sensors/Ina219/Ina219.cpp index 6ab65897..ec05a6c8 100644 --- a/src/modules/sensors/Ina219/Ina219.cpp +++ b/src/modules/sensors/Ina219/Ina219.cpp @@ -1,136 +1,242 @@ /****************************************************************** - Used Adafruit INA219 Current Sensor - Support for INA219 - https://github.com/adafruit/Adafruit_INA219 + Used GyverINA Current Sensor + Support for INA219 INA226 + https://github.com/GyverLibs/GyverINA - adapted for version 4dev @Serghei63 + adapted for version 4dev @Mit4bmw ******************************************************************/ #include "Global.h" #include "classes/IoTItem.h" #include -#include +#include +#include -Adafruit_INA219 ina219; - float shuntvoltage = 0; - float busvoltage = 0; - float current_mA = 0; - float loadvoltage = 0; - float power_mW = 0; - // shuntvoltage = ina219.getShuntVoltage_mV(); // Получение напряжение на шунте - // busvoltage = ina219.getBusVoltage_V(); // Получение значение напряжения V - // current_mA = ina219.getCurrent_mA(); // Получение значение тока в мА - // power_mW = ina219.getPower_mW(); // Получение значение мощности - // loadvoltage = busvoltage + (shuntvoltage / 1000); // Расчет напряжение на нагрузки - - class Ina219loadvoltage : public IoTItem { - public: - Ina219loadvoltage(String parameters) : IoTItem(parameters) { - - // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 - } - - void doByInterval() { - - loadvoltage = busvoltage + (shuntvoltage / 1000); - value.valD = loadvoltage; - - regEvent(value.valD, "Ina219loadvoltage"); - } - - ~Ina219loadvoltage(){}; +// Структура для хранения настроек датчика +struct Ina219Value +{ + float shunt = 0; + float maxV = 0; }; - class Ina219busvoltage : public IoTItem { - public: - Ina219busvoltage(String parameters) : IoTItem(parameters) { +// глобальные списки необходимы для хранения Модуля Настроек. Ключ - адрес +std::map ina219SettingArray; - // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 +// глобальные списки необходимы для хранения объектов используемых разными датчиками из модуля. Ключ - адрес +std::map ina219Array; + +// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку +INA219 *instanceIna219(uint8_t ADDR) +{ + /** default I2C address **/ + if (ADDR == 0) + ADDR = 0x40; // 1000000 (A0+A1=GND) + + // учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась + if (ina219Array.find(ADDR) == ina219Array.end()) + { + if (ina219SettingArray.find(ADDR) != ina219SettingArray.end()) + ina219Array[ADDR] = new INA219(ina219SettingArray[ADDR]->shunt, ina219SettingArray[ADDR]->maxV, (uint8_t)ADDR); + else + ina219Array[ADDR] = new INA219(0.1f, 3.2f, (uint8_t)ADDR); // Стандартные значения для модуля INA219 (0.1 Ом, 3.2А, адрес 0x40) + ina219Array[ADDR]->begin(); + // ina219ValueArray[ADDR] = new Ina219Value; + } + return ina219Array[ADDR]; +} + +class Ina219voltage : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina219voltage(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); } - void doByInterval() { - - busvoltage = ina219.getBusVoltage_V(); - value.valD = busvoltage; - - regEvent(value.valD, "Ina219busvoltage"); + void doByInterval() + { + regEvent(instanceIna219(_addr)->getVoltage(), "Ina219voltage"); } - ~Ina219busvoltage(){}; + ~Ina219voltage(){}; }; +class Ina219shuntvoltage : public IoTItem +{ +private: + uint8_t _addr = 0; -class Ina219curr : public IoTItem { - public: - Ina219curr(String parameters) : IoTItem(parameters) { - - // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 +public: + Ina219shuntvoltage(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); } - void doByInterval() { - - current_mA = ina219.getCurrent_mA(); - value.valD = current_mA; - - regEvent(value.valD, "Ina219curr"); - } - - ~Ina219curr(){}; -}; - -class Ina219shuntvoltage : public IoTItem { - public: - Ina219shuntvoltage(String parameters) : IoTItem(parameters) { - - // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 - } - void doByInterval() { - - shuntvoltage = ina219.getShuntVoltage_mV(); - value.valD = shuntvoltage; - - regEvent(value.valD, "Ina219shuntvoltage"); + void doByInterval() + { + regEvent(instanceIna219(_addr)->getShuntVoltage(), "Ina219shuntvoltage"); } ~Ina219shuntvoltage(){}; }; -class Power_mW : public IoTItem { - public: - Power_mW(String parameters) : IoTItem(parameters) { +class Ina219curr : public IoTItem +{ +private: + uint8_t _addr = 0; - // Wire.begin(2,0); // Инициализация шины I2C для модуля E01 +public: + Ina219curr(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); } - void doByInterval() { - - power_mW = ina219.getPower_mW(); - value.valD = power_mW; - - regEvent(value.valD, "Ina219power"); // TODO: найти способ понимания ошибки получения данных - + void doByInterval() + { + regEvent(instanceIna219(_addr)->getCurrent(), "Ina219curr"); } - ~Power_mW(){}; + ~Ina219curr(){}; }; -void* getAPI_Ina219(String subtype, String param) { - if (subtype == F("Ina219curr")) { - ina219.begin(); + +class Ina219Power : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina219Power(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + } + void doByInterval() + { + regEvent(instanceIna219(_addr)->getPower(), "Ina219power"); // TODO: найти способ понимания ошибки получения данных + } + + ~Ina219Power(){}; +}; + +class Ina219Setting : public IoTItem +{ +private: + uint8_t _addr = 0; + int adjClbr = 0; + int resol = 1; + + +public: + Ina219Setting(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + jsonRead(parameters, "adjClbr", adjClbr); + jsonRead(parameters, "resol", resol); + + + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + + ina219SettingArray[_addr] = new Ina219Value; + jsonRead(parameters, "shunt", ina219SettingArray[_addr]->shunt); + jsonRead(parameters, "maxV", ina219SettingArray[_addr]->maxV); + + instanceIna219(_addr)->adjCalibration(adjClbr); + if (resol == 1) + resol = 0b0011; + else + resol += 0b0111; + + instanceIna219(_addr)->setResolution(INA219_VBUS, resol); // Напряжение в 12ти битном режиме + instanceIna219(_addr)->setResolution(INA219_VSHUNT, resol); // Ток в 12ти битном режиме + } + + void onModuleOrder(String &key, String &value) + { + if (key == "getClbr") + { + SerialPrint("i", F("Ina219"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna219(_addr)->getCalibration()); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "sleep") + { + if (param.size() == 1) + { + if (param[0].valD == 0) + instanceIna219(_addr)->sleep(false); + if (param[0].valD == 1) + instanceIna219(_addr)->sleep(true); + return {}; + } + } + /* + else if (command == "getCalibration") + { + SerialPrint("i", F("Ina219"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna219(_addr)->getCalibration()); + return {}; + }*/ + return {}; + } + + ~Ina219Setting(){}; +}; + +void *getAPI_Ina219(String subtype, String param) +{ + if (subtype == F("Ina219curr")) + { return new Ina219curr(param); - } else if (subtype == F("Ina219shuntvoltage")) { - ina219.begin(); + } + else if (subtype == F("Ina219shuntvoltage")) + { return new Ina219shuntvoltage(param); - } else if (subtype == F("power_mW")) { - ina219.begin(); - return new Power_mW(param); - } else if (subtype == F("Ina219busvoltage")) { - ina219.begin(); - return new Ina219busvoltage(param); - } else if (subtype == F("Ina219loadvoltage")) { - ina219.begin(); - return new Ina219loadvoltage(param); - } else { - return nullptr; + } + else if (subtype == F("Ina219power")) + { + return new Ina219Power(param); + } + else if (subtype == F("Ina219voltage")) + { + return new Ina219voltage(param); + } + else if (subtype == F("Ina219setting")) + { + return new Ina219Setting(param); + // Ina219Setting *ptr = new Ina219Setting(param); + // ina219SettingArray[ptr->getAddr()] = ptr; + // return ptr; + } + else + { + return nullptr; } } diff --git a/src/modules/sensors/Ina219/modinfo.json b/src/modules/sensors/Ina219/modinfo.json index 1dc9638f..f9694297 100644 --- a/src/modules/sensors/Ina219/modinfo.json +++ b/src/modules/sensors/Ina219/modinfo.json @@ -1,99 +1,141 @@ { - "menuSection": "Сенсоры", - - "configItem": [{ - "global": 0, - "name": "INA219 Tок", - "type": "Reading", - "subtype": "Ina219curr", - "id": "Ina219current", - "widget": "anydatamAmp", - "page": "INA 219", - "descr": "219 Датчик тока", - "int": 10 - }, - { - "global": 0, - "name": "INA219 Напряжение", - "type": "Reading", - "subtype": "Ina219busvoltage", - "id": "Ina219busvoltage", - "widget": "anydataVlt", - "page": "INA 219", - "descr": "219 Датчик напряжения", - "int": 10 - }, - { - "global": 0, - "name": "INA219 Мощность", - "type": "Reading", - "subtype": "power_mW", - "id": "Ina219power", - "widget": "anydatamWtt", - "page": "INA 219", - "descr": "219 Мощность", - "int": 10 - }, - { - "global": 0, - "name": "INA219 Напряжение нагрузки", - "type": "Reading", - "subtype": "Ina219loadvoltage", - "id": "Ina219loadvoltage", - "widget": "anydataVlt", - "page": "INA 219", - "descr": "219 Напряжение нагрузки", - "int": 10 - }, - { - "global": 0, - "name": "INA219 Шунт", - "type": "Reading", - "subtype": "Ina219shuntvoltage", - "id": "Ina219shuntvoltage", - "widget": "anydatamVlt", - "page": "INA 219", - "descr": "219 Напряжение шунта", - "int": 10 - }], - - "about": { - "authorName": "Serghei Crasnicov", - "authorContact": "https://t.me/Serghei63", - "authorGit": "https://github.com/Serghei63", - "specialThanks": "Дмитрий , Serg", - "moduleName": "Ina219", - "moduleVersion": "1.0", - "usedRam": { - "esp32_4mb": 15, - "esp8266_4mb": 15 - }, - "subTypes": [ - "Ina219curr", - "Ina219busvoltage", - "power_mW", - "Ina219loadvoltage", - "Ina219shuntvoltage" - ], - "title": "Милливатметр постоянного тока", - "moduleDesc": "Измеряет постоянный ток до 3.2 ампера, напряжение до 26 вольт и мощность на нагрузке", - "propInfo": { - "int": "Количество секунд между опросами датчика." - } + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "INA219 Tок", + "type": "Reading", + "subtype": "Ina219curr", + "id": "ina219_A", + "widget": "anydataAmp", + "page": "INA 219", + "descr": "Сила тока", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 }, - - "defActive": false, - - "usedLibs": { - "esp32_4mb": [ - "https://github.com/adafruit/Adafruit_INA219.git" - ], - - "esp8266_4mb": [ - "https://github.com/adafruit/Adafruit_INA219.git" - ] + { + "global": 0, + "name": "INA219 Напряжение", + "type": "Reading", + "subtype": "Ina219voltage", + "id": "ina219_V", + "widget": "anydataVlt", + "page": "INA 219", + "descr": "Напряжения", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA219 Мощность", + "type": "Reading", + "subtype": "Ina219power", + "id": "ina219_W", + "widget": "anydataWt", + "page": "INA 219", + "descr": "Мощность", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA219 Шунт", + "type": "Reading", + "subtype": "Ina219shuntvoltage", + "id": "ina219_Vsh", + "widget": "anydataVlt", + "page": "INA 219", + "descr": "Напряжение шунта", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA219 Настройки", + "type": "Reading", + "subtype": "Ina219setting", + "id": "ina219_set", + "widget": "nil", + "page": "", + "descr": "", + "addr": "0x40", + "shunt": 0.1, + "maxV": 3.2, + "adjClbr": 0, + "resol": 4, + "btn-getClbr": "nil" } - - } - - \ No newline at end of file + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Дмитрий, Serg, v2.0 - Mitchel @Mit4bmw", + "moduleName": "Ina219", + "moduleVersion": "2.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "Ina219curr", + "Ina219voltage", + "Ina219power", + "Ina219shuntvoltage", + "Ina219setting" + ], + "title": "Милливатметр постоянного тока", + "moduleDesc": "Измеряет постоянный ток до 3.2 ампера, напряжение до 26 вольт и мощность на нагрузке. Модуль INA219 Настройки - для изменении настроек нужен постоянно в конфигурации, должен стоять перед рдугими модулями с тем же адресом, без него работает на значенях по умолчанию", + "propInfo": { + "int": "Количество секунд между опросами датчика.", + "addr": "Адрес датчика на шине, обычно 0x40. Если оставить поле пустым, то запуститься сканер I2C и подключение к адресу 0x40", + "shunt": "Сопротивление шунта, штатно 0.1Ом. Изменить если его перепаяли", + "maxV": "Максимальный ожидаемый ток, штатно 3.2А, для указаного шунта", + "adjClbr": "Задать смещение (подкрутить) калибровочное значение на указанное значение. -20 = Уменьшить калибровочное значение на 20", + "resol": "Установка режима усреднения для измерения напряжения и тока, рекомендуется для повышения стабильности показаний на шумной нагрузке. Варианты 1(без усреднения),2,4,8,16,32,64,128", + "btn-getClbr": "Кнопка запроса текущей калибровки, выводится в лог" + }, + "funcInfo": [ + { + "name": "sleep", + "descr": "INA219 Настройки. Установка / снятие режима сна датчика INA219", + "params": [ + "1- вкл сна/ 0-выкл сна" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32_4mb3f": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32cam_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32s2_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp8266_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp8266_16mb": [ + "https://github.com/GyverLibs/GyverINA" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Ina226/Ina226.cpp b/src/modules/sensors/Ina226/Ina226.cpp new file mode 100644 index 00000000..1cbd8c51 --- /dev/null +++ b/src/modules/sensors/Ina226/Ina226.cpp @@ -0,0 +1,237 @@ +/****************************************************************** + Used GyverINA Current Sensor + Support for INA219 INA226 + https://github.com/GyverLibs/GyverINA + + adapted for version 4dev @Mit4bmw + ******************************************************************/ + +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include +#include + + + +// Структура для хранения настроек датчика +struct Ina226Value +{ + float shunt = 0; + float maxV = 0; +}; + +// глобальные списки необходимы для хранения Модуля Настроек. Ключ - адрес +std::map ina226SettingArray; + +// глобальные списки необходимы для хранения объектов используемых разными датчиками из модуля. Ключ - адрес +std::map ina226Array; + +// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку +INA226 *instanceIna226(uint8_t ADDR) +{ + /** default I2C address **/ + if (ADDR == 0) + ADDR = 0x40; // 1000000 (A0+A1=GND) + + // учитываем, что библиотека может работать с несколькими линиями на разных пинах, поэтому инициируем библиотеку, если линия ранее не использовалась + if (ina226Array.find(ADDR) == ina226Array.end()) + { + if (ina226SettingArray.find(ADDR) != ina226SettingArray.end()) + ina226Array[ADDR] = new INA226(ina226SettingArray[ADDR]->shunt, ina226SettingArray[ADDR]->maxV, (uint8_t)ADDR); + else + ina226Array[ADDR] = new INA226(0.1f, 0.8f, (uint8_t)ADDR); // Стандартные значения для модуля INA226 (0.1 Ом, 0.8А, адрес 0x40) + ina226Array[ADDR]->begin(); + // ina226ValueArray[ADDR] = new Ina226Value; + } + return ina226Array[ADDR]; +} + +class Ina226voltage : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina226voltage(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + } + + void doByInterval() + { + regEvent(instanceIna226(_addr)->getVoltage(), "Ina226voltage"); + } + + ~Ina226voltage(){}; +}; + +class Ina226shuntvoltage : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina226shuntvoltage(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + } + void doByInterval() + { + regEvent(vinstanceIna226(_addr)->getShuntVoltage(), "Ina226shuntvoltage"); + } + + ~Ina226shuntvoltage(){}; +}; + +class Ina226curr : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina226curr(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + } + void doByInterval() + { + regEvent(instanceIna226(_addr)->getCurrent(), "Ina226curr"); + } + + ~Ina226curr(){}; +}; + +class Ina226Power : public IoTItem +{ +private: + uint8_t _addr = 0; + +public: + Ina226Power(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + } + void doByInterval() + { + regEvent(instanceIna226(_addr)->getPower(), "Ina226power"); // TODO: найти способ понимания ошибки получения данных + } + + ~Ina226Power(){}; +}; + +class Ina226Setting : public IoTItem +{ +private: + uint8_t _addr = 0; + int adjClbr = 0; + int resol = 1; + + +public: + Ina226Setting(String parameters) : IoTItem(parameters) + { + String sAddr; + jsonRead(parameters, "addr", sAddr); + jsonRead(parameters, "adjClbr", adjClbr); + jsonRead(parameters, "resol", resol); + + + if (sAddr == "") + scanI2C(); + else + _addr = hexStringToUint8(sAddr); + + ina226SettingArray[_addr] = new Ina226Value; + jsonRead(parameters, "shunt", ina226SettingArray[_addr]->shunt); + jsonRead(parameters, "maxV", ina226SettingArray[_addr]->maxV); + + instanceIna226(_addr)->adjCalibration(adjClbr); + + instanceIna226(_addr)->setAveraging(resol); // Напряжение в 12ти битном режиме + } + + void onModuleOrder(String &key, String &value) + { + if (key == "getClbr") + { + SerialPrint("i", F("Ina226"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna226(_addr)->getCalibration()); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "sleep") + { + if (param.size() == 1) + { + if (param[0].valD == 0) + instanceIna226(_addr)->sleep(false); + if (param[0].valD == 1) + instanceIna226(_addr)->sleep(true); + return {}; + } + } + /* + else if (command == "getCalibration") + { + SerialPrint("i", F("Ina226"), "addr: " + String(_addr) + ", Value Calibration:" + instanceIna226(_addr)->getCalibration()); + return {}; + }*/ + return {}; + } + + ~Ina226Setting(){}; +}; + +void *getAPI_Ina226(String subtype, String param) +{ + if (subtype == F("Ina226curr")) + { + return new Ina226curr(param); + } + else if (subtype == F("Ina226shuntvoltage")) + { + return new Ina226shuntvoltage(param); + } + else if (subtype == F("Ina226power")) + { + return new Ina226Power(param); + } + else if (subtype == F("Ina226voltage")) + { + return new Ina226voltage(param); + } + else if (subtype == F("Ina226setting")) + { + return new Ina226Setting(param); + // Ina226Setting *ptr = new Ina226Setting(param); + // ina226SettingArray[ptr->getAddr()] = ptr; + // return ptr; + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/Ina226/modinfo.json b/src/modules/sensors/Ina226/modinfo.json new file mode 100644 index 00000000..5a8f7f15 --- /dev/null +++ b/src/modules/sensors/Ina226/modinfo.json @@ -0,0 +1,141 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "INA226 Tок", + "type": "Reading", + "subtype": "Ina226curr", + "id": "ina226_A", + "widget": "anydataAmp", + "page": "INA 226", + "descr": "Сила тока", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA226 Напряжение", + "type": "Reading", + "subtype": "Ina226voltage", + "id": "ina226_V", + "widget": "anydataVlt", + "page": "INA 226", + "descr": "Напряжения", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA226 Мощность", + "type": "Reading", + "subtype": "Ina226power", + "id": "ina226_W", + "widget": "anydataWt", + "page": "INA 226", + "descr": "Мощность", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA226 Шунт", + "type": "Reading", + "subtype": "Ina226shuntvoltage", + "id": "ina226_Vsh", + "widget": "anydataVlt", + "page": "INA 226", + "descr": "Напряжение шунта", + "addr": "0x40", + "plus": 0, + "multiply": 1, + "round": 3, + "int": 10 + }, + { + "global": 0, + "name": "INA226 Настройки", + "type": "Reading", + "subtype": "Ina226setting", + "id": "ina226_set", + "widget": "nil", + "page": "", + "descr": "", + "addr": "0x40", + "shunt": 0.1, + "maxV": 3.2, + "adjClbr": 0, + "resol": 4, + "btn-getClbr": "nil" + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "v2.0 - Mitchel @Mit4bmw", + "moduleName": "Ina226", + "moduleVersion": "2.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "Ina226curr", + "Ina226voltage", + "Ina226power", + "Ina226shuntvoltage", + "Ina226setting" + ], + "title": "Милливатметр постоянного тока", + "moduleDesc": "Стандартные значения для модуля INA226 (Сопротивление шунта - 0.1 Ом, Максимальный ожидаемый ток - 0.8 А, Адрес на шине I2c - 0x40). Модуль INA226 Настройки - для изменении настроек нужен постоянно в конфигурации, должен стоять перед рдугими модулями с тем же адресом, без него работает на значенях по умолчанию", + "propInfo": { + "int": "Количество секунд между опросами датчика.", + "addr": "Адрес датчика на шине, обычно 0x40. Если оставить поле пустым, то запуститься сканер I2C и подключение к адресу 0x40", + "shunt": "Сопротивление шунта, штатно 0.1Ом. Изменить если его перепаяли", + "maxV": "Максимальный ожидаемый ток, штатно 0.8А, для указаного шунта", + "adjClbr": "Задать смещение (подкрутить) калибровочное значение на указанное значение. -20 = Уменьшить калибровочное значение на 20", + "resol": "Установка режима усреднения (колическва замеров) для измерения напряжения и тока, рекомендуется для повышения стабильности показаний на шумной нагрузке. Пропорционально увеличивает время оцифровки. Варианты 0(без усреднения), от 1 до 7 - соответстввует 4,16,64,128,256,512,1024", + "btn-getClbr": "Кнопка запроса текущей калибровки, выводится в лог" + }, + "funcInfo": [ + { + "name": "sleep", + "descr": "INA226 Настройки. Установка / снятие режима сна датчика INA226", + "params": [ + "1- вкл сна/ 0-выкл сна" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32_4mb3f": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32cam_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp32s2_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp8266_4mb": [ + "https://github.com/GyverLibs/GyverINA" + ], + "esp8266_16mb": [ + "https://github.com/GyverLibs/GyverINA" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/IoTWiegand/modinfo.json b/src/modules/sensors/IoTWiegand/modinfo.json index ff7457e2..f91afe00 100644 --- a/src/modules/sensors/IoTWiegand/modinfo.json +++ b/src/modules/sensors/IoTWiegand/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -39,6 +39,12 @@ "esp32_4mb": [ "https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino" ], + "esp32_4mb3f": [ + "https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino" + ], + "esp32cam_4mb": [ + "https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino" + ], "esp8266_4mb": [ "https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino" ] diff --git a/src/modules/sensors/Max6675/modinfo.json b/src/modules/sensors/Max6675/modinfo.json index 69a61a32..469bbef1 100644 --- a/src/modules/sensors/Max6675/modinfo.json +++ b/src/modules/sensors/Max6675/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -41,6 +41,9 @@ "esp32_4mb": [ "adafruit/MAX6675 library" ], + "esp32_4mb3f": [ + "adafruit/MAX6675 library" + ], "esp8266_4mb": [ "adafruit/MAX6675 library" ] diff --git a/src/modules/sensors/Mhz19/modinfo.json b/src/modules/sensors/Mhz19/modinfo.json index 638fe131..3eacecaf 100644 --- a/src/modules/sensors/Mhz19/modinfo.json +++ b/src/modules/sensors/Mhz19/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -73,11 +73,17 @@ "ABC": "Автокалибровка. По умолчанию включена. Раз в сутки на 20 мин. надо выставлять на свежий воздух.", "pin": "пин получения значений по ШИМ. Esp8266: GPIO 15 - D8, ESP32: GPIO 21, > MHZ19: PWM желтый провод", "maxRetriesNotAvailable": "Максимальное количество попыток опроса сенсора по ШИМ. (может задерживать контроллер)" - } + }, + "title": "Mhz19 Датчик уровеня концентрации CO2" }, "defActive": false, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32s2_4mb": [ + "plerup/EspSoftwareSerial" + ], + "esp32cam_4mb": [], "esp8266_4mb": [] } } \ No newline at end of file diff --git a/src/modules/sensors/Ntc/Ntc.cpp b/src/modules/sensors/Ntc/Ntc.cpp new file mode 100644 index 00000000..09354a66 --- /dev/null +++ b/src/modules/sensors/Ntc/Ntc.cpp @@ -0,0 +1,75 @@ +/****************************************************************** + Simple library for NTC thermistors + https://github.com/GyverLibs/GyverNTC + + adapted for version 4 @Serghei63 + ******************************************************************/ + + +#include "Global.h" +#include "classes/IoTItem.h" + + +class NTCt : public IoTItem { + private: + + unsigned int _pin; + float R1 = 10000.0; // voltage divider resistor value + float Beta = 3950.0; // Beta value + float To = 25.; // Temperature Celsius + float Ro = 10000.0; // Resistance of Thermistor at 25 degree Celsius + float adcMax = 1023.0; + float Vs = 3.3; + public: + NTCt(String parameters): IoTItem(parameters) { + + _pin = jsonReadInt(parameters, "pin"); + R1 = jsonReadInt(parameters, "R1"); // voltage divider resistor value + Beta = jsonReadInt(parameters, "Beta"); // Beta value + To = jsonReadInt(parameters, "T0"); // Temperature degree Celsius + Ro = jsonReadInt(parameters, "R0"); // Resistance of Thermistor at 25 degree Celsius + Vs = jsonReadInt(parameters, "Vs"); // Resistance of Thermistor at 25 degree Celsius + To = To+273.15; + +#if defined ESP8266 + adcMax = 1023.0; +#elif defined ESP32 + adcMax = 4095.0; +#endif + + } + + void doByInterval() { + + float Vout, Rt = 0; + float T = 0; + float adc = 0; + for (int i = 0; i < 10; i++) + { + adc += IoTgpio.analogRead(_pin); + } + adc /= 10; + + Vout = adc * Vs/adcMax; + Rt = R1 * Vout / (Vs - Vout); + T = 1/(1/To + log(Rt/Ro)/Beta); // Temperature in Kelvin + value.valD = T - 273.15; // Celsius + + SerialPrint("i", F("Ntc"), "adc = " + String(adc)+ " ,Vout = " + String(Vout)+ " ,T = " + String(value.valD)); + if (String(value.valD) != "nan") regEvent(value.valD, "Ntc"); + else + SerialPrint("E", "Ntc", "Error"); + } + + ~NTCt() {}; +}; + +void* getAPI_Ntc(String subtype, String param) { + if (subtype == F("Ntc")) { + + return new NTCt(param); + + } else { + return nullptr; + } +} diff --git a/src/modules/sensors/Ntc/modinfo.json b/src/modules/sensors/Ntc/modinfo.json new file mode 100644 index 00000000..6f0b1ce4 --- /dev/null +++ b/src/modules/sensors/Ntc/modinfo.json @@ -0,0 +1,64 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Cенсор температуры NTC", + "type": "Reading", + "subtype": "Ntc", + "id": "Ntctmp", + "widget": "anydataTmp", + "page": "Сенсоры", + "descr": "NTC Температура", + "needSave": 0, + "val": "0", + "int": 15, + "pin": "35", + "R1": "10000", + "R0": "10000", + "Beta": "3950.0", + "T0": "25", + "Vs": "3.3", + "round": 1 + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "https://t.me/Mit4bmw", + "moduleName": "Ntc", + "moduleVersion": "1.1", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "Ntc" + ], + "title": "Модуль датчика Ntc", + "moduleDesc": "", + "retInfo": "", + "propInfo": { + "pin": "Аналоговый пин (для esp8266 = 0, для esp32 алаоговый gpio, например 35)", + "R1": "Сопротивление подтягивающего резистора, должен быть равен сопротивлению термистера", + "Vs": "Напряжение питания датчика, Для точности измерить и ввести своё, по умолчанию 3.3В", + "R0": "Сопротивление термистора при температуре То, например 10 КОм при 25С", + "T0": "Базовая температура, температура измерения сопротивление термистора (Rterm), обычно 25С", + "Beta": "Beta термистора" + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Pzem004t/modinfo.json b/src/modules/sensors/Pzem004t/modinfo.json index 697015f9..68f6299b 100644 --- a/src/modules/sensors/Pzem004t/modinfo.json +++ b/src/modules/sensors/Pzem004t/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -115,8 +115,8 @@ "Pzem004pf", "Pzem004cmd" ], - "title": "Счетчик электроэнергии PZEM 004 t версии 3.0 (с модбасом). Возможно подключение трех счетчиков к одной esp для трехфазных сетей. Для этого нужно настроить разные адреса modbus в платах pzem", - "moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры", + "title": "Счетчик электроэнергии PZEM 004 t версии 3.0 (с модбасом)", + "moduleDesc": "Считает потраченную электроэнергию, измеряет напряжение, частоту, силу тока и прочие параметры. Возможно подключение трех счетчиков к одной esp для трехфазных сетей. Для этого нужно настроить разные адреса modbus в платах pzem", "propInfo": { "addr": "Адрес modbus", "int": "Количество секунд между опросами датчика. Желателно устанавливать одинаковые интервалы для параметров (для одного адреса Pzem) что опрос происходил один раз, остальные из 500мс буфера.", @@ -128,6 +128,8 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/RCswitch/modinfo.json b/src/modules/sensors/RCswitch/modinfo.json index dae2a245..5c44ba44 100644 --- a/src/modules/sensors/RCswitch/modinfo.json +++ b/src/modules/sensors/RCswitch/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -64,6 +64,12 @@ "esp32_4mb": [ "rc-switch @ ^2.6.4" ], + "esp32_4mb3f": [ + "rc-switch @ ^2.6.4" + ], + "esp32cam_4mb": [ + "rc-switch @ ^2.6.4" + ], "esp8266_4mb": [ "rc-switch @ ^2.6.4" ], diff --git a/src/modules/sensors/RTC/RTC.cpp b/src/modules/sensors/RTC/RTC.cpp index 727303f0..ea1dee8b 100644 --- a/src/modules/sensors/RTC/RTC.cpp +++ b/src/modules/sensors/RTC/RTC.cpp @@ -39,18 +39,18 @@ class RTC : public IoTItem { return this; } - ulong getRtcUnixTime() { - return _watch->gettimeUnix(); + unsigned long getRtcUnixTime() { + return _watch->gettimeUnix() - jsonReadInt(settingsFlashJson, F("timezone")) * 60 * 60; } void onModuleOrder(String &key, String &value) { if (key == "setUTime") { char *stopstring; - ulong ut = strtoul(value.c_str(), &stopstring, 10); + unsigned long ut = strtoul(value.c_str(), &stopstring, 10); _watch->settimeUnix(ut); SerialPrint("i", F("RTC"), "Устанавливаем время: " + value); } else if (key == "setSysTime") { - _watch->settimeUnix(unixTime); + _watch->settimeUnix(unixTime + jsonReadInt(settingsFlashJson, F("timezone")) * 60 * 60); SerialPrint("i", F("RTC"), F("Запоминаем системное время")); } } @@ -63,6 +63,42 @@ class RTC : public IoTItem { valTmp.valS = _watch->gettime(param[0].valS + " "); return valTmp; } + } else if (command == "setUnixTime") { + if (param.size() == 1) { + long ut = strtoul(param[0].valS.c_str(), nullptr, 10); + _watch->settimeUnix(ut); + return {}; + } + } else if (command == "setTime") { + if (param.size() == 6) { + _watch->settime(param[0].valD, param[1].valD, param[2].valD, param[3].valD, param[4].valD, param[5].valD); //сек, мин, час, день, мес, год + return {}; + } + } else if (command == "getTimeFloat") { + if (param.size() == 1) { + IoTValue valTmp; + _watch->gettime(); + valTmp.isDecimal = true; + String type = param[0].valS; + if (type == "H") { + valTmp.valD = static_cast(_watch->Hours); + } else if (type == "i") { + valTmp.valD = static_cast(_watch->minutes); + } else if (type == "s") { + valTmp.valD = static_cast(_watch->seconds); + } else if (type == "w") { + valTmp.valD = static_cast(_watch->weekday); + } else if (type == "d") { + valTmp.valD = static_cast(_watch->day); + } else if (type == "m") { + valTmp.valD = static_cast(_watch->month); + } else if (type == "Y") { + valTmp.valD = static_cast(_watch->year); + } else { + return {}; // Если переданный тип не поддерживается + } + return valTmp; + } } return {}; diff --git a/src/modules/sensors/RTC/modinfo.json b/src/modules/sensors/RTC/modinfo.json index 3ecef7f7..f22250a1 100644 --- a/src/modules/sensors/RTC/modinfo.json +++ b/src/modules/sensors/RTC/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -18,7 +18,7 @@ "ticker": 0, "int": 5, "btn-setUTime": "0", - "btn-setSysTime": "nil" + "btn-setSysTime": "nil" } ], "about": { @@ -51,19 +51,44 @@ { "name": "getTime", "descr": "Получить строковое значение времени по указанному формату.", - "params": ["Формат как у функции date() в PHP"] + "params": [ + "Формат как у функции date() в PHP" + ] + }, + { + "name": "setTime", + "descr": "Установить время через сценарии в формате сек, мин, час, день, мес, год.", + "params": [ + "сек", "мин", "час", "день", "мес", "год" + ] + }, + { + "name": "setUnixTime", + "descr": "Установить время через сценарий в формате юникстайм", + "params": [ + "rtc.setUnixTime('46489234') - параметр в виде строки" + ] + }, + { + "name": "getTimeFloat", + "descr": "Получить числовое значение времени по указанному формату", + "params": [ + "s - Вернуть секунды, i - Вернуть минуты, H - Вернуть часы в 24-часовом формате, d - Вернуть день месяца, w - Вернуть день недели, m - Вернуть месяц, Y - Вернуть год(4 знака)" + ] } ] }, "defActive": true, "usedLibs": { - "esp32_4mb": ["https://github.com/tremaru/iarduino_RTC"], - "esp8266_4mb": ["https://github.com/tremaru/iarduino_RTC"], - "esp8266_1mb": ["https://github.com/tremaru/iarduino_RTC"], - "esp8266_1mb_ota": ["https://github.com/tremaru/iarduino_RTC"], - "esp8285_1mb": ["https://github.com/tremaru/iarduino_RTC"], - "esp8285_1mb_ota": ["https://github.com/tremaru/iarduino_RTC"], - "esp8266_2mb": ["https://github.com/tremaru/iarduino_RTC"], - "esp8266_2mb_ota": ["https://github.com/tremaru/iarduino_RTC"] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] } } \ No newline at end of file diff --git a/src/modules/sensors/S8/modinfo.json b/src/modules/sensors/S8/modinfo.json index 6f702b87..c4ac2fe9 100644 --- a/src/modules/sensors/S8/modinfo.json +++ b/src/modules/sensors/S8/modinfo.json @@ -1,19 +1,19 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { - "name": "(S8) Cенсор качества воздуха", - "num": 3, - "type": "Reading", - "subtype": "S8co", - "id": "s8co", - "widget": "anydataPpm", - "page": "Сенсоры", - "descr": "S8_CO2", - "int": 15, - "round": 1, - "rxPin": 13, - "txPin": 15 + "name": "(S8) Cенсор качества воздуха", + "num": 3, + "type": "Reading", + "subtype": "S8co", + "id": "s8co", + "widget": "anydataPpm", + "page": "Сенсоры", + "descr": "S8_CO2", + "int": 15, + "round": 1, + "rxPin": 13, + "txPin": 15 } ], "about": { @@ -35,9 +35,8 @@ }, "defActive": true, "usedLibs": { - "esp32_4mb": [ - ], - "esp8266_4mb": [ - ] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp8266_4mb": [] } } \ No newline at end of file diff --git a/src/modules/sensors/Scd40/Scd40.cpp b/src/modules/sensors/Scd40/Scd40.cpp new file mode 100644 index 00000000..df4a4476 --- /dev/null +++ b/src/modules/sensors/Scd40/Scd40.cpp @@ -0,0 +1,432 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include + +#include + +// SensirionI2CScd4x scd4x; + +SensirionI2CScd4x *scd4x = nullptr; // create an object of the CSD40 class +char errorMessageScd4x[256]; +uint16_t errorCodeScd4x; + +void printUint16Hex(uint16_t value) +{ + Serial.print(value < 4096 ? "0" : ""); + Serial.print(value < 256 ? "0" : ""); + Serial.print(value < 16 ? "0" : ""); + Serial.print(value, HEX); +} + +void printSerialNumber(uint16_t serial0, uint16_t serial1, uint16_t serial2) +{ + Serial.print("Serial: 0x"); + printUint16Hex(serial0); + printUint16Hex(serial1); + printUint16Hex(serial2); + Serial.println(); +} + +// Функция инициализации библиотечного класса, возвращает Единстрвенный указать на библиотеку +SensirionI2CScd4x *instanceScd4x() +{ + if (!scd4x) + { // Если библиотека ранее инициализировалась, т о просто вернем указатель + // Инициализируем библиотеку + scd4x = new SensirionI2CScd4x(); + Wire.begin(); + scd4x->begin(Wire); + + //Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай) + // stop potentially previously started measurement + errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute stopPeriodicMeasurement(): "); + Serial.println(errorMessageScd4x); + } + + //Запрашиваем и выводим серийный номер датчика + uint16_t serial0; + uint16_t serial1; + uint16_t serial2; + errorCodeScd4x = instanceScd4x()->getSerialNumber(serial0, serial1, serial2); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute getSerialNumber(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else + { + printSerialNumber(serial0, serial1, serial2); + } + + //Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек) + // Start Measurement + errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute startPeriodicMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + + Serial.println("Waiting for first measurement... "); + } + return scd4x; +} + +class Scd40co2 : public IoTItem +{ +private: +bool lowPeriodic = true; +bool autoCalibration = true; +//int targetCo2 = 0; +public: + Scd40co2(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, F("lowPeriodic"), lowPeriodic); + jsonRead(parameters, F("autoCalibration"), autoCalibration); + // jsonRead(parameters, F("targetCo2"), targetCo2); + settingParameters(); + } + + void doByInterval() + { + // Read Measurement + uint16_t co2 = 0; + float temperature = 0.0f; + float humidity = 0.0f; + bool isDataReady = false; + //Запрашиваем библиотеку о готовности отправить запрос + errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute getDataReadyFlag(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + return; + } + if (!isDataReady) + { + return; + } + //Если все нормально забираем у библиотеки данные + errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute readMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else if (co2 == 0) + { + Serial.println("Invalid sample detected, skipping."); + } + else + { + Serial.print("Co2:"); + Serial.print(co2); + Serial.print("\t"); + // Serial.print("Temperature:"); + // Serial.print(temperature); + // Serial.print("\t"); + // Serial.print("Humidity:"); + // Serial.println(humidity); + } + // value.valD = scd4x.readMeasurement(temperature); + value.valD = co2; + if (value.valD < 5000) + regEvent(value.valD, "Scd40co2"); + else + SerialPrint("E", "Sensor Scd40co2", "Error", _id); + } + + //Хук для обработки кнопки + void onModuleOrder(String &key, String &value) + { + if (key == "Recalibration")//название кнопки btn-Recalibration + { + SerialPrint("i", F("Sensor Scd40co2"), "User run calibration, targetCo2: " + value); + Recalibration(value.toInt()); + } + } + void Recalibration(int targetCo2) + { + //Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай) + // stop potentially previously started measurement + errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute stopPeriodicMeasurement(): "); + Serial.println(errorMessageScd4x); + } + delay(500); // Из описания performForcedRecalibration 2. Stop periodic measurement. Wait 500 ms. + uint16_t frcCorrection; + errorCodeScd4x = instanceScd4x()->performForcedRecalibration(targetCo2, frcCorrection); + + if (errorCodeScd4x) + { + Serial.print("Error trying to execute performForcedRecalibration(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else + { + Serial.println("performForcedRecalibration(): OK!"); + SerialPrint("i", F("Sensor Scd40co2"), "Calibration is OK, frcCorrection: " + String(frcCorrection)); + } + + //Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек) + // Start Measurement + errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute startPeriodicMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + + Serial.println("Waiting for first measurement... "); + } + + void settingParameters() + { + //Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай) + // stop potentially previously started measurement + errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute stopPeriodicMeasurement(): "); + Serial.println(errorMessageScd4x); + } + + errorCodeScd4x = instanceScd4x()->startLowPowerPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute startLowPowerPeriodicMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else + { + Serial.println("startLowPowerPeriodicMeasurement(): OK!"); + } + + errorCodeScd4x = instanceScd4x()->setAutomaticSelfCalibration((uint16_t)autoCalibration); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute setAutomaticSelfCalibration(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else + { + Serial.println("setAutomaticSelfCalibration(): OK!"); + } + + //Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек) + // Start Measurement + errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute startPeriodicMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + + Serial.println("Waiting for first measurement... "); + } + + ~Scd40co2(){}; +}; + +class Scd40t : public IoTItem +{ +private: + int offsetT; +public: + Scd40t(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, F("offset"), offsetT); + setTemperatureOffset(); + } + + void doByInterval() + { + // Read Measurement + uint16_t co2 = 0; + float temperature = 0.0f; + float humidity = 0.0f; + bool isDataReady = false; + errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute getDataReadyFlag(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + return; + } + if (!isDataReady) + { + return; + } + errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity); + if (errorCodeScd4x) + { + Serial.print("errorCodeScd4x trying to execute readMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else if (co2 == 0) + { + Serial.println("Invalid sample detected, skipping."); + } + else + { + // Serial.print("Co2:"); + // Serial.print(co2); + // Serial.print("\t"); + Serial.print("Temperature:"); + Serial.print(temperature); + Serial.print("\t"); + // Serial.print("Humidity:"); + // Serial.println(humidity); + } + // value.valD = scd4x.readMeasurement(temperature); + value.valD = temperature; + if (value.valD < 124) + regEvent(value.valD, "Scd40t"); + else + SerialPrint("E", "Sensor Scd40t", "Error", _id); + } + + void setTemperatureOffset() + { + //Останавливаем периодический опрос датчика вбиблиотеке для запроса Сер.номера (на всякий случай) + // stop potentially previously started measurement + errorCodeScd4x = instanceScd4x()->stopPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute stopPeriodicMeasurement(): "); + Serial.println(errorMessageScd4x); + } + + errorCodeScd4x = instanceScd4x()->setTemperatureOffset((uint16_t)offsetT); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute setTemperatureOffset(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else + { + Serial.println("setTemperatureOffset(): OK!"); + } + + //Обратно стартуем периодический опрос датчика библиотекой (по описанию библиотеки каждые 5сек) + // Start Measurement + errorCodeScd4x = instanceScd4x()->startPeriodicMeasurement(); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute startPeriodicMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + + Serial.println("Waiting for first measurement... "); + } + + ~Scd40t(){}; +}; + +class Scd40h : public IoTItem +{ +public: + Scd40h(String parameters) : IoTItem(parameters) + { + } + + void doByInterval() + { + // Read Measurement + uint16_t co2 = 0; + float temperature = 0.0f; + float humidity = 0.0f; + bool isDataReady = false; + errorCodeScd4x = instanceScd4x()->getDataReadyFlag(isDataReady); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute getDataReadyFlag(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + return; + } + if (!isDataReady) + { + return; + } + errorCodeScd4x = instanceScd4x()->readMeasurement(co2, temperature, humidity); + if (errorCodeScd4x) + { + Serial.print("Error trying to execute readMeasurement(): "); + errorToString(errorCodeScd4x, errorMessageScd4x, 256); + Serial.println(errorMessageScd4x); + } + else if (co2 == 0) + { + Serial.println("Invalid sample detected, skipping."); + } + else + { + // Serial.print("Co2:"); + // Serial.print(co2); + // Serial.print("\t"); + // Serial.print("Temperature:"); + // Serial.print(temperature); + // Serial.print("\t"); + Serial.print("Humidity:"); + Serial.println(humidity); + Serial.print("\t"); + } + // value.valD = scd4x.readMeasurement(temperature); + value.valD = humidity; + if (value.valD < 100) + regEvent(value.valD, "Scd40h"); + else + SerialPrint("E", "Sensor Scd40h", "Error", _id); + } + + ~Scd40h(){}; +}; + +void *getAPI_Scd40(String subtype, String param) +{ + + if (subtype == F("Scd40co2")) + { + // hdc1080.begin(0x40); + // scd4x->begin(Wire); + // scd4x.begin(Wire); + return new Scd40co2(param); + } + else if (subtype == F("Scd40t")) + { + // hdc1080.begin(0x40); + // scd4x->begin(Wire); + return new Scd40t(param); + } + else if (subtype == F("Scd40h")) + { + // hdc1080.begin(0x40); + // scd4x->begin(Wire); + return new Scd40h(param); + // } else { + // return nullptr; + } + + return nullptr; +} \ No newline at end of file diff --git a/src/modules/sensors/Scd40/modinfo.json b/src/modules/sensors/Scd40/modinfo.json new file mode 100644 index 00000000..9e29e654 --- /dev/null +++ b/src/modules/sensors/Scd40/modinfo.json @@ -0,0 +1,90 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "SCD40 Углекислый газ", + "type": "Reading", + "subtype": "Scd40co2", + "id": "Scd40co2", + "widget": "anydataPpm", + "page": "Сенсоры", + "descr": "SCD40 Углекислый газ", + "int": 15, + "round": 0, + "lowPeriodic": 1, + "autoCalibration": 1, + "btn-Recalibration": 0 + }, + { + "global": 0, + "name": "SCD40 Влажность", + "type": "Reading", + "subtype": "Scd40h", + "id": "Scd40h", + "widget": "anydataHum", + "page": "Сенсоры", + "descr": "SCD40 Влажность", + "int": 15, + "round": 1 + }, + { + "global": 0, + "name": "SCD40 Температура", + "type": "Reading", + "subtype": "Scd40t", + "id": "Scd40t", + "widget": "anydataTmp", + "page": "Сенсоры", + "descr": "SCD40 Температура", + "int": 15, + "round": 1, + "offset": 4 + } + ], + "about": { + "authorName": "Serghei Crasnicov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "Bubnov Mikhail @Mit4bmw", + "moduleName": "Scd40", + "moduleVersion": "0.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "Scd40co2", + "Scd40t", + "Scd40h" + ], + "title": "Датчик температуры и влажности Scd40", + "moduleDesc": "Позволяет получить значения температуры и влажности с Scd40.", + "propInfo": { + "int": "Количество секунд между опросами библиотеки (датчик опрашивается библиотекой по своему таймеру, см. lowPeriodic).", + "offset": "Смещение температуры представляет собой разницу между температурой, измеренной SCD4x, и фактической температурой окружающей среды температура. По умолчанию смещение температуры в библиотеке/датчике установлено на 4°C.", + "lowPeriodic": "Медленные режим опроса датчика библиотекой. 0-каждые 5сек, 1-каждые 30сек", + "autoCalibration": "Автоматическая калибровка, по умолчанию включена AutomaticSelfCalibration, 0 - выключена", + "btn-Recalibration": "Кнопка принудительной калибровки. В поле указать Целевая концентрация CO₂ в миллионных долях. Перед калибровкой необходимо находтся в течение > 3 минут в среде с однородной и постоянной концентрацией CO₂. Выдает в лог Значение коррекции FRC в co₂ ppm" + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "Sensirion I2C SCD4x @0.4.0", + "Sensirion Core @0.6.0" + ], + "esp32_4mb3f": [ + "Sensirion I2C SCD4x @0.4.0", + "Sensirion Core @0.6.0" + ], + "esp32cam_4mb": [ + "Sensirion I2C SCD4x @0.4.0", + "Sensirion Core @0.6.0" + ], + "esp8266_4mb": [ + "Sensirion I2C SCD4x @0.4.0", + "Sensirion Core @0.6.0" + ] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Sds011/Sds011.cpp b/src/modules/sensors/Sds011/Sds011.cpp index fb081e38..1a903d86 100644 --- a/src/modules/sensors/Sds011/Sds011.cpp +++ b/src/modules/sensors/Sds011/Sds011.cpp @@ -157,16 +157,17 @@ public: void loop() { ts_sds.update(); - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= _interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } + IoTItem::loop(); + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= _interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } } //======================================================================================================= // doByInterval() diff --git a/src/modules/sensors/Sds011/modinfo.json b/src/modules/sensors/Sds011/modinfo.json index 8fd8eb80..3026e56e 100644 --- a/src/modules/sensors/Sds011/modinfo.json +++ b/src/modules/sensors/Sds011/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -61,13 +61,20 @@ "continuousMode": "1 - Непрерывный режим, 0 - Режим с остановкой (щедящий)", "maxRetriesNotAvailable": "Количество попыток ожидания ответа сенсора при опросе (не нужно менять)", "retryDelayMs": "Задержка между попытками, миллисекунды (не нужно менять)" - } + }, + "title": "Sds011 Датчик концентрации пыли в воздухе" }, "defActive": false, "usedLibs": { "esp32_4mb": [ "Nova Fitness Sds dust sensors library@1.5.1" ], + "esp32_4mb3f": [ + "Nova Fitness Sds dust sensors library@1.5.1" + ], + "esp32cam_4mb": [ + "Nova Fitness Sds dust sensors library@1.5.1" + ], "esp8266_4mb": [ "Nova Fitness Sds dust sensors library@1.5.1" ] diff --git a/src/modules/sensors/Sgp30/modinfo.json b/src/modules/sensors/Sgp30/modinfo.json index 94c59309..b3da3628 100644 --- a/src/modules/sensors/Sgp30/modinfo.json +++ b/src/modules/sensors/Sgp30/modinfo.json @@ -1,31 +1,31 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { - "global": 0, - "name": "SGP30 Cенсор качества воздуха", - "num": 3, - "type": "Reading", - "subtype": "Sgp30t", - "id": "sgp30t", - "widget": "anydatappm", - "page": "Сенсоры", - "descr": "TVOC", - "int": 30, - "round": 1 + "global": 0, + "name": "SGP30 Cенсор качества воздуха", + "num": 3, + "type": "Reading", + "subtype": "Sgp30t", + "id": "sgp30t", + "widget": "anydatappm", + "page": "Сенсоры", + "descr": "TVOC", + "int": 30, + "round": 1 }, { - "global": 0, - "name": "SGP30 Cенсор газа", - "num": 4, - "type": "Reading", - "subtype": "Sgp30e", - "id": "sgp30e", - "widget": "anydatappm", - "page": "Сенсоры", - "descr": "eCO2", - "int": 30, - "round": 1 + "global": 0, + "name": "SGP30 Cенсор газа", + "num": 4, + "type": "Reading", + "subtype": "Sgp30e", + "id": "sgp30e", + "widget": "anydatappm", + "page": "Сенсоры", + "descr": "eCO2", + "int": 30, + "round": 1 } ], "about": { @@ -51,6 +51,12 @@ "esp32_4mb": [ "sparkfun/SparkFun SGP30 Arduino Library@^1.0.5" ], + "esp32_4mb3f": [ + "sparkfun/SparkFun SGP30 Arduino Library@^1.0.5" + ], + "esp32cam_4mb": [ + "sparkfun/SparkFun SGP30 Arduino Library@^1.0.5" + ], "esp8266_4mb": [ "sparkfun/SparkFun SGP30 Arduino Library@^1.0.5" ], diff --git a/src/modules/sensors/Sht20/modinfo.json b/src/modules/sensors/Sht20/modinfo.json index 39b07e6d..443b91b7 100644 --- a/src/modules/sensors/Sht20/modinfo.json +++ b/src/modules/sensors/Sht20/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -52,6 +52,9 @@ "esp32_4mb": [ "robtillaart/SHT2x@^0.1.1" ], + "esp32_4mb3f": [ + "robtillaart/SHT2x@^0.1.1" + ], "esp8266_4mb": [ "robtillaart/SHT2x@^0.1.1" ], diff --git a/src/modules/sensors/Sht30/modinfo.json b/src/modules/sensors/Sht30/modinfo.json index 47e42933..5cbedff1 100644 --- a/src/modules/sensors/Sht30/modinfo.json +++ b/src/modules/sensors/Sht30/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -52,6 +52,12 @@ "esp32_4mb": [ "WEMOS SHT3x@1.0.0" ], + "esp32_4mb3f": [ + "WEMOS SHT3x@1.0.0" + ], + "esp32cam_4mb": [ + "WEMOS SHT3x@1.0.0" + ], "esp8266_4mb": [ "WEMOS SHT3x@1.0.0" ], diff --git a/src/modules/sensors/Sonar/modinfo.json b/src/modules/sensors/Sonar/modinfo.json index 8c79734a..eaaeed5c 100644 --- a/src/modules/sensors/Sonar/modinfo.json +++ b/src/modules/sensors/Sonar/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "global": 0, @@ -38,6 +38,8 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], "esp8266_4mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], diff --git a/src/modules/sensors/UART/modinfo.json b/src/modules/sensors/UART/modinfo.json index 1e63b4e1..6eaa0e1c 100644 --- a/src/modules/sensors/UART/modinfo.json +++ b/src/modules/sensors/UART/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Сенсоры", + "menuSection": "sensors", "configItem": [ { "name": "UART", @@ -44,22 +44,31 @@ { "name": "println", "descr": "Отправить в UART строку текста и признак завершения строки (перевод строки).", - "params": ["Строка текста"] + "params": [ + "Строка текста" + ] }, { "name": "print", "descr": "Отправить в UART строку текста.", - "params": ["Строка текста"] + "params": [ + "Строка текста" + ] }, { "name": "printHex", "descr": "Отправить в UART HEX-строку.", - "params": ["HEX-строка."] + "params": [ + "HEX-строка." + ] }, { "name": "printFFF", "descr": "Отправить в UART текстовую строку и hex метку 3 байта 0xFF0xFF0xFF.", - "params": ["Строка текста", "1 - обернуть строку в кавычки, 0 - отправить без кавычек"] + "params": [ + "Строка текста", + "1 - обернуть строку в кавычки, 0 - отправить без кавычек" + ] } ] }, @@ -68,6 +77,12 @@ "esp32_4mb": [ "plerup/EspSoftwareSerial" ], + "esp32_4mb3f": [ + "plerup/EspSoftwareSerial" + ], + "esp32cam_4mb": [ + "plerup/EspSoftwareSerial" + ], "esp8266_4mb": [ "plerup/EspSoftwareSerial" ], diff --git a/src/modules/sensors/ld2410/ld2410.cpp b/src/modules/sensors/ld2410/ld2410.cpp new file mode 100644 index 00000000..c17c5751 --- /dev/null +++ b/src/modules/sensors/ld2410/ld2410.cpp @@ -0,0 +1,448 @@ +#include "Global.h" +#include "classes/IoTItem.h" + +#define MONITOR_SERIAL Serial +#define RADAR_SERIAL Serial1 +// #define RADAR_RX_PIN 32 +// #define RADAR_TX_PIN 33 + +#include +// ld2410 radar; + +ld2410 *radar = nullptr; + +ld2410 *instanceradar(uint8_t _rx, uint8_t _tx) +{ + if (!radar) + { // Если библиотека ранее инициализировалась, т о просто вернем указатель + // Инициализируем библиотеку + radar = new ld2410(); + // radar->begin(); // При необходимости делаем begin библиотеке + + RADAR_SERIAL.begin(256000, SERIAL_8N1, _rx, _tx); // UART for monitoring the radar + + if (radar->begin(RADAR_SERIAL)) + { + SerialPrint("i", "LD2410", "Radar sensor подключен"); + SerialPrint("i", "LD2410", "Connect LD2410 radar TX to GPIO: " + String(_rx)); + SerialPrint("i", "LD2410", "Connect LD2410 radar RX to GPIO: " + String(_tx)); + SerialPrint("i", "LD2410", "LD2410 firmware version: " + String(radar->firmware_major_version)); + SerialPrint("i", "LD2410", "LD2410 firmware version: " + String(radar->firmware_minor_version)); + SerialPrint("i", "LD2410", "LD2410 firmware version: " + String(radar->firmware_bugfix_version)); + } + else + { + SerialPrint("i", "LD2410", "Radar sensor не подключен"); + } + } + return radar; +} + +int presence = 0; +int presence_last = 0; +int movement = 0; +int movement_last = 0; +int distance = 0; +int distance_last = 0; +int energy = 0; +int energy_last = 0; +int err = 0; + +uint32_t lastReadingP = 0; +uint32_t lastReadingM = 0; +uint32_t lastReadingD = 0; +uint32_t lastReadingE = 0; +bool radarConnected = false; + +// датчик присутствия +class ld2410m : public IoTItem +{ +private: + int _ticker; + uint8_t _rx, _tx; + +public: + //======================================================================================================= + // setup() + + ld2410m(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, "ticker", _ticker); + String tmp; + jsonRead(parameters, "RX", tmp); + _rx = tmp.toInt(); + jsonRead(parameters, "TX", tmp); + _tx = tmp.toInt(); + } + + void loop() + { + instanceradar(_rx, _tx)->read(); + if (instanceradar(_rx, _tx)->isConnected() && millis() - lastReadingP > 1000) // Report every 1000ms + { + lastReadingP = millis(); + err = 0; + presence = instanceradar(_rx, _tx)->presenceDetected(); + if (instanceradar(_rx, _tx)->stationaryTargetDetected()) + { + movement = 0; + distance = instanceradar(_rx, _tx)->stationaryTargetDistance(); + energy = instanceradar(_rx, _tx)->stationaryTargetEnergy(); + } + if (instanceradar(_rx, _tx)->movingTargetDetected()) + { + movement = 1; + distance = instanceradar(_rx, _tx)->movingTargetDistance(); + energy = instanceradar(_rx, _tx)->movingTargetEnergy(); + } + + if (presence != presence_last) + { + value.valD = int(presence); + regEvent(value.valD, "ld2410m"); + presence_last = presence; + // SerialPrint("i", "LD2410", "присутствие: " + String(int(presence))); + } + + if (presence && movement != movement_last) + { + value.valD = int(presence); + regEvent(value.valD, "ld2410m"); + } + if (presence && _ticker) + { + value.valD = int(presence); + regEvent(value.valD, "ld2410m"); + presence_last = presence; + } + } + if (!instanceradar(_rx, _tx)->isConnected()) + { + if (err == 0) + { + // value.valD = "Err"; + // regEvent(value.valD, "ld2410m"); + // SerialPrint("E", "LD2410", "Radar sensor ошибка получения данных"); + } + err = 1; + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "setmaxvalues") + { + if (param[0].isDecimal && param[1].isDecimal && param[2].isDecimal) + { + uint8_t newMovingMaxDistance = param[0].valD; + uint8_t newStationaryMaxDistance = param[1].valD; + uint16_t inactivityTimer = param[2].valD; + if (newMovingMaxDistance > 0 && newStationaryMaxDistance > 0 && newMovingMaxDistance <= 8 && newStationaryMaxDistance <= 8) + { + if (instanceradar(_rx, _tx)->setMaxValues(newMovingMaxDistance, newStationaryMaxDistance, inactivityTimer)) + { + SerialPrint("i", "LD2410", "Параметры установлены. Перезапустите датчик"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка записи параметров."); + } + } + else + { + SerialPrint("E", "LD2410", "Заданы не корректные параметры. Диапазон от 1 до 8 "); + } + } + } + if (command == "setsensitivity") + { + if (param[0].isDecimal && param[1].isDecimal && param[2].isDecimal) + { + uint8_t gate = param[0].valD; + uint8_t motionSensitivity = param[1].valD; + uint16_t stationarySensitivity = param[2].valD; + if (motionSensitivity >= 0 && stationarySensitivity >= 0 && motionSensitivity <= 100 && stationarySensitivity <= 100) + { + if (instanceradar(_rx, _tx)->setGateSensitivityThreshold(gate, motionSensitivity, stationarySensitivity)) + { + SerialPrint("i", "LD2410", "Параметры установлены. Перезапустите датчик"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка записи параметров."); + } + } + else + { + SerialPrint("E", "LD2410", "Заданы не корректные параметры. Диапазон от 1 до 100 "); + } + } + } + if (command == "enableengineeringmode") + { + if (instanceradar(_rx, _tx)->requestStartEngineeringMode()) + { + SerialPrint("i", "LD2410", "Параметры установлены. Перезапустите датчик"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка записи параметров."); + } + } + if (command == "disableengineeringmode") + { + if (instanceradar(_rx, _tx)->requestEndEngineeringMode()) + { + SerialPrint("i", "LD2410", "Параметры установлены. Перезапустите датчик"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка записи параметров."); + } + } + if (command == "restart") + { + if (instanceradar(_rx, _tx)->requestRestart()) + { + SerialPrint("i", "LD2410", "Датчик перезапущен"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка перезапуска датчика."); + } + } + if (command == "readversion") + { + if (instanceradar(_rx, _tx)->requestFirmwareVersion()) + { + SerialPrint("i", "LD2410", (String)instanceradar(_rx, _tx)->firmware_major_version); + SerialPrint("i", "LD2410", (String)instanceradar(_rx, _tx)->firmware_minor_version); + } + else + { + SerialPrint("E", "LD2410", "Ошибка получения версии."); + } + } + if (command == "factoryreset") + { + if (instanceradar(_rx, _tx)->requestFactoryReset()) + { + SerialPrint("i", "LD2410", "Параметры установлены. Перезапустите датчик"); + } + else + { + SerialPrint("E", "LD2410", "Ошибка сброса параметров."); + } + } + } + ~ld2410m() + { + delete ld2410; + radar = nullptr; + }; +}; +//--------------------------------------------------------------------------- + +// движение / стационарное - присутствие +class ld2410t : public IoTItem +{ +private: + uint8_t _rx, _tx; + +public: + //======================================================================================================= + // setup() + + ld2410t(String parameters) : IoTItem(parameters) + { + String tmp; + jsonRead(parameters, "RX", tmp); + _rx = tmp.toInt(); + jsonRead(parameters, "TX", tmp); + _tx = tmp.toInt(); + } + + void loop() + { + instanceradar(_rx, _tx)->read(); + if (instanceradar(_rx, _tx)->isConnected() && millis() - lastReadingM > 1000) // Report every 1000ms + { + lastReadingM = millis(); + err = 0; + presence = instanceradar(_rx, _tx)->presenceDetected(); + if (instanceradar(_rx, _tx)->stationaryTargetDetected()) + { + movement = 0; + // distance = instanceradar(_rx, _tx)->stationaryTargetDistance(); + // energy = instanceradar(_rx, _tx)->stationaryTargetEnergy(); + } + if (instanceradar(_rx, _tx)->movingTargetDetected()) + { + movement = 1; + // distance = instanceradar(_rx, _tx)->movingTargetDistance(); + // energy = instanceradar(_rx, _tx)->movingTargetEnergy(); + } + } + if (presence == 0 && movement_last != 0) + { + movement = 0; + } + if (movement != movement_last) + { + value.valD = int(movement); + regEvent(value.valD, "ld2410t"); + movement_last = movement; + } + } + + ~ld2410t() + { + delete ld2410; + radar = nullptr; + }; +}; +//--------------------------------------------------------------------------- + +// дистанция +class ld2410d : public IoTItem +{ +private: + uint8_t _rx, _tx; + +public: + //======================================================================================================= + // setup() + + ld2410d(String parameters) : IoTItem(parameters) + { + String tmp; + jsonRead(parameters, "RX", tmp); + _rx = tmp.toInt(); + jsonRead(parameters, "TX", tmp); + _tx = tmp.toInt(); + } + + void loop() + { + instanceradar(_rx, _tx)->read(); + if (instanceradar(_rx, _tx)->isConnected() && millis() - lastReadingD > 1000) // Report every 1000ms + { + lastReadingD = millis(); + err = 0; + presence = instanceradar(_rx, _tx)->presenceDetected(); + if (instanceradar(_rx, _tx)->stationaryTargetDetected()) + { + // movement = 0; + distance = instanceradar(_rx, _tx)->stationaryTargetDistance(); + // energy = instanceradar(_rx, _tx)->stationaryTargetEnergy(); + } + if (instanceradar(_rx, _tx)->movingTargetDetected()) + { + // movement = 1; + distance = instanceradar(_rx, _tx)->movingTargetDistance(); + // energy = instanceradar(_rx, _tx)->movingTargetEnergy(); + } + if (presence == 0 && distance_last != 0) + { + distance = 0; + } + if (distance != distance_last) + { + value.valD = int(distance); + regEvent(value.valD, "ld2410d"); + distance_last = distance; + } + } + } + ~ld2410d() + { + delete ld2410; + radar = nullptr; + }; +}; +//--------------------------------------------------------------------------- + +// энергия +class ld2410e : public IoTItem +{ +private: + uint8_t _rx, _tx; + +public: + //======================================================================================================= + // setup() + + ld2410e(String parameters) : IoTItem(parameters) + { + String tmp; + jsonRead(parameters, "RX", tmp); + _rx = tmp.toInt(); + jsonRead(parameters, "TX", tmp); + _tx = tmp.toInt(); + } + + void loop() + { + instanceradar(_rx, _tx)->read(); + if (instanceradar(_rx, _tx)->isConnected() && millis() - lastReadingE > 1000) // Report every 1000ms + { + lastReadingE = millis(); + err = 0; + presence = instanceradar(_rx, _tx)->presenceDetected(); + if (instanceradar(_rx, _tx)->stationaryTargetDetected()) + { + // movement = 0; + // distance = instanceradar(_rx, _tx)->stationaryTargetDistance(); + energy = instanceradar(_rx, _tx)->stationaryTargetEnergy(); + } + if (instanceradar(_rx, _tx)->movingTargetDetected()) + { + // movement = 1; + // distance = instanceradar(_rx, _tx)->movingTargetDistance(); + energy = instanceradar(_rx, _tx)->movingTargetEnergy(); + } + if (presence == 0 && energy_last != 0) + { + energy = 0; + } + if (energy != energy_last) + { + value.valD = int(energy); + regEvent(value.valD, "ld2410e"); + // value = Value; + // regEvent((String)(int)value.valD, F("ld2410e"), false, true); + // SerialPrint("i", "LD2410", "Radar sensor энергия: " + String(energy)); + energy_last = energy; + } + } + } + ~ld2410e() + { + delete ld2410; + radar = nullptr; + }; +}; +//--------------------------------------------------------------------------- +void *getAPI_ld2410(String subtype, String param) +{ + if (subtype == F("ld2410m")) + { + return new ld2410m(param); + } + else if (subtype == F("ld2410t")) + { + return new ld2410t(param); + } + else if (subtype == F("ld2410d")) + { + return new ld2410d(param); + } + else if (subtype == F("ld2410e")) + { + return new ld2410e(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/ld2410/modinfo.json b/src/modules/sensors/ld2410/modinfo.json new file mode 100644 index 00000000..298079f1 --- /dev/null +++ b/src/modules/sensors/ld2410/modinfo.json @@ -0,0 +1,95 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "LD2410 Датчик присутствия", + "type": "Reading", + "subtype": "ld2410m", + "id": "ld2410m", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "Присутствие", + "RX": 32, + "TX": 33, + "val": 0, + "round": 0, + "ticker": 0 + }, + { + "global": 0, + "name": "LD2410 подвижность объета", + "type": "Reading", + "subtype": "ld2410t", + "id": "ld2410t", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "Движение", + "RX": 32, + "TX": 33, + "val": 0, + "round": 0 + }, + { + "global": 0, + "name": "LD2410 дистанция до объекта", + "type": "Reading", + "subtype": "ld2410d", + "id": "ld2410d", + "widget": "anydataCm", + "page": "Сенсоры", + "descr": "Дистанция", + "RX": 32, + "TX": 33, + "val": 0, + "round": 0 + }, + { + "global": 0, + "name": "LD2410 отраженная энергия", + "type": "Reading", + "subtype": "ld2410e", + "id": "ld2410e", + "widget": "anydataHum", + "page": "Сенсоры", + "descr": "Энергия", + "RX": 32, + "TX": 33, + "val": 0, + "round": 0 + } + ], + "about": { + "authorName": "AVAKS", + "authorContact": "https://t.me/avaks", + "authorGit": "https://github.com/avaksru", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "Mitchel https://t.me/Mit4bmw", + "moduleName": "ld2410", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 0 + }, + "subTypes": [ + "ld2410m", + "ld2410t", + "ld2410d", + "ld2410e" + ], + "title": "Радарный датчик присутствия человека Hi-Link HLK-LD2410", + "moduleDesc": "Умный датчик, который измеряет расстояние, определяет стоит объект или двигается. Умеет разбивать зоны движения (всего можно до 8 зон задать). ", + "propInfo": { + "ticker": "1 - генерирует событие каждую секунду пока обнаружено присутствие, 0 - событие генереруется только один раз когда присутствие обнаружено или когда обьект вышел из поля обнаружения датчика" + } + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [ + "ncmreynolds/ld2410@^0.1.3" + ], + "esp32_4mb3f": [ + "ncmreynolds/ld2410@^0.1.3" + ] + } +} \ No newline at end of file diff --git a/src/modules/virtual/Cron/modinfo.json b/src/modules/virtual/Cron/modinfo.json index 54783e3f..4356adb6 100644 --- a/src/modules/virtual/Cron/modinfo.json +++ b/src/modules/virtual/Cron/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -28,7 +28,7 @@ "esp8266_4mb": 15 }, "title": "Будильник типа Cron", - "moduleDesc": "Планировщик времени для периодического выполнения заданий в определённое время. Генерирует событие в указанное время по формату Cron https://ru.wikipedia.org/wiki/Cron ", + "moduleDesc": "Планировщик времени для периодического выполнения заданий в определённое время. Генерирует событие в указанное время по формату Cron https://ru.wikipedia.org/wiki/Cron . Но в данной реализации есть возможность задавать секундный интервал, поэтому слева необходимо добавить еще один разряд значений секунд - подробнее в базовой библиотеке https://github.com/staticlibs/ccronexpr", "propInfo": { "formatNextAlarm": "Формат представления даты и времени срабатывания следующего уведомления. http://cppstudio.com/post/621/", "needSave": "Требуется сохранять(1) или нет(0) состояние в энерго независимую память." @@ -50,7 +50,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/GoogleSheet/GoogleSheet.cpp b/src/modules/virtual/GoogleSheet/GoogleSheet.cpp new file mode 100644 index 00000000..3b196c4f --- /dev/null +++ b/src/modules/virtual/GoogleSheet/GoogleSheet.cpp @@ -0,0 +1,173 @@ + +#include "Global.h" +#include "classes/IoTItem.h" + +class GoogleSheet : public IoTItem +{ +private: + String id; + String logid; + String scid = ""; + String shname = ""; + // bool init = false; + // int interval = 1; + // long interval; + String URL = ("http://iotmanager.org/projects/google.php?/macros/s/"); // F("https://script.google.com/macros/s/"); + String urlFinal; + HTTPClient http; + WiFiClient client; + +public: + GoogleSheet(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, F("id"), id); + jsonRead(parameters, F("logid"), logid); + jsonRead(parameters, F("scid"), scid); + jsonRead(parameters, F("shname"), shname); + // jsonRead(parameters, F("int"), interval); + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); + // interval = interval * 1000 * 60; // так как у нас в минутах + urlFinal = URL + scid + F("/exec?") + F("sheet=") + shname; + } + + void doByInterval() + { + if (isNetworkActive()) + { + String value = getItemValue(logid); + if (value != "") + send2Google(logid, value, true); + } + } + + // void loop() + // { + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } + + IoTValue execute(String command, std::vector ¶m) + { + if (isNetworkActive()) + { + if (command == F("logGoogle")) + { // Логирование определенного элемента по его идентификатору в GoogleSheet + /* if (param.size() == 1) + { + String id = param[0].valS; + String value = getItemValue(id); + send2Google(id, value, true); + return {}; + } + */ + if (param.size() >= 1) + { + int sizeOfParam = param.size(); + for (unsigned int i = 0; i < sizeOfParam; i++) + { + IoTItem *itm = findIoTItemOnValue(param[i].valS); + if (itm != nullptr) + { + if (i == sizeOfParam - 1) + send2Google(itm->getID(), param[i].valS, true); + else + send2Google(itm->getID(), param[i].valS, false); + } + } + } + } + else if (command == F("logGoogleAny")) + { // Запись произвольных данных в GoogleSheet + if (param.size() == 2) + { + send2Google(param[0].valS, param[1].valS, true); + return {}; + } + } + } + return {}; + } + + void send2Google(String logid, String value, bool send) + { + urlFinal = urlFinal + ("&id=") + logid + ("&value=") + value; + + if (!send) + return; // Не отправляем просто накапливаем данные + + /* if (!init) + { + init = true; + urlFinal = urlFinal + "&init=1"; + } + */ +// HTTPClient http; +#if defined ESP8266 + // WiFiClient client; + // client.setInsecure(); + if (!http.begin(client, urlFinal)) +#elif defined ESP32 + // WiFiClient client; + if (!http.begin(urlFinal)) +#endif + { + SerialPrint("I", F("GoogleSheet"), F("connection failed")); + } + // http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // HTTPC_STRICT_FOLLOW_REDIRECTS HTTPC_FORCE_FOLLOW_REDIRECTS + http.addHeader(F("Content-Type"), F("application/x-www-form-urlencoded")); + int httpCode = http.GET(); + + String payload = http.getString(); + SerialPrint("<-", F("GoogleSheet"), "URL: " + urlFinal); + SerialPrint("->", F("GoogleSheet"), "server: " + (String)httpCode); /*"URL: " + urlFinal + */ + + /* if (httpCode > 0) + { + SerialPrint("->", F("GoogleSheet"), "msg from server: " + (String)payload.c_str()); + } + */ + // http.end(); + // Обнуляем данные в запросе, так как все отправили + urlFinal = URL + scid + F("/exec?") + F("sheet=") + shname; + }; + + // поиск элемента модуля в существующей конфигурации по его значению + // По хорошему ЭТО наверное надо в ЯДРО пернести + IoTItem *findIoTItemOnValue(const String &value) + { + if (value == "") + return nullptr; + for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) + { + if ((*it)->getValue() == value) + return *it; + } + + return nullptr; + }; + + ~GoogleSheet() { http.end(); }; +}; + +void *getAPI_GoogleSheet(String subtype, String param) +{ + + if (subtype == F("GoogleSheet")) + { + return new GoogleSheet(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/virtual/GoogleSheet/goggleapp.gs b/src/modules/virtual/GoogleSheet/goggleapp.gs new file mode 100644 index 00000000..2fc24a21 --- /dev/null +++ b/src/modules/virtual/GoogleSheet/goggleapp.gs @@ -0,0 +1,52 @@ + +var SS = SpreadsheetApp.openById('GOOGLE_SHEET_ID'); + +function doGet(e){ + + var value = e.parameters.value; + var id = e.parameters.id; + var sheet = e.parameter.sheet; + var init = e.parameter.init; + + if (sheet == undefined) { + sheet = SS.getActiveSheet(); + } else{ + sheet = SS.getSheetByName(sheet); + } + + if (sheet == null) { + return ContentService.createTextOutput("Error sheet name"); + } + +// if (init !== undefined){ + sheet.getRange('A1').setValue("ID"); + sheet.getRange('B1').setValue("VALUE"); + sheet.getRange('C1').setValue("DATE"); +// } + + var nextFreeRow = sheet.getLastRow() + 1; + + if (id == undefined) id = "nan"; + if (value !== undefined){ + try { + var now = Utilities.formatDate(new Date(), "GMT+03:00", "dd.MM.yyyy HH:mm:ss"); + for (let i = 0; i < id.length; i++) { + sheet.getRange("A"+nextFreeRow).setValue(id[i]); + value[i] = value[i].replace(/\./, ","); + sheet.getRange("B"+nextFreeRow).setValue(value[i]); + sheet.getRange("C"+nextFreeRow).setValue(now); + nextFreeRow++; + } + return ContentService.createTextOutput("Successfully wrote: " + + e.parameter.value + "\ninto spreadsheet."); + //Logger.log(str); + } + catch(f){ + return ContentService.createTextOutput("Error in parsing appendRow"); + } + } else { + return ContentService.createTextOutput("Error wrote: value = " + + e.parameter.value + " , id = "+e.parameter.id); + } +} + diff --git a/src/modules/virtual/GoogleSheet/modinfo.json b/src/modules/virtual/GoogleSheet/modinfo.json new file mode 100644 index 00000000..8fa0c5fb --- /dev/null +++ b/src/modules/virtual/GoogleSheet/modinfo.json @@ -0,0 +1,69 @@ +{ + "menuSection": "virtual_elments", + "configItem": [ + { + "global": 0, + "name": "GoogleSheet", + "type": "Reading", + "subtype": "GoogleSheet", + "id": "sheet", + "widget": "nil", + "page": "", + "descr": "", + "int": 5, + "logid": "", + "scid": "", + "shname": "Logger" + } + ], + "about": { + "authorName": "Bubnov Mikhail", + "authorContact": "https://t.me/Mit4bmw", + "authorGit": "https://github.com/Mit4el", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "@AVAKS", + "moduleName": "GoogleSheet", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 10, + "esp8266_4mb": 10 + }, + "title": "Логирование в Google таблицы", + "moduleDesc": "Расширение позволяющее логировать любую величину в Google таблицы. Инструкция https://drive.google.com/file/d/1VuI06arObNfH3rsOSHKww1tyhfgqXd9F/view?usp=sharing", + "propInfo": { + "int": "Интервал логирования в минутах", + "logid": "ID величины которую будем логировать", + "scid": "Идентификатор развертывания Google Apps (script id)", + "shname": "Наименование листа в таблице (sheet name)" + }, + "retInfo": "", + "funcInfo": [ + { + "name": "logGoogle", + "descr": "Использовать не чаще раз в минуту! Логирование элементов в GoogleSheet, ID элементов указывать через запятую, от одного до N (проверено на 16шт). В данной функции поиск элементов идет по их значению, если несколько элементов с одинаковым значение, может быть не правильно указан его id в Таблице.", + "params": [ + "id Идентификатор 1-го элеменета", + "id Идентификатор N-го элеменета" + ] + }, + { + "name": "logGoogleAny", + "descr": "Использовать не чаще раз в минуту! Запись произвольных данных в GoogleSheet", + "params": [ + "Наименование данных", + "Значение для записи" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], + "esp8266_4mb": [] + } +} \ No newline at end of file diff --git a/src/modules/virtual/Loging/Loging.cpp b/src/modules/virtual/Loging/Loging.cpp index 9671632e..362fcdeb 100644 --- a/src/modules/virtual/Loging/Loging.cpp +++ b/src/modules/virtual/Loging/Loging.cpp @@ -16,14 +16,14 @@ class Loging : public IoTItem { int _wsNum = -1; int points; - //int keepdays; + // int keepdays; IoTItem *dateIoTItem; String prevDate = ""; bool firstTimeInit = true; - long interval; + // long interval; public: Loging(String parameters) : IoTItem(parameters) { @@ -34,9 +34,10 @@ class Loging : public IoTItem { points = 300; SerialPrint("E", F("Loging"), "'" + id + "' user set more points than allowed, value reset to 300"); } - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60; // приводим к милисекундам - //jsonRead(parameters, F("keepdays"), keepdays, false); + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); + // jsonRead(parameters, F("keepdays"), keepdays, false); // создадим экземпляр класса даты dateIoTItem = (IoTItem *)getAPI_Date("{\"id\": \"" + id + "-date\",\"int\":\"20\",\"subtype\":\"date\"}"); @@ -275,7 +276,7 @@ class Loging : public IoTItem { void deleteLastFile() { IoTFSInfo tmp = getFSInfo(); SerialPrint("i", "Loging", String(tmp.freePer) + " % free flash remaining"); - if (tmp.freePer <= 20.00) { + if (tmp.freePer <= 50.00) { String dir = "/lg/" + id; filesList = getFilesList(dir); int i = 0; @@ -299,22 +300,20 @@ class Loging : public IoTItem { _wsNum = wsNum; } - String getValue() { - return ""; - } + String getValue() { return ""; } - void loop() { - if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) { - prevMillis = millis(); - if (interval != 0) { - this->doByInterval(); - } - } - } - } + // void loop() { + // if (enableDoByInt) { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) { + // prevMillis = millis(); + // if (interval != 0) { + // this->doByInterval(); + // } + // } + // } + // } void regEvent(const String &value, const String &consoleInfo, bool error = false, bool genEvent = true) { String userDate = getItemValue(id + "-date"); @@ -330,14 +329,10 @@ class Loging : public IoTItem { } // просто максимальное количество точек - int calculateMaxCount() { - return 86400; - } + int calculateMaxCount() { return 86400; } // путь вида: /lg/log/1231231.txt - unsigned long getFileUnixLocalTime(String path) { - return gmtTimeToLocal(selectToMarkerLast(deleteToMarkerLast(path, "."), "/").toInt() + START_DATETIME); - } + unsigned long getFileUnixLocalTime(String path) { return gmtTimeToLocal(selectToMarkerLast(deleteToMarkerLast(path, "."), "/").toInt() + START_DATETIME); } void setValue(const IoTValue &Value, bool genEvent = true) { value = Value; this->SetDoByInterval(String(value.valD)); @@ -399,6 +394,4 @@ class Date : public IoTItem { } }; -void *getAPI_Date(String param) { - return new Date(param); -} +void *getAPI_Date(String param) { return new Date(param); } diff --git a/src/modules/virtual/Loging/modinfo.json b/src/modules/virtual/Loging/modinfo.json index 8fbdcbe3..c5bbf6b4 100644 --- a/src/modules/virtual/Loging/modinfo.json +++ b/src/modules/virtual/Loging/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -51,7 +51,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/LogingDaily/LogingDaily.cpp b/src/modules/virtual/LogingDaily/LogingDaily.cpp index 6e7e87b4..b6f5b1e0 100644 --- a/src/modules/virtual/LogingDaily/LogingDaily.cpp +++ b/src/modules/virtual/LogingDaily/LogingDaily.cpp @@ -25,7 +25,7 @@ class LogingDaily : public IoTItem { String prevDate = ""; bool firstTimeInit = true; - long interval; + // long interval; public: LogingDaily(String parameters) : IoTItem(parameters) { @@ -36,12 +36,13 @@ class LogingDaily : public IoTItem { jsonRead(parameters, F("telegram"), telegram); jsonRead(parameters, F("descr"), descr); - if (points > 365) { - points = 365; - SerialPrint("E", F("LogingDaily"), "'" + id + "' user set more points than allowed, value reset to 365"); + if (points > 200) { + points = 200; + SerialPrint("E", F("LogingDaily"), "'" + id + "' user set more points than allowed, value reset to 200"); } - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60; // приводим к милисекундам + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); } void doByInterval() { @@ -84,7 +85,7 @@ class LogingDaily : public IoTItem { if (telegram == 1) { String msg = descr + ": total " + String(currentValue) + ", consumed " + String(difference); for (std::list::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it) { - if ((*it)->getSubtype() == "TelegramLT" || "Telegram") { + if ((*it)->getSubtype() == "TelegramLT" || "Telegram" || "Telegram_v2") { (*it)->sendTelegramMsg(false, msg); } } @@ -221,25 +222,21 @@ class LogingDaily : public IoTItem { _wsNum = wsNum; } - String getValue() { - return ""; - } + String getValue() { return ""; } - void loop() { - if (enableDoByInt) { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) { - prevMillis = millis(); - this->doByInterval(); - } - } - } + // void loop() { + // if (enableDoByInt) { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } // просто максимальное количество точек - int calculateMaxCount() { - return 86400; - } + int calculateMaxCount() { return 86400; } void onModuleOrder(String &key, String &value) { if (key == "defvalue") { diff --git a/src/modules/virtual/LogingDaily/modinfo.json b/src/modules/virtual/LogingDaily/modinfo.json index 8118c82a..4f0a5f09 100644 --- a/src/modules/virtual/LogingDaily/modinfo.json +++ b/src/modules/virtual/LogingDaily/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -13,7 +13,7 @@ "num": 1, "int": 1, "logid": "t", - "points": 365, + "points": 200, "telegram": 0, "test": 0, "btn-defvalue": 0, @@ -44,7 +44,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/Math/Math.cpp b/src/modules/virtual/Math/Math.cpp new file mode 100644 index 00000000..7b14c396 --- /dev/null +++ b/src/modules/virtual/Math/Math.cpp @@ -0,0 +1,69 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include + +class IoTMath : public IoTItem { +private: + + time_t convertTime(float day, float month, float year, float hour, float minute) { + // Преобразование из float в int + int d = static_cast(day); + int m = static_cast(month); + int y = static_cast(year); + int h = static_cast(hour); + int min = static_cast(minute); + + if (d < 1 || d > 31 || m < 1 || m > 12 || y < 1900 || h < 0 || h > 23 || min < 0 || min > 59) { + SerialPrint("E", F("IoTMath"), F("Invalid date or time parameters!")); + return -1; + } + + // Структура для хранения даты и времени + struct tm t; + t.tm_year = y - 1900; + t.tm_mon = m - 1; + t.tm_mday = d; + t.tm_hour = h; + t.tm_min = min; + t.tm_sec = 0; + t.tm_isdst = -1; // Пусть система сама определяет DST + + return mktime(&t); + } + +public: + IoTMath(String parameters) : IoTItem(parameters) {} + + IoTValue execute(String command, std::vector ¶m) { + if(command == "map" && param.size() == 5) { + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = map(param[0].valD, param[1].valD, param[2].valD, param[3].valD, param[4].valD); + //SerialPrint("i", F("IoTMath"), F("Mapping value done.")); + return valTmp; + } else if(command == "convertTime" && param.size() == 5) { + time_t unixTime = convertTime(param[0].valD, param[1].valD, param[2].valD, param[3].valD, param[4].valD); + + if (unixTime == -1) { + SerialPrint("E", F("IoTMath"), F("Failed to convert time.")); + return {}; + } + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = unixTime; + //SerialPrint("i", F("IoTMath"), F("Time conversion done.")); + return valTmp; + } + + SerialPrint("E", F("IoTMath"), F("Unknown command or wrong parameters.")); + return {}; + } +}; + +void *getAPI_IoTMath(String subtype, String param) { + if (subtype == F("IoTMath")) { + return new IoTMath(param); + } + return nullptr; +} diff --git a/src/modules/virtual/Math/modinfo.json b/src/modules/virtual/Math/modinfo.json new file mode 100644 index 00000000..6265e85b --- /dev/null +++ b/src/modules/virtual/Math/modinfo.json @@ -0,0 +1,62 @@ +{ + "menuSection": "virtual_elments", + "configItem": [ + { + "global": 0, + "name": "Math library", + "type": "Reading", + "subtype": "IoTMath", + "id": "math", + "widget": "anydataValue", + "page": "Математика", + "descr": "" + } + ], + "about": { + "authorName": "Berenbaum Maxim", + "authorContact": "https://t.me/Bermax_sys", + "authorGit": "https://github.com/xpohuk8", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "Ilya Belyakov", + "moduleName": "IoTMath", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Модуль Math", + "moduleDesc": "Модуль для расчета значений с использованием математических функций. Подходит для коррекции показаний датчиков.", + "propInfo": { + }, + "funcInfo": [ + { + "name": "map", + "descr": "Аналог функции map", + "params": [ + "Значение1", "Значение2", "Значение3", "Значение4" + ] + }, + { + "name": "convertTime", + "descr": "Перевести время из формата d-m-Y H:i:s например, 13-08-2023 16:24:00 в юникс-время ", + "params": [ + "tm.convertTime(13, 08, 2023, 16, 24); - передаем пять целых чисел. секунды подставятся в ноль" + ] + } + ] + }, + "defActive": true, + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32s2_4mb": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} diff --git a/src/modules/virtual/Timer/modinfo.json b/src/modules/virtual/Timer/modinfo.json index def3437c..c0b02ea3 100644 --- a/src/modules/virtual/Timer/modinfo.json +++ b/src/modules/virtual/Timer/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -58,19 +58,30 @@ { "name": "int", "descr": "Можно изменить шаг тиков.", - "params": ["Число в секундах"] + "params": [ + "Число в секундах" + ] }, { "name": "setInitCountDown", "descr": "Меняем начальное значение счетчика, устанавливаемое после сброса.", - "params": ["Число в секундах"] + "params": [ + "Число в секундах" + ] } ] }, "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/VButton/modinfo.json b/src/modules/virtual/VButton/modinfo.json index 36e5fabf..366e6b51 100644 --- a/src/modules/virtual/VButton/modinfo.json +++ b/src/modules/virtual/VButton/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -36,7 +36,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/Variable/modinfo.json b/src/modules/virtual/Variable/modinfo.json index 2a50d1c2..49bff50c 100644 --- a/src/modules/virtual/Variable/modinfo.json +++ b/src/modules/virtual/Variable/modinfo.json @@ -1,5 +1,5 @@ { - "menuSection": "Виртуальные элементы", + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -96,7 +96,14 @@ "defActive": true, "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], diff --git a/src/modules/virtual/VariableColor/VariableColor.cpp b/src/modules/virtual/VariableColor/VariableColor.cpp deleted file mode 100644 index 12e0ff34..00000000 --- a/src/modules/virtual/VariableColor/VariableColor.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "Global.h" -#include "classes/IoTItem.h" - -// дочь - родитель -class VariableColor : public IoTItem -{ -private: -public: - VariableColor(String parameters) : IoTItem(parameters) - { - } - - void doByInterval() - { - } - - // событие когда пользователь подключается приложением или веб интерфейсом к усройству - void onMqttWsAppConnectEvent() - { - SerialPrint("i", "Connecting", "Dashbord open "); - regEvent(value.valS, "VariableColor", false, true); - } - - IoTValue execute(String command, std::vector ¶m) - { - if (command == "widget" && param.size() == 2) - { - String json = "{}"; - jsonWriteStr(json, param[0].valS, param[1].valS); - sendSubWidgetsValues(_id, json); - } - return {}; - } -}; - -void *getAPI_VariableColor(String subtype, String param) -{ - if (subtype == F("VariableColor")) - { - return new VariableColor(param); - } - else - { - return nullptr; - } -} diff --git a/src/modules/virtual/VariableColor/modinfo.json b/src/modules/virtual/VariableColor/modinfo.json deleted file mode 100644 index e03c1ff9..00000000 --- a/src/modules/virtual/VariableColor/modinfo.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "menuSection": "Виртуальные элементы", - "configItem": [ - { - "global": 0, - "name": "Цветной текст", - "type": "Reading", - "subtype": "VariableColor", - "id": "color", - "needSave": 0, - "widget": "anydataDef", - "page": "Вывод", - "descr": "Цветной текст", - "val": "...", - "round": 0 - } - ], - "about": { - "authorName": "AVAKS", - "authorContact": "https://t.me/@avaks_dev", - "authorGit": "https://github.com/avaksru", - "specialThanks": "", - "moduleName": "VariableColor", - "moduleVersion": "1", - "usedRam": { - "esp32_4mb": 15, - "esp8266_4mb": 15 - }, - "title": "Цветной текст", - "moduleDesc": "Текст с возможностью динамического изменения цвета", - "propInfo": { - "val": "Значение при старте" - } - }, - - "defActive": true, - - "usedLibs": { - "esp32_4mb": [], - "esp8266_4mb": [], - "esp8266_1mb": [], - "esp8266_1mb_ota": [], - "esp8285_1mb": [], - "esp8285_1mb_ota": [], - "esp8266_2mb": [], - "esp8266_2mb_ota": [] - } -} \ No newline at end of file diff --git a/src/modules/virtual/Weather/Weather.cpp b/src/modules/virtual/Weather/Weather.cpp index 2ff85df4..b173593f 100644 --- a/src/modules/virtual/Weather/Weather.cpp +++ b/src/modules/virtual/Weather/Weather.cpp @@ -3,7 +3,7 @@ #include long prevWeatherMillis = millis() - 60001; -StaticJsonDocument Weatherdoc; +//StaticJsonDocument Weatherdoc; extern IoTGpio IoTgpio; class Weather : public IoTItem @@ -11,22 +11,23 @@ class Weather : public IoTItem private: String _location; String _param; - long interval; - + // long interval; + DynamicJsonDocument Weatherdoc; public: - Weather(String parameters) : IoTItem(parameters) + Weather(String parameters) : Weatherdoc(1024), IoTItem(parameters) { _location = jsonReadStr(parameters, "location"); _param = jsonReadStr(parameters, "param"); - jsonRead(parameters, F("int"), interval); - interval = interval * 1000 * 60 * 60; // интервал проверки погоды в часах + long interval; + jsonRead(parameters, F("int"), interval); // интервал проверки погоды в часах + setInterval(interval * 60 * 60); } void getWeather() { String ret; - if (WiFi.status() == WL_CONNECTED) + if (isNetworkActive()) { // char c; String payload; @@ -113,19 +114,21 @@ public: regEvent(value.valS, "Weather"); } } - void loop() - { - if (enableDoByInt) - { - currentMillis = millis(); - difference = currentMillis - prevMillis; - if (difference >= interval) - { - prevMillis = millis(); - this->doByInterval(); - } - } - } + + // void loop() + // { + // if (enableDoByInt) + // { + // currentMillis = millis(); + // difference = currentMillis - prevMillis; + // if (difference >= interval) + // { + // prevMillis = millis(); + // this->doByInterval(); + // } + // } + // } + IoTValue execute(String command, std::vector ¶m) { if (command == "get") diff --git a/src/modules/virtual/Weather/modinfo.json b/src/modules/virtual/Weather/modinfo.json index cbdad91f..928bc442 100644 --- a/src/modules/virtual/Weather/modinfo.json +++ b/src/modules/virtual/Weather/modinfo.json @@ -1,6 +1,5 @@ { - "menuSection": "Виртуальные элементы", - + "menuSection": "virtual_elments", "configItem": [ { "global": 0, @@ -19,7 +18,6 @@ "val": "..." } ], - "about": { "authorName": "AVAKS", "authorContact": "https://t.me/@avaks_dev", @@ -39,12 +37,17 @@ "int": "Интервал запроса погоды в часах" } }, - "defActive": false, - "usedLibs": { "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32cam_4mb": [], + "esp32_16mb": [], + "esp32s2_4mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], "esp8266_4mb": [], + "esp8266_16mb": [], "esp8266_1mb": [], "esp8266_1mb_ota": [], "esp8285_1mb": [], @@ -52,4 +55,4 @@ "esp8266_2mb": [], "esp8266_2mb_ota": [] } -} +} \ No newline at end of file diff --git a/src/modules/virtual/owmWeather/modinfo.json b/src/modules/virtual/owmWeather/modinfo.json new file mode 100644 index 00000000..43d3708a --- /dev/null +++ b/src/modules/virtual/owmWeather/modinfo.json @@ -0,0 +1,102 @@ +{ + "menuSection": "virtual_elments", + + "configItem": [ + { + "global": 0, + "name": "Погода OWM", + "type": "Reading", + "subtype": "owmWeather", + "id": "owm", + "needSave": 0, + "widget": "nil", + "page": "Погода", + "descr": "Температура на улице", + "int": 30, + "API_key": "", + "city": "Moscow", + "lon": "37.54", + "lat": "57.74", + "lang": "ru", + "param": "", + "round": 1, + "val": "...", + "debug": 0 + } + ], + + "about": { + "authorName": "Serghei Crasnicov, v2.0 Mikhail Bubnov", + "authorContact": "https://t.me/Serghei63 https://t.me/Mit4bmw", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "AVAKS", + "moduleName": "owmWeather", + "moduleVersion": "2.1", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "temp", + "humidity", + "pressure", + "speed", + "deg", + "all", + "main", + "description", + "icon", + "sunrise", + "sunset", + "name" + ], + "title": "Погода", + "moduleDesc": "Получение погоды из интернет OpenWeatherMap", + "propInfo": { + "param": "Тип текущего Item: temp - температура, humidity - влажность, pressure - давление, speed - скорость ветра, deg - направление ветра, all - процент облачности, sunrise - рассвет, sunset - закат, description - Погодные условия, icon - код иконки, name - город. Если оставить пустым пудет искать и публиковать при изменении в Items с именами wea_temp и т.д. wea_...", + "int": "Интервал запроса погоды в минутах", + "API_key": "API ключ", + "city": "Название города, через запятую можно уочнить код страны. Наример Moscow или Moscow,ru или Москва. Если город не задан будут использоваться координаты. OWM рекомендует координаты", + "lon": "Долгота, при использовании координат, будет автоматически выбран ближайший город", + "lat": "Широта, при использовании координат, будет автоматически выбран ближайший город", + "lang": "Язык используемый в ответах OpenWetaherMap", + "debug":"1 - выводить дополнительный лог в сериал" + }, + "funcInfo": [ + { + "name": "sunrise", + "descr": "Функция астрологического таймера, можно задать время до/после рассвета, вернётся количество минут до сработки или после (-). После заката будет возвращать +999 (Значение <=0 будет от сработки и до заката), когда 0 - значит время подошло", + "params": ["количество минут до(-)/после(+) рассвета"] + }, + { + "name": "sunset", + "descr": "Функция астрологического таймера, можно задать время до/после заката, вернётся количество минут до сработки или после (-), когда 0 - значит время подошло", + "params": ["количество минут до(-)/после(+) заката"] + }, + { + "name": "night", + "descr": "Вернет 1 если ночь (уже закат) и 0 если день (рассвет)", + "params": [] + } + ] + }, + + "defActive": true, + + "usedLibs": { + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp32s2_4mb": [], + "esp32_16mb": [], + "esp32s3_16mb": [], + "esp32c3m_4mb": [], + "esp8266_4mb": [], + "esp8266_16mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} diff --git a/src/modules/virtual/owmWeather/owmWeather.cpp b/src/modules/virtual/owmWeather/owmWeather.cpp new file mode 100644 index 00000000..6204db5c --- /dev/null +++ b/src/modules/virtual/owmWeather/owmWeather.cpp @@ -0,0 +1,372 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#include "NTP.h" +// long prevWeatherMillis = millis() - 60001; +// TODO Зачем так много??? +// StaticJsonDocument Weatherdoc1; + +extern IoTGpio IoTgpio; +class owmWeather : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + + String _location; + String _param; + // long interval; + String _API_key; + String _city = ""; + String _lat = ""; + String _lon = ""; + String _lang = ""; + bool _debug = false; + DynamicJsonDocument Weatherdoc1; + unsigned long _sunsetTime = 0; + unsigned long _sunriseTime = 0; + uint32_t _tzone = 0; + +public: + owmWeather(String parameters) : Weatherdoc1(1024), IoTItem(parameters) + { + _API_key = jsonReadStr(parameters, "API_key"); + // _ID_sity = jsonReadStr(parameters, "ID_sity"); + if (!jsonRead(parameters, "city", _city)) + _city = ""; + jsonRead(parameters, "lon", _lon); + jsonRead(parameters, "lat", _lat); + jsonRead(parameters, "lang", _lang); + jsonRead(parameters, "param", _param); + jsonRead(parameters, "debug", _debug); + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); + } + + void getWeather() + { + String ret; + + if (isNetworkActive()) + { + String urlReq; + if (_city != "") + { + // Для нового API + // request = "http://api.openweathermap.org/geo/1.0/direct?q=" _сity + "&limit=1&appid=" + _API_key; + // Устарело, но пока работает + urlReq = "http://api.openweathermap.org/data/2.5/weather?q=" + _city + "&units=metric&lang=" + _lang + "&appid=" + _API_key; + } + else + { + // Получаем название города по координатам. Новый API + // request = "http://api.openweathermap.org/geo/1.0/reverse?lat=" + _lat + "&lon=" + _lon + "&limit=1&appid=" + _API_key; + //[0].local_names:{ru:Москва,...} + urlReq = "http://api.openweathermap.org/data/2.5/weather?lat=" + _lat + "&lon=" + _lon + "&units=metric&lang=" + _lang + "&appid=" + _API_key; + } + + WiFiClient client; + HTTPClient http; + String payload; + bool find = false; + http.setTimeout(500); + http.begin(client, urlReq); // urlCurrent + // http.begin(client, "http://api.openweathermap.org/data/2.5/weather?id=" + _ID_sity + "&appid=" + _API_key + "&units=metric"); + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + int httpCode = http.GET(); + + if (httpCode > 0) + { + ret = httpCode; + + if (httpCode == HTTP_CODE_OK) + { + payload = http.getString(); + + deserializeJson(Weatherdoc1, payload); + // ret += payload; + if (_debug) + SerialPrint("i", "Weatherdoc1", "memoryUsage: " + String(Weatherdoc1.memoryUsage())); + } + } + else + { + ret = http.errorToString(httpCode).c_str(); + } + if (_debug) + { + SerialPrint("<-", F("getOwmWeather"), urlReq); + SerialPrint("->", F("getOwmWeather"), "server: " + ret); + } + http.end(); + } + } + + void doByInterval() + { + + getWeather(); + if (jsonReadStr(Weatherdoc1["main"], "temp", true) != "null") + { + _tzone = Weatherdoc1["timezone"].as(); + _sunriseTime = std::atoll(jsonReadStr(Weatherdoc1["sys"], "sunrise", true).c_str()); + _sunriseTime = _sunriseTime + _tzone; + _sunsetTime = std::atoll(jsonReadStr(Weatherdoc1["sys"], "sunset", true).c_str()); + _sunsetTime = _sunsetTime + _tzone; + + publishNew("main", "temp"); + publishNew("main", "pressure"); + publishNew("main", "humidity"); + publishNew("wind", "speed"); + publishNew("wind", "deg"); + publishNew("clouds", "all"); + publishNew("weather", "main"); + publishNew("weather", "description"); + publishNew("weather", "icon"); + publishNew("sys", "sunrise"); + publishNew("sys", "sunset"); + publishNew("", "name"); + + if (_param == "temp") + { + value.valS = jsonReadStr(Weatherdoc1["main"], "temp", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "pressure") + { + // value.valS = jsonReadStr(Weatherdoc1["main"], "pressure", true); + int tval; + jsonRead(Weatherdoc1["main"], "pressure", tval, true); + regEvent(tval / 1.333, "owmWeather"); + } + else if (_param == "humidity") + { + value.valS = jsonReadStr(Weatherdoc1["main"], "humidity", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "speed") + { + value.valS = jsonReadStr(Weatherdoc1["wind"], "speed", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "deg") + { + value.valS = jsonReadStr(Weatherdoc1["wind"], "deg", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "all") + { + value.valS = jsonReadStr(Weatherdoc1["clouds"], "all", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "main") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "main", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "description") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "description", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "icon") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "icon", true); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "sunrise") + { + value.valS = getTimeDotFormatedFromUnix(_sunriseTime); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "sunset") + { + value.valS = getTimeDotFormatedFromUnix(_sunsetTime); + regEvent(value.valS, "owmWeather"); + } + else if (_param == "name") + { + value.valS = Weatherdoc1["name"].as(); + regEvent(value.valS, "owmWeather"); + } + // value.isDecimal = false; + + // regEvent(value.valS, "owmWeather"); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + IoTValue value = {}; + if (command == "get") + { + // getWeather(); + doByInterval(); + } + else if (command == "night") + { + if (_sunsetTime == 0 || !isTimeSynch) + { + SerialPrint("i", ("AstroTimer"), "Not TimeSynch or Weather data server"); + value.valD = 0; + return value; + } + long dt_cur = getSystemTime() + _tzone; + // Если светло + if (dt_cur >= _sunriseTime && dt_cur < _sunsetTime) + value.valD = 0; + else // если темно + value.valD = 1; + if (_debug) + { + SerialPrint("i", ("AstroTimer"), "night: " + String(value.valD)); + } + } + + else if (command == "sunset") + { + if (_sunsetTime == 0 || !isTimeSynch) + { + SerialPrint("i", ("AstroTimer"), "Not TimeSynch or Weather data server"); + value.valD = 999; + return value; + } + long dt_cur = getSystemTime() + _tzone; + if (param.size()) + { + if (param[0].isDecimal) + { + long dt_set = (_sunsetTime + (int)(param[0].valD * 60)); + long dt = dt_set - dt_cur; + value.valD = dt / 60; + if (_debug) + { + SerialPrint("i", ("AstroTimer"), "set: " + getTimeDotFormatedFromUnix(dt_set) + " time: " + getTimeDotFormatedFromUnix(dt_cur) + " sunset: " + getTimeDotFormatedFromUnix(_sunsetTime) + " Dt: " + String(param[0].valD) + " diff: " + String(value.valD)); + } + } + } + } + else if (command == "sunrise") + { + if (_sunriseTime == 0 || !isTimeSynch) + { + SerialPrint("i", ("AstroTimer"), "Not TimeSynch or Weather data server"); + value.valD = 999; + return value; + } + long dt_cur = getSystemTime() + _tzone; + if (dt_cur >= _sunsetTime) + { + SerialPrint("i", ("AstroTimer"), "УЖЕ Закат, таймер не считаем time: " + getTimeDotFormatedFromUnix(dt_cur) + " diff: " + String(value.valD)); + value.valD = 999; + return value; + } + if (param.size()) + { + if (param[0].isDecimal) + { + long dt_set = (_sunriseTime + (int)(param[0].valD * 60)); + long dt = dt_set - dt_cur; + value.valD = dt / 60; + if (_debug) + { + SerialPrint("i", ("AstroTimer"), "set: " + getTimeDotFormatedFromUnix(dt_set) + " time: " + getTimeDotFormatedFromUnix(dt_cur) + " sunrise: " + getTimeDotFormatedFromUnix(_sunriseTime) + " Dt: " + String(param[0].valD) + " diff: " + String(value.valD)); + } + } + } + } + return value; + } + + // проверяем если пришедшее значение отличается от предыдущего регистрируем событие + void publishNew(String root, String param) + { + IoTItem *tmp = findIoTItem("wea_" + param); + if (!tmp) + return; + + if (root == "weather") + { // ☔⛅❄🌤🌥🌦🌧🌨🌩🌫🌞 + String icn = Weatherdoc1[root][0]["icon"].as(); + if (icn == "01d" || icn == "01n") + icn = "🌞"; + else if (icn == "02d" || icn == "02n") + icn = "🌤"; + else if (icn == "03d" || icn == "03n") + icn = "🌥"; + else if (icn == "04d" || icn == "04n") + icn = "🌫"; + else if (icn == "09d" || icn == "09n") + icn = "🌧"; + else if (icn == "10d" || icn == "10n") + icn = "🌦"; + else if (icn == "11d" || icn == "11n") + icn = "🌩"; + else if (icn == "13d" || icn == "13n") + icn = "❄"; + else + icn = ""; + if (Weatherdoc1[root][0][param].as() != tmp->value.valS) + { + if (param == "description") + tmp->setValue(Weatherdoc1[root][0][param].as() + icn, true); + else + tmp->setValue(Weatherdoc1[root][0][param].as(), true); + } + } + else if (root == "") + { + if (Weatherdoc1[param].as() != tmp->value.valS) + { + tmp->setValue(Weatherdoc1[param].as(), true); + } + } + else if (root == "sys") + { + if (Weatherdoc1[root][param].as() != tmp->value.valS) + { + if (param == "sunrise") + { + tmp->setValue(getTimeDotFormatedFromUnix(_sunriseTime), true); + } + else if (param == "sunset") + { + tmp->setValue(getTimeDotFormatedFromUnix(_sunsetTime), true); + } + else + { + tmp->setValue(Weatherdoc1[root][param].as(), true); + } + } + } + else + { + if (Weatherdoc1[root][param].as() != tmp->value.valS) + { + if (param == "pressure") + { + int tval = Weatherdoc1[root][param].as(); + tmp->setValue(String(tval / 1.333), true); + } + else + { + tmp->setValue(Weatherdoc1[root][param].as(), true); + } + } + } + } + + ~owmWeather(){}; +}; + +void *getAPI_owmWeather(String subtype, String param) +{ + if (subtype == F("owmWeather")) + { + return new owmWeather(param); + } + else + { + return nullptr; + } +} diff --git a/src/utils/FileUtils.cpp b/src/utils/FileUtils.cpp index f59316d5..c12bffbe 100644 --- a/src/utils/FileUtils.cpp +++ b/src/utils/FileUtils.cpp @@ -124,8 +124,8 @@ const String readFile(const String& filename, size_t max_size) { size_t size = file.size(); if (size > max_size) { file.close(); - if (path == "/config.json") - return "[]"; + // что это за бред! + if (path == "/config.json") return "[]"; return "large"; } String temp = file.readString(); @@ -133,9 +133,7 @@ const String readFile(const String& filename, size_t max_size) { return temp; } -const String filepath(const String& filename) { - return filename.startsWith("/") ? filename : "/" + filename; -} +const String filepath(const String& filename) { return filename.startsWith("/") ? filename : "/" + filename; } bool cutFile(const String& src, const String& dst) { String srcPath = filepath(src); @@ -288,9 +286,7 @@ String getFilesList(String& directory) { } #if defined(ESP8266) -bool getInfo(FSInfo& info) { - return FileFS.info(info); -} +bool getInfo(FSInfo& info) { return FileFS.info(info); } // Информация о ФС IoTFSInfo getFSInfo() { diff --git a/src/utils/JsonUtils.cpp b/src/utils/JsonUtils.cpp index fa89de73..9a616d26 100644 --- a/src/utils/JsonUtils.cpp +++ b/src/utils/JsonUtils.cpp @@ -2,16 +2,12 @@ #include "utils/FileUtils.h" // new================================================================================ -String jsonReadStrDoc(DynamicJsonDocument& doc, String name) { - return doc[name].as(); -} +String jsonReadStrDoc(DynamicJsonDocument &doc, String name) { return doc[name].as(); } -void jsonWriteStrDoc(DynamicJsonDocument& doc, String name, String value) { - doc[name] = value; -} +void jsonWriteStrDoc(DynamicJsonDocument &doc, String name, String value) { doc[name] = value; } // new============================================================================== -bool jsonRead(const String& json, String key, long& value, bool e) { +bool jsonRead(const String &json, String key, long &value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -31,7 +27,7 @@ bool jsonRead(const String& json, String key, long& value, bool e) { return true; } -bool jsonRead(const String& json, String key, float& value, bool e) { +bool jsonRead(const String &json, String key, float &value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -51,7 +47,7 @@ bool jsonRead(const String& json, String key, float& value, bool e) { return true; } -bool jsonRead(const String& json, String key, String& value, bool e) { +bool jsonRead(const String &json, String key, String &value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -71,14 +67,14 @@ bool jsonRead(const String& json, String key, String& value, bool e) { return true; } -bool jsonRead(const String& json, String key, bool& value, bool e) { +bool jsonRead(const String &json, String key, bool &value, bool e) { int lvalue = value; bool ret = jsonRead(json, key, lvalue, e); value = lvalue; return ret; } -bool jsonRead(const String& json, String key, int& value, bool e) { +bool jsonRead(const String &json, String key, int &value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -98,8 +94,35 @@ bool jsonRead(const String& json, String key, int& value, bool e) { return true; } +bool jsonReadArray(const String &json, String key, std::vector &jArray, bool e) { + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + DeserializationError error = deserializeJson(doc, json); + if (error) { + if (e) { + SerialPrint("E", F("jsonReadArray"), error.f_str()); + jsonErrorDetected(); + } + return false; + } else if (!doc.containsKey(key)) { + if (e) { + SerialPrint("E", F("jsonReadArray"), key + " missing in " + json); + jsonErrorDetected(); + } + return false; + } + + if (doc[key].is()) { + for (int8_t i = 0; i < doc[key].size(); i++) jArray.push_back(doc[key][i].as()); + + } else { + jArray.push_back(doc[key].as()); + } + + return true; +} + // new============================================================================== -bool jsonWriteStr_(String& json, const String& key, const String& value, bool e) { +bool jsonWriteStr_(String &json, const String &key, const String &value, bool e) { bool ret = true; DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); @@ -116,7 +139,7 @@ bool jsonWriteStr_(String& json, const String& key, const String& value, bool e) return ret; } -bool jsonWriteBool_(String& json, const String& key, bool value, bool e) { +bool jsonWriteBool_(String &json, const String &key, bool value, bool e) { bool ret = true; DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); @@ -133,7 +156,7 @@ bool jsonWriteBool_(String& json, const String& key, bool value, bool e) { return ret; } -bool jsonWriteInt_(String& json, const String& key, int value, bool e) { +bool jsonWriteInt_(String &json, const String &key, int value, bool e) { bool ret = true; DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); @@ -150,7 +173,7 @@ bool jsonWriteInt_(String& json, const String& key, int value, bool e) { return ret; } -bool jsonWriteFloat_(String& json, const String &key, float value, bool e) { +bool jsonWriteFloat_(String &json, const String &key, float value, bool e) { bool ret = true; DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); @@ -167,7 +190,7 @@ bool jsonWriteFloat_(String& json, const String &key, float value, bool e) { return ret; } -void writeUint8tValueToJsonString(uint8_t* payload, size_t length, size_t headerLenth, String& json) { +void writeUint8tValueToJsonString(uint8_t *payload, size_t length, size_t headerLenth, String &json) { String payloadStr; payloadStr.reserve(length + 1); for (size_t i = headerLenth; i < length; i++) { @@ -176,7 +199,7 @@ void writeUint8tValueToJsonString(uint8_t* payload, size_t length, size_t header jsonMergeObjects(json, payloadStr); } -bool jsonMergeObjects(String& json1, String& json2, bool e) { +bool jsonMergeObjects(String &json1, String &json2, bool e) { bool ret = true; DynamicJsonDocument doc1(JSON_BUFFER_SIZE); DeserializationError error1 = deserializeJson(doc1, json1); @@ -202,7 +225,7 @@ void jsonMergeDocs(JsonObject dest, JsonObjectConst src) { } // depricated====================================================================== -String jsonReadStr(const String& json, String name, bool e) { +String jsonReadStr(const String &json, String name, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -214,7 +237,7 @@ String jsonReadStr(const String& json, String name, bool e) { return doc[name].as(); } -boolean jsonReadBool(const String& json, String name, bool e) { +boolean jsonReadBool(const String &json, String name, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -226,7 +249,7 @@ boolean jsonReadBool(const String& json, String name, bool e) { return doc[name].as(); } -int jsonReadInt(const String& json, String name, bool e) { +int jsonReadInt(const String &json, String name, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -238,8 +261,20 @@ int jsonReadInt(const String& json, String name, bool e) { return doc[name].as(); } +long int jsonReadLInt(const String &json, String name, bool e) { + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + DeserializationError error = deserializeJson(doc, json); + if (error) { + if (e) { + SerialPrint("E", F("jsonRead"), error.f_str()); + jsonErrorDetected(); + } + } + return doc[name].as(); +} + // depricated======================================================================== -String jsonWriteStr(String& json, String name, String value, bool e) { +String jsonWriteStr(String &json, String name, String value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -254,7 +289,7 @@ String jsonWriteStr(String& json, String name, String value, bool e) { return json; } -String jsonWriteBool(String& json, String name, boolean value, bool e) { +String jsonWriteBool(String &json, String name, boolean value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -269,7 +304,7 @@ String jsonWriteBool(String& json, String name, boolean value, bool e) { return json; } -String jsonWriteInt(String& json, String name, int value, bool e) { +String jsonWriteInt(String &json, String name, int value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -284,7 +319,7 @@ String jsonWriteInt(String& json, String name, int value, bool e) { return json; } -String jsonWriteFloat(String& json, String name, float value, bool e) { +String jsonWriteFloat(String &json, String name, float value, bool e) { DynamicJsonDocument doc(JSON_BUFFER_SIZE); DeserializationError error = deserializeJson(doc, json); if (error) { diff --git a/src/utils/SerialPrint.cpp b/src/utils/SerialPrint.cpp index 4b3d1434..879405da 100644 --- a/src/utils/SerialPrint.cpp +++ b/src/utils/SerialPrint.cpp @@ -11,11 +11,11 @@ void SerialPrint(const String& errorLevel, const String& module, const String& m tosend += msg; Serial.println(tosend); - if (isNetworkActive()) { - if (jsonReadInt(settingsFlashJson, F("log")) != 0) { - sendStringToWs(F("corelg"), tosend, -1); - } + // if (isNetworkActive()) { // все проверки происходят в sendStringToWs() + if (jsonReadInt(settingsFlashJson, F("log")) != 0) { + sendStringToWs(F("corelg"), tosend, -1); } + // } if (errorLevel == "E") { cleanString(tosend); diff --git a/src/utils/Statistic.cpp b/src/utils/Statistic.cpp index 812236dc..1a992514 100644 --- a/src/utils/Statistic.cpp +++ b/src/utils/Statistic.cpp @@ -16,11 +16,11 @@ void stInit() { void updateDeviceStatus() { String ret; - String serverIP; - jsonRead(settingsFlashJson, F("serverip"), serverIP); + String serverIP = "http://iotmanager.org"; + // jsonRead(settingsFlashJson, F("serverip"), serverIP); String url = serverIP + F("/projects/esprebootstat.php"); // SerialPrint("i", "Stat", "url " + url); - if ((WiFi.status() == WL_CONNECTED)) { + if ((isNetworkActive())) { WiFiClient client; HTTPClient http; http.begin(client, url); @@ -34,7 +34,7 @@ void updateDeviceStatus() { int httpResponseCode = http.POST(httpRequestData); if (httpResponseCode > 0) { - ret = httpResponseCode; + ret = http.errorToString(httpResponseCode).c_str(); if (httpResponseCode == HTTP_CODE_OK) { String payload = http.getString(); ret += " " + payload; diff --git a/src/utils/StringUtils.cpp b/src/utils/StringUtils.cpp index 4234a68f..f33db48c 100644 --- a/src/utils/StringUtils.cpp +++ b/src/utils/StringUtils.cpp @@ -204,4 +204,23 @@ void cleanString(String& str) { for (size_t i = 0; i < str.length(); i++) { if (allowedChars.indexOf(str.charAt(i)) == -1) str.setCharAt(i, ' '); } +} + +std::vector splitStr(const String& str, const String& delimiter) { + std::vector result; + size_t newPos, pos = 0; + while ((newPos = str.indexOf(delimiter, pos)) != -1) { + result.push_back(str.substring(pos, newPos)); + pos = newPos + delimiter.length(); + } + result.push_back(str.substring(pos)); + return result; +} + + +bool strInVector(const String& str, const std::vector& vec) { + for (size_t i = 0; i < vec.size(); i++) { + if (vec[i] == str) return true; + } + return false; } \ No newline at end of file diff --git a/src/utils/WiFiUtils.cpp b/src/utils/WiFiUtils.cpp index af23f900..b2e0fb2f 100644 --- a/src/utils/WiFiUtils.cpp +++ b/src/utils/WiFiUtils.cpp @@ -1,127 +1,187 @@ #include "utils/WiFiUtils.h" +#include +#define TRIESONE 25 // количество попыток подключения к одной сети из несколких +#define TRIES 40 // количество попыток подключения сети если она одна -void routerConnect() { - WiFi.setAutoConnect(false); - WiFi.persistent(false); +void routerConnect() +{ + WiFi.setAutoConnect(false); + WiFi.persistent(false); - WiFi.mode(WIFI_STA); - byte tries = 40; + WiFi.mode(WIFI_STA); + byte triesOne = TRIESONE; - String _ssid = jsonReadStr(settingsFlashJson, "routerssid"); - String _password = jsonReadStr(settingsFlashJson, "routerpass"); + std::vector _ssidList; + std::vector _passwordList; + jsonReadArray(settingsFlashJson, "routerssid", _ssidList); + jsonReadArray(settingsFlashJson, "routerpass", _passwordList); + if (_ssidList.size() > 1) + triesOne = TRIES; - if (_ssid == "" && _password == "") { - WiFi.begin(); - } else { - WiFi.begin(_ssid.c_str(), _password.c_str()); + if (_passwordList.size() == 0 && _ssidList[0] == "" && _passwordList[0] == "") + { + WiFi.begin(); + } + else + { + WiFi.begin(_ssidList[0].c_str(), _passwordList[0].c_str()); #ifdef ESP32 - WiFi.setTxPower(WIFI_POWER_19_5dBm); + WiFi.setTxPower(WIFI_POWER_19_5dBm); #else - WiFi.setOutputPower(20.5); + WiFi.setOutputPower(20.5); #endif - SerialPrint("i", "WIFI", "ssid: " + _ssid); - SerialPrint("i", "WIFI", "pass: " + _password); + String _ssid; + String _password; + for (int8_t i = 0; i < _ssidList.size(); i++) + { + _ssid = _ssid + _ssidList[i] + "; "; } - - while (--tries && WiFi.status() != WL_CONNECTED) { - if (WiFi.status() == WL_CONNECT_FAILED) { - SerialPrint("E", "WIFI", "password is not correct"); - tries = 1; - jsonWriteInt(errorsHeapJson, "passer", 1); - break; - } - Serial.print("."); - delay(1000); + for (int8_t i = 0; i < _passwordList.size(); i++) + { + _password = _password + _passwordList[i] + "; "; } - - if (WiFi.status() != WL_CONNECTED) { - Serial.println(""); - startAPMode(); - } else { - Serial.println(""); - SerialPrint("i", "WIFI", "http://" + WiFi.localIP().toString()); - jsonWriteStr(settingsFlashJson, "ip", WiFi.localIP().toString()); - - mqttInit(); + SerialPrint("i", "WIFI", "ssid list: " + _ssid); + SerialPrint("i", "WIFI", "pass list: " + _password); + } + for (size_t i = 0; i < _ssidList.size(); i++) + { + triesOne = TRIESONE; + if (WiFi.status() == WL_CONNECTED) + break; + WiFi.begin(_ssidList[i].c_str(), _passwordList[i].c_str()); + SerialPrint("i", "WIFI", "ssid connect: " + _ssidList[i]); + SerialPrint("i", "WIFI", "pass connect: " + _passwordList[i]); + while (--triesOne && WiFi.status() != WL_CONNECTED) + { +// SerialPrint("i", "WIFI", ": " + String((int)WiFi.status())); +#ifdef ESP8266 + if (WiFi.status() == WL_CONNECT_FAILED || WiFi.status() == WL_WRONG_PASSWORD) +#else + if (WiFi.status() == WL_CONNECT_FAILED) +#endif + { + SerialPrint("E", "WIFI", "password is not correct"); + triesOne = 1; + jsonWriteInt(errorsHeapJson, "passer", 1); + break; + } + Serial.print("."); + delay(1000); } - SerialPrint("i", F("WIFI"), F("Network Init")); + Serial.println(""); + } + + if (WiFi.status() != WL_CONNECTED) + { + Serial.println(""); + startAPMode(); + } + else + { + Serial.println(""); + SerialPrint("i", "WIFI", "http://" + WiFi.localIP().toString()); + jsonWriteStr(settingsFlashJson, "ip", WiFi.localIP().toString()); + + mqttInit(); + } + SerialPrint("i", F("WIFI"), F("Network Init")); } -bool startAPMode() { - SerialPrint("i", "WIFI", "AP Mode"); +bool startAPMode() +{ + SerialPrint("i", "WIFI", "AP Mode"); - WiFi.disconnect(); - WiFi.mode(WIFI_AP); + WiFi.disconnect(); + WiFi.mode(WIFI_AP); - String _ssidAP = jsonReadStr(settingsFlashJson, "apssid"); - String _passwordAP = jsonReadStr(settingsFlashJson, "appass"); + String _ssidAP = jsonReadStr(settingsFlashJson, "apssid"); + String _passwordAP = jsonReadStr(settingsFlashJson, "appass"); - WiFi.softAP(_ssidAP.c_str(), _passwordAP.c_str()); - IPAddress myIP = WiFi.softAPIP(); + WiFi.softAP(_ssidAP.c_str(), _passwordAP.c_str()); + IPAddress myIP = WiFi.softAPIP(); - SerialPrint("i", "WIFI", "AP IP: " + myIP.toString()); - jsonWriteStr(settingsFlashJson, "ip", myIP.toString()); + SerialPrint("i", "WIFI", "AP IP: " + myIP.toString()); + jsonWriteStr(settingsFlashJson, "ip", myIP.toString()); - if (jsonReadInt(errorsHeapJson, "passer") != 1) { - ts.add( - WIFI_SCAN, 30 * 1000, [&](void*) { - String sta_ssid = jsonReadStr(settingsFlashJson, "routerssid"); - - SerialPrint("i", "WIFI", "scanning for " + sta_ssid); - - if (RouterFind(sta_ssid)) { - ts.remove(WIFI_SCAN); - WiFi.scanDelete(); - routerConnect(); - } - }, - nullptr, true); - } - return true; + if (jsonReadInt(errorsHeapJson, "passer") != 1) + { + ts.add( + WIFI_SCAN, 30 * 1000, + [&](void *) + { + std::vector jArray; + jsonReadArray(settingsFlashJson, "routerssid", jArray); + for (int8_t i = 0; i < jArray.size(); i++) + { + SerialPrint("i", "WIFI", "scanning for " + jArray[i]); + } + if (RouterFind(jArray)) + { + ts.remove(WIFI_SCAN); + WiFi.scanDelete(); + routerConnect(); + } + }, + nullptr, true); + } + return true; } -boolean RouterFind(String ssid) { - bool res = false; - int n = WiFi.scanComplete(); - SerialPrint("i", "WIFI", "scan result: " + String(n, DEC)); +boolean RouterFind(std::vector jArray) +{ + bool res = false; + int n = WiFi.scanComplete(); + SerialPrint("i", "WIFI", "scan result: " + String(n, DEC)); - if (n == -2) { //Сканирование не было запущено, запускаем - SerialPrint("i", "WIFI", "start scanning"); - WiFi.scanNetworks(true, false); // async, show_hidden - } + if (n == -2) + { // Сканирование не было запущено, запускаем + SerialPrint("i", "WIFI", "start scanning"); + WiFi.scanNetworks(true, false); // async, show_hidden + } - else if (n == -1) { //Сканирование все еще выполняется - SerialPrint("i", "WIFI", "scanning in progress"); - } + else if (n == -1) + { // Сканирование все еще выполняется + SerialPrint("i", "WIFI", "scanning in progress"); + } - else if (n == 0) { //ни одна сеть не найдена - SerialPrint("i", "WIFI", "no networks found"); - WiFi.scanNetworks(true, false); - } - - else if (n > 0) { - for (int8_t i = 0; i < n; i++) { - if (WiFi.SSID(i) == ssid) { - res = true; - } - // SerialPrint("i", "WIFI", (res ? "*" : "") + String(i, DEC) + ") " + WiFi.SSID(i)); - jsonWriteStr_(ssidListHeapJson, String(i), WiFi.SSID(i)); - - // String(WiFi.RSSI(i) + else if (n == 0) + { // ни одна сеть не найдена + SerialPrint("i", "WIFI", "no networks found"); + WiFi.scanNetworks(true, false); + } + else if (n > 0) + { + for (int8_t i = 0; i < n; i++) + { + for (int8_t k = 0; k < jArray.size(); k++) + { + if (WiFi.SSID(i) == jArray[k]) + { + res = true; } + } + // SerialPrint("i", "WIFI", (res ? "*" : "") + String(i, DEC) + ") " + WiFi.SSID(i)); + jsonWriteStr_(ssidListHeapJson, String(i), WiFi.SSID(i)); + + // String(WiFi.RSSI(i) } - SerialPrint("i", "WIFI", ssidListHeapJson); - WiFi.scanDelete(); - return res; + } + SerialPrint("i", "WIFI", ssidListHeapJson); + WiFi.scanDelete(); + return res; } boolean isNetworkActive() { return WiFi.status() == WL_CONNECTED; } +uint8_t getNumAPClients() { + return WiFi.softAPgetStationNum(); +} + uint8_t RSSIquality() { uint8_t res = 0; - if (WiFi.status() == WL_CONNECTED) { + if (isNetworkActive()) { int rssi = WiFi.RSSI(); if (rssi >= -50) { res = 6; //"Excellent"; diff --git a/tools/large_spiffs_16MB.csv b/tools/large_spiffs_16MB.csv new file mode 100644 index 00000000..2fd72050 --- /dev/null +++ b/tools/large_spiffs_16MB.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x480000, +app1, app, ota_1, 0x490000,0x480000, +spiffs, data, spiffs, 0x910000,0x6E0000, +coredump, data, coredump,0xFF0000,0x10000, \ No newline at end of file diff --git a/tools/partitions_custom.csv b/tools/partitions_custom.csv new file mode 100644 index 00000000..5dec3c06 --- /dev/null +++ b/tools/partitions_custom.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x180000, +app1, app, ota_1, 0x190000,0x180000, +spiffs, data, spiffs, 0x310000,0xF0000, \ No newline at end of file diff --git a/tools/patch8266_16m.py b/tools/patch8266_16m.py new file mode 100644 index 00000000..c0358cec --- /dev/null +++ b/tools/patch8266_16m.py @@ -0,0 +1,23 @@ +# правим %USERPROFILE%\.platformio\platforms\espressif8266\builder\main.py 103-115 +# для добавления возможности прошивки 16мб модуля esp8266 + +import os +import shutil +from sys import platform + +if platform == "linux" or platform == "linux2": + # linux + mainPyPath = '~/.platformio/platforms/espressif8266@4.0.1/builder/main.py' +else: + # windows + mainPyPath = os.environ['USERPROFILE'] + '\\.platformio\\platforms\\espressif8266@4.0.1\\builder\\main.py' + +# print(mainPyPath) + +with open(mainPyPath) as fr: + oldData = fr.read() + if not 'if _value == -0x6000:' in oldData: + shutil.copyfile(mainPyPath, mainPyPath+'.bak') + newData = oldData.replace('_value += 0xE00000 # correction', '_value += 0xE00000 # correction\n\n if _value == -0x6000:\n _value = env[k]-0x40200000') + with open(mainPyPath, 'w') as fw: + fw.write(newData) \ No newline at end of file diff --git a/tools/prebuildscript.py b/tools/prebuildscript.py new file mode 100644 index 00000000..7eb4a4b1 --- /dev/null +++ b/tools/prebuildscript.py @@ -0,0 +1,28 @@ + +import os +import configparser + +def before_build(): # source, target, env + print("Current Build targets", BUILD_TARGETS) + # Это всё потому что не работает "buildprog". При сборке прошивки Targets пустой, на всякий случай исключим все остальные + if (BUILD_TARGETS == ['upload'] or + BUILD_TARGETS == ['buildfs'] or + BUILD_TARGETS == ['uploadfs'] or + BUILD_TARGETS == ['uploadfsota'] or + BUILD_TARGETS == ['size']): + return + + print("Clear BUILD_TIME, delete main.o !") + config = configparser.ConfigParser() # создаём объекта парсера INI + config.read("platformio.ini") + deviceName = config["platformio"]["default_envs"] + # удаляем объектный файл где используется время сборки, для обновления + try: + os.remove("./.pio/build/"+deviceName+"/src/Main.cpp.o") + except OSError as e: + # If it fails, inform the user. + print("Error: %s - %s." % (e.filename, e.strerror)) + +#env.AddPreAction("buildprog", before_build) # $BUILD_DIR/src/main.cpp.o + +before_build() \ No newline at end of file diff --git a/webConfigMyProfile/build/bundle.js b/webConfigMyProfile/build/bundle.js index b0ced366..bfe7e3b3 100644 --- a/webConfigMyProfile/build/bundle.js +++ b/webConfigMyProfile/build/bundle.js @@ -1,5 +1,616 @@ -var app=function(){"use strict";function t(){}const e=t=>t;function n(t,e){for(const n in e)t[n]=e[n];return t}function o(t){return t()}function r(){return Object.create(null)}function l(t){t.forEach(o)}function i(t){return"function"==typeof t}function s(t,e){return t!=t?e==e:t!==e||t&&"object"==typeof t||"function"==typeof t}function c(t,e,n,o){if(t){const r=a(t,e,n,o);return t[0](r)}}function a(t,e,o,r){return t[1]&&r?n(o.ctx.slice(),t[1](r(e))):o.ctx}function u(t,e,n,o){if(t[2]&&o){const r=t[2](o(n));if(void 0===e.dirty)return r;if("object"==typeof r){const t=[],n=Math.max(e.dirty.length,r.length);for(let o=0;o32){const e=[],n=t.ctx.length/32;for(let t=0;twindow.performance.now():()=>Date.now(),m=p?t=>requestAnimationFrame(t):t;const g=new Set;function v(t){g.forEach((e=>{e.c(t)||(g.delete(e),e.f())})),0!==g.size&&m(v)}function $(t){let e;return 0===g.size&&m(v),{promise:new Promise((n=>{g.add(e={c:t,f:n})})),abort(){g.delete(e)}}}function b(t,e){t.appendChild(e)}function y(t){if(!t)return document;const e=t.getRootNode?t.getRootNode():t.ownerDocument;return e&&e.host?e:t.ownerDocument}function x(t){const e=S("style");return function(t,e){b(t.head||t,e),e.sheet}(y(t),e),e.sheet}function _(t,e,n){t.insertBefore(e,n||null)}function w(t){t.parentNode.removeChild(t)}function k(t,e){for(let n=0;nt.removeEventListener(e,n,o)}function P(t,e,n){null==n?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function q(t,e){e=""+e,t.wholeText!==e&&(t.data=e)}function M(t,e){t.value=null==e?"":e}function I(t,e,n,o){null===n?t.style.removeProperty(e):t.style.setProperty(e,n,o?"important":"")}function T(t,e){for(let n=0;n>>0}(u)}_${s}`,d=y(t),{stylesheet:p,rules:h}=G.get(d)||function(t,e){const n={stylesheet:x(e),rules:{}};return G.set(t,n),n}(d,t);h[f]||(h[f]=!0,p.insertRule(`@keyframes ${f} ${u}`,p.cssRules.length));const m=t.style.animation||"";return t.style.animation=`${m?`${m}, `:""}${f} ${o}ms linear ${r}ms 1 both`,U+=1,f}function A(t,e){const n=(t.style.animation||"").split(", "),o=n.filter(e?t=>t.indexOf(e)<0:t=>-1===t.indexOf("__svelte")),r=n.length-o.length;r&&(t.style.animation=o.join(", "),U-=r,U||m((()=>{U||(G.forEach((t=>{const{ownerNode:e}=t.stylesheet;e&&w(e)})),G.clear())})))}function F(n,o,r,l){if(!o)return t;const i=n.getBoundingClientRect();if(o.left===i.left&&o.right===i.right&&o.top===i.top&&o.bottom===i.bottom)return t;const{delay:s=0,duration:c=300,easing:a=e,start:u=h()+s,end:f=u+c,tick:d=t,css:p}=r(n,{from:o,to:i},l);let m,g=!0,v=!1;function b(){p&&A(n,m),g=!1}return $((t=>{if(!v&&t>=u&&(v=!0),v&&t>=f&&(d(1,0),b()),!g)return!1;if(v){const e=0+1*a((t-u)/c);d(e,1-e)}return!0})),p&&(m=D(n,0,1,c,s,a,p)),s||(v=!0),d(0,1),b}function L(t){const e=getComputedStyle(t);if("absolute"!==e.position&&"fixed"!==e.position){const{width:n,height:o}=e,r=t.getBoundingClientRect();t.style.position="absolute",t.style.width=n,t.style.height=o,J(t,r)}}function J(t,e){const n=t.getBoundingClientRect();if(e.left!==n.left||e.top!==n.top){const o=getComputedStyle(t),r="none"===o.transform?"":o.transform;t.style.transform=`${r} translate(${e.left-n.left}px, ${e.top-n.top}px)`}}function V(t){B=t}function z(){if(!B)throw new Error("Function called outside component initialization");return B}const K=[],H=[],Q=[],W=[],X=Promise.resolve();let Y=!1;function Z(t){Q.push(t)}function tt(t){W.push(t)}const et=new Set;let nt,ot=0;function rt(){const t=B;do{for(;ot{nt=null}))),nt}function st(t,e,n){t.dispatchEvent(R(`${e?"intro":"outro"}${n}`))}const ct=new Set;let at;function ut(){at={r:0,c:[],p:at}}function ft(){at.r||l(at.c),at=at.p}function dt(t,e){t&&t.i&&(ct.delete(t),t.i(e))}function pt(t,e,n,o){if(t&&t.o){if(ct.has(t))return;ct.add(t),at.c.push((()=>{ct.delete(t),o&&(n&&t.d(1),o())})),t.o(e)}else o&&o()}const ht={duration:0};function mt(n,o,r){let l,s,c=o(n,r),a=!1,u=0;function f(){l&&A(n,l)}function d(){const{delay:o=0,duration:r=300,easing:i=e,tick:d=t,css:p}=c||ht;p&&(l=D(n,0,1,r,o,i,p,u++)),d(0,1);const m=h()+o,g=m+r;s&&s.abort(),a=!0,Z((()=>st(n,!0,"start"))),s=$((t=>{if(a){if(t>=g)return d(1,0),st(n,!0,"end"),f(),a=!1;if(t>=m){const e=i((t-m)/r);d(e,1-e)}}return a}))}let p=!1;return{start(){p||(p=!0,A(n),i(c)?(c=c(),it().then(d)):d())},invalidate(){p=!1},end(){a&&(f(),a=!1)}}}function gt(n,o,r){let s,c=o(n,r),a=!0;const u=at;function f(){const{delay:o=0,duration:r=300,easing:i=e,tick:f=t,css:d}=c||ht;d&&(s=D(n,1,0,r,o,i,d));const p=h()+o,m=p+r;Z((()=>st(n,!1,"start"))),$((t=>{if(a){if(t>=m)return f(0,1),st(n,!1,"end"),--u.r||l(u.c),!1;if(t>=p){const e=i((t-p)/r);f(1-e,e)}}return a}))}return u.r+=1,i(c)?it().then((()=>{c=c(),f()})):f(),{end(t){t&&c.tick&&c.tick(1,0),a&&(s&&A(n,s),a=!1)}}}function vt(t,e){t.f(),function(t,e){pt(t,1,1,(()=>{e.delete(t.key)}))}(t,e)}function $t(t,e,n,o,r,l,i,s,c,a,u,f){let d=t.length,p=l.length,h=d;const m={};for(;h--;)m[t[h].key]=h;const g=[],v=new Map,$=new Map;for(h=p;h--;){const t=f(r,l,h),s=n(t);let c=i.get(s);c?o&&c.p(t,e):(c=a(s,t),c.c()),v.set(s,g[h]=c),s in m&&$.set(s,Math.abs(h-m[s]))}const b=new Set,y=new Set;function x(t){dt(t,1),t.m(s,u),i.set(t.key,t),u=t.first,p--}for(;d&&p;){const e=g[p-1],n=t[d-1],o=e.key,r=n.key;e===n?(u=e.first,d--,p--):v.has(r)?!i.has(o)||b.has(o)?x(e):y.has(r)?d--:$.get(o)>$.get(r)?(y.add(o),x(e)):(b.add(r),d--):(c(n,i),d--)}for(;d--;){const e=t[d];v.has(e.key)||c(e,i)}for(;p;)x(g[p-1]);return g}function bt(t,e,n){const o=t.$$.props[e];void 0!==o&&(t.$$.bound[o]=n,n(t.$$.ctx[o]))}function yt(t){t&&t.c()}function xt(t,e,n,r){const{fragment:s,on_mount:c,on_destroy:a,after_update:u}=t.$$;s&&s.m(e,n),r||Z((()=>{const e=c.map(o).filter(i);a?a.push(...e):l(e),t.$$.on_mount=[]})),u.forEach(Z)}function _t(t,e){const n=t.$$;null!==n.fragment&&(l(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function wt(t,e){-1===t.$$.dirty[0]&&(K.push(t),Y||(Y=!0,X.then(rt)),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const r=o.length?o[0]:n;return d.ctx&&s(d.ctx[t],d.ctx[t]=r)&&(!d.skip_bound&&d.bound[t]&&d.bound[t](r),p&&wt(e,t)),n})):[],d.update(),p=!0,l(d.before_update),d.fragment=!!i&&i(d.ctx),n.target){if(n.hydrate){const t=function(t){return Array.from(t.childNodes)}(n.target);d.fragment&&d.fragment.l(t),t.forEach(w)}else d.fragment&&d.fragment.c();n.intro&&dt(e.$$.fragment),xt(e,n.target,n.anchor,n.customElement),rt()}V(f)}class St{$destroy(){_t(this,1),this.$destroy=t}$on(t,e){const n=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return n.push(e),()=>{const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}$set(t){var e;this.$$set&&(e=t,0!==Object.keys(e).length)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}function jt(t){const e=t-1;return e*e*e+1}function Ot(t){return--t*t*t*t*t+1} -/*! ***************************************************************************** +var app = (function () { + "use strict"; + function t() {} + const e = (t) => t; + function n(t, e) { + for (const n in e) t[n] = e[n]; + return t; + } + function o(t) { + return t(); + } + function r() { + return Object.create(null); + } + function l(t) { + t.forEach(o); + } + function i(t) { + return "function" == typeof t; + } + function s(t, e) { + return t != t + ? e == e + : t !== e || (t && "object" == typeof t) || "function" == typeof t; + } + function c(t, e, n, o) { + if (t) { + const r = a(t, e, n, o); + return t[0](r); + } + } + function a(t, e, o, r) { + return t[1] && r ? n(o.ctx.slice(), t[1](r(e))) : o.ctx; + } + function u(t, e, n, o) { + if (t[2] && o) { + const r = t[2](o(n)); + if (void 0 === e.dirty) return r; + if ("object" == typeof r) { + const t = [], + n = Math.max(e.dirty.length, r.length); + for (let o = 0; o < n; o += 1) t[o] = e.dirty[o] | r[o]; + return t; + } + return e.dirty | r; + } + return e.dirty; + } + function f(t, e, n, o, r, l) { + if (r) { + const i = a(e, n, o, l); + t.p(i, r); + } + } + function d(t) { + if (t.ctx.length > 32) { + const e = [], + n = t.ctx.length / 32; + for (let t = 0; t < n; t++) e[t] = -1; + return e; + } + return -1; + } + const p = "undefined" != typeof window; + let h = p ? () => window.performance.now() : () => Date.now(), + m = p ? (t) => requestAnimationFrame(t) : t; + const g = new Set(); + function v(t) { + g.forEach((e) => { + e.c(t) || (g.delete(e), e.f()); + }), + 0 !== g.size && m(v); + } + function $(t) { + let e; + return ( + 0 === g.size && m(v), + { + promise: new Promise((n) => { + g.add((e = { c: t, f: n })); + }), + abort() { + g.delete(e); + }, + } + ); + } + function b(t, e) { + t.appendChild(e); + } + function y(t) { + if (!t) return document; + const e = t.getRootNode ? t.getRootNode() : t.ownerDocument; + return e && e.host ? e : t.ownerDocument; + } + function x(t) { + const e = S("style"); + return ( + (function (t, e) { + b(t.head || t, e), e.sheet; + })(y(t), e), + e.sheet + ); + } + function _(t, e, n) { + t.insertBefore(e, n || null); + } + function w(t) { + t.parentNode.removeChild(t); + } + function k(t, e) { + for (let n = 0; n < t.length; n += 1) t[n] && t[n].d(e); + } + function S(t) { + return document.createElement(t); + } + function j(t) { + return document.createTextNode(t); + } + function O() { + return j(" "); + } + function C() { + return j(""); + } + function N(t, e, n, o) { + return t.addEventListener(e, n, o), () => t.removeEventListener(e, n, o); + } + function P(t, e, n) { + null == n + ? t.removeAttribute(e) + : t.getAttribute(e) !== n && t.setAttribute(e, n); + } + function q(t, e) { + (e = "" + e), t.wholeText !== e && (t.data = e); + } + function M(t, e) { + t.value = null == e ? "" : e; + } + function I(t, e, n, o) { + null === n + ? t.style.removeProperty(e) + : t.style.setProperty(e, n, o ? "important" : ""); + } + function T(t, e) { + for (let n = 0; n < t.options.length; n += 1) { + const o = t.options[n]; + if (o.__value === e) return void (o.selected = !0); + } + t.selectedIndex = -1; + } + function E(t) { + const e = t.querySelector(":checked") || t.options[0]; + return e && e.__value; + } + function R(t, e, { bubbles: n = !1, cancelable: o = !1 } = {}) { + const r = document.createEvent("CustomEvent"); + return r.initCustomEvent(t, n, o, e), r; + } + const G = new Map(); + let B, + U = 0; + function D(t, e, n, o, r, l, i, s = 0) { + const c = 16.666 / o; + let a = "{\n"; + for (let t = 0; t <= 1; t += c) { + const o = e + (n - e) * l(t); + a += 100 * t + `%{${i(o, 1 - o)}}\n`; + } + const u = a + `100% {${i(n, 1 - n)}}\n}`, + f = `__svelte_${(function (t) { + let e = 5381, + n = t.length; + for (; n--; ) e = ((e << 5) - e) ^ t.charCodeAt(n); + return e >>> 0; + })(u)}_${s}`, + d = y(t), + { stylesheet: p, rules: h } = + G.get(d) || + (function (t, e) { + const n = { stylesheet: x(e), rules: {} }; + return G.set(t, n), n; + })(d, t); + h[f] || + ((h[f] = !0), p.insertRule(`@keyframes ${f} ${u}`, p.cssRules.length)); + const m = t.style.animation || ""; + return ( + (t.style.animation = `${ + m ? `${m}, ` : "" + }${f} ${o}ms linear ${r}ms 1 both`), + (U += 1), + f + ); + } + function A(t, e) { + const n = (t.style.animation || "").split(", "), + o = n.filter( + e ? (t) => t.indexOf(e) < 0 : (t) => -1 === t.indexOf("__svelte") + ), + r = n.length - o.length; + r && + ((t.style.animation = o.join(", ")), + (U -= r), + U || + m(() => { + U || + (G.forEach((t) => { + const { ownerNode: e } = t.stylesheet; + e && w(e); + }), + G.clear()); + })); + } + function F(n, o, r, l) { + if (!o) return t; + const i = n.getBoundingClientRect(); + if ( + o.left === i.left && + o.right === i.right && + o.top === i.top && + o.bottom === i.bottom + ) + return t; + const { + delay: s = 0, + duration: c = 300, + easing: a = e, + start: u = h() + s, + end: f = u + c, + tick: d = t, + css: p, + } = r(n, { from: o, to: i }, l); + let m, + g = !0, + v = !1; + function b() { + p && A(n, m), (g = !1); + } + return ( + $((t) => { + if ((!v && t >= u && (v = !0), v && t >= f && (d(1, 0), b()), !g)) + return !1; + if (v) { + const e = 0 + 1 * a((t - u) / c); + d(e, 1 - e); + } + return !0; + }), + p && (m = D(n, 0, 1, c, s, a, p)), + s || (v = !0), + d(0, 1), + b + ); + } + function L(t) { + const e = getComputedStyle(t); + if ("absolute" !== e.position && "fixed" !== e.position) { + const { width: n, height: o } = e, + r = t.getBoundingClientRect(); + (t.style.position = "absolute"), + (t.style.width = n), + (t.style.height = o), + J(t, r); + } + } + function J(t, e) { + const n = t.getBoundingClientRect(); + if (e.left !== n.left || e.top !== n.top) { + const o = getComputedStyle(t), + r = "none" === o.transform ? "" : o.transform; + t.style.transform = `${r} translate(${e.left - n.left}px, ${ + e.top - n.top + }px)`; + } + } + function V(t) { + B = t; + } + function z() { + if (!B) throw new Error("Function called outside component initialization"); + return B; + } + const K = [], + H = [], + Q = [], + W = [], + X = Promise.resolve(); + let Y = !1; + function Z(t) { + Q.push(t); + } + function tt(t) { + W.push(t); + } + const et = new Set(); + let nt, + ot = 0; + function rt() { + const t = B; + do { + for (; ot < K.length; ) { + const t = K[ot]; + ot++, V(t), lt(t.$$); + } + for (V(null), K.length = 0, ot = 0; H.length; ) H.pop()(); + for (let t = 0; t < Q.length; t += 1) { + const e = Q[t]; + et.has(e) || (et.add(e), e()); + } + Q.length = 0; + } while (K.length); + for (; W.length; ) W.pop()(); + (Y = !1), et.clear(), V(t); + } + function lt(t) { + if (null !== t.fragment) { + t.update(), l(t.before_update); + const e = t.dirty; + (t.dirty = [-1]), + t.fragment && t.fragment.p(t.ctx, e), + t.after_update.forEach(Z); + } + } + function it() { + return ( + nt || + ((nt = Promise.resolve()), + nt.then(() => { + nt = null; + })), + nt + ); + } + function st(t, e, n) { + t.dispatchEvent(R(`${e ? "intro" : "outro"}${n}`)); + } + const ct = new Set(); + let at; + function ut() { + at = { r: 0, c: [], p: at }; + } + function ft() { + at.r || l(at.c), (at = at.p); + } + function dt(t, e) { + t && t.i && (ct.delete(t), t.i(e)); + } + function pt(t, e, n, o) { + if (t && t.o) { + if (ct.has(t)) return; + ct.add(t), + at.c.push(() => { + ct.delete(t), o && (n && t.d(1), o()); + }), + t.o(e); + } else o && o(); + } + const ht = { duration: 0 }; + function mt(n, o, r) { + let l, + s, + c = o(n, r), + a = !1, + u = 0; + function f() { + l && A(n, l); + } + function d() { + const { + delay: o = 0, + duration: r = 300, + easing: i = e, + tick: d = t, + css: p, + } = c || ht; + p && (l = D(n, 0, 1, r, o, i, p, u++)), d(0, 1); + const m = h() + o, + g = m + r; + s && s.abort(), + (a = !0), + Z(() => st(n, !0, "start")), + (s = $((t) => { + if (a) { + if (t >= g) return d(1, 0), st(n, !0, "end"), f(), (a = !1); + if (t >= m) { + const e = i((t - m) / r); + d(e, 1 - e); + } + } + return a; + })); + } + let p = !1; + return { + start() { + p || ((p = !0), A(n), i(c) ? ((c = c()), it().then(d)) : d()); + }, + invalidate() { + p = !1; + }, + end() { + a && (f(), (a = !1)); + }, + }; + } + function gt(n, o, r) { + let s, + c = o(n, r), + a = !0; + const u = at; + function f() { + const { + delay: o = 0, + duration: r = 300, + easing: i = e, + tick: f = t, + css: d, + } = c || ht; + d && (s = D(n, 1, 0, r, o, i, d)); + const p = h() + o, + m = p + r; + Z(() => st(n, !1, "start")), + $((t) => { + if (a) { + if (t >= m) return f(0, 1), st(n, !1, "end"), --u.r || l(u.c), !1; + if (t >= p) { + const e = i((t - p) / r); + f(1 - e, e); + } + } + return a; + }); + } + return ( + (u.r += 1), + i(c) + ? it().then(() => { + (c = c()), f(); + }) + : f(), + { + end(t) { + t && c.tick && c.tick(1, 0), a && (s && A(n, s), (a = !1)); + }, + } + ); + } + function vt(t, e) { + t.f(), + (function (t, e) { + pt(t, 1, 1, () => { + e.delete(t.key); + }); + })(t, e); + } + function $t(t, e, n, o, r, l, i, s, c, a, u, f) { + let d = t.length, + p = l.length, + h = d; + const m = {}; + for (; h--; ) m[t[h].key] = h; + const g = [], + v = new Map(), + $ = new Map(); + for (h = p; h--; ) { + const t = f(r, l, h), + s = n(t); + let c = i.get(s); + c ? o && c.p(t, e) : ((c = a(s, t)), c.c()), + v.set(s, (g[h] = c)), + s in m && $.set(s, Math.abs(h - m[s])); + } + const b = new Set(), + y = new Set(); + function x(t) { + dt(t, 1), t.m(s, u), i.set(t.key, t), (u = t.first), p--; + } + for (; d && p; ) { + const e = g[p - 1], + n = t[d - 1], + o = e.key, + r = n.key; + e === n + ? ((u = e.first), d--, p--) + : v.has(r) + ? !i.has(o) || b.has(o) + ? x(e) + : y.has(r) + ? d-- + : $.get(o) > $.get(r) + ? (y.add(o), x(e)) + : (b.add(r), d--) + : (c(n, i), d--); + } + for (; d--; ) { + const e = t[d]; + v.has(e.key) || c(e, i); + } + for (; p; ) x(g[p - 1]); + return g; + } + function bt(t, e, n) { + const o = t.$$.props[e]; + void 0 !== o && ((t.$$.bound[o] = n), n(t.$$.ctx[o])); + } + function yt(t) { + t && t.c(); + } + function xt(t, e, n, r) { + const { fragment: s, on_mount: c, on_destroy: a, after_update: u } = t.$$; + s && s.m(e, n), + r || + Z(() => { + const e = c.map(o).filter(i); + a ? a.push(...e) : l(e), (t.$$.on_mount = []); + }), + u.forEach(Z); + } + function _t(t, e) { + const n = t.$$; + null !== n.fragment && + (l(n.on_destroy), + n.fragment && n.fragment.d(e), + (n.on_destroy = n.fragment = null), + (n.ctx = [])); + } + function wt(t, e) { + -1 === t.$$.dirty[0] && + (K.push(t), Y || ((Y = !0), X.then(rt)), t.$$.dirty.fill(0)), + (t.$$.dirty[(e / 31) | 0] |= 1 << e % 31); + } + function kt(e, n, o, i, s, c, a, u = [-1]) { + const f = B; + V(e); + const d = (e.$$ = { + fragment: null, + ctx: null, + props: c, + update: t, + not_equal: s, + bound: r(), + on_mount: [], + on_destroy: [], + on_disconnect: [], + before_update: [], + after_update: [], + context: new Map(n.context || (f ? f.$$.context : [])), + callbacks: r(), + dirty: u, + skip_bound: !1, + root: n.target || f.$$.root, + }); + a && a(d.root); + let p = !1; + if ( + ((d.ctx = o + ? o(e, n.props || {}, (t, n, ...o) => { + const r = o.length ? o[0] : n; + return ( + d.ctx && + s(d.ctx[t], (d.ctx[t] = r)) && + (!d.skip_bound && d.bound[t] && d.bound[t](r), p && wt(e, t)), + n + ); + }) + : []), + d.update(), + (p = !0), + l(d.before_update), + (d.fragment = !!i && i(d.ctx)), + n.target) + ) { + if (n.hydrate) { + const t = (function (t) { + return Array.from(t.childNodes); + })(n.target); + d.fragment && d.fragment.l(t), t.forEach(w); + } else d.fragment && d.fragment.c(); + n.intro && dt(e.$$.fragment), + xt(e, n.target, n.anchor, n.customElement), + rt(); + } + V(f); + } + class St { + $destroy() { + _t(this, 1), (this.$destroy = t); + } + $on(t, e) { + const n = this.$$.callbacks[t] || (this.$$.callbacks[t] = []); + return ( + n.push(e), + () => { + const t = n.indexOf(e); + -1 !== t && n.splice(t, 1); + } + ); + } + $set(t) { + var e; + this.$$set && + ((e = t), 0 !== Object.keys(e).length) && + ((this.$$.skip_bound = !0), this.$$set(t), (this.$$.skip_bound = !1)); + } + } + function jt(t) { + const e = t - 1; + return e * e * e + 1; + } + function Ot(t) { + return --t * t * t * t * t + 1; + } + /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any @@ -12,5 +623,2265 @@ var app=function(){"use strict";function t(){}const e=t=>t;function n(t,e){for(c LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */function Ct(t){var{fallback:e}=t,o=function(t,e){var n={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.indexOf(o)<0&&(n[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(o=Object.getOwnPropertySymbols(t);r(t.set(c.key,{rect:s.getBoundingClientRect()}),()=>{if(r.has(c.key)){const{rect:t}=r.get(c.key);return r.delete(c.key),function(t,e,r){const{delay:l=0,duration:s=(t=>30*Math.sqrt(t)),easing:c=jt}=n(n({},o),r),a=e.getBoundingClientRect(),u=t.left-a.left,f=t.top-a.top,d=t.width/a.width,p=t.height/a.height,h=Math.sqrt(u*u+f*f),m=getComputedStyle(e),g="none"===m.transform?"":m.transform,v=+m.opacity;return{delay:l,duration:i(s)?s(h):s,easing:c,css:(t,e)=>`\n\t\t\t\topacity: ${t*v};\n\t\t\t\ttransform-origin: top left;\n\t\t\t\ttransform: ${g} translate(${e*u}px,${e*f}px) scale(${t+(1-t)*d}, ${t+(1-t)*p});\n\t\t\t`}}(t,s,c)}return t.delete(c.key),e&&e(s,c,l)})}return[s(l,r,!1),s(r,l,!0)]}function Nt(t,{from:e,to:n},o={}){const r=getComputedStyle(t),l="none"===r.transform?"":r.transform,[s,c]=r.transformOrigin.split(" ").map(parseFloat),a=e.left+e.width*s/n.width-(n.left+s),u=e.top+e.height*c/n.height-(n.top+c),{delay:f=0,duration:d=(t=>120*Math.sqrt(t)),easing:p=jt}=o;return{delay:f,duration:i(d)?d(Math.sqrt(a*a+u*u)):d,easing:p,css:(t,o)=>{const r=o*a,i=o*u,s=t+o*e.width/n.width,c=t+o*e.height/n.height;return`transform: ${l} translate(${r}px, ${i}px) scale(${s}, ${c});`}}}function Pt(e){let n,o,r,l,i,s,c,a,u,f,d;return{c(){n=S("div"),o=S("div"),r=S("div"),l=O(),i=S("div"),s=O(),c=S("div"),a=O(),u=S("div"),f=j(e[0]),d=j("%"),P(r,"class","gauge-fill gauge-bg svelte-1er7lpr"),P(i,"class","gauge-fill svelte-1er7lpr"),I(i,"transform","rotate(-"+e[2]+"deg)"),I(i,"box-shadow","inset 0 0 60px -5px "+e[1]),P(c,"class","gauge-white svelte-1er7lpr"),P(u,"class","gauge-value svelte-1er7lpr"),P(o,"class","gauge-overflow svelte-1er7lpr"),P(n,"class","gauge svelte-1er7lpr")},m(t,e){_(t,n,e),b(n,o),b(o,r),b(o,l),b(o,i),b(o,s),b(o,c),b(o,a),b(o,u),b(u,f),b(u,d)},p(t,[e]){4&e&&I(i,"transform","rotate(-"+t[2]+"deg)"),2&e&&I(i,"box-shadow","inset 0 0 60px -5px "+t[1]),1&e&&q(f,t[0])},i:t,o:t,d(t){t&&w(n)}}}function qt(t,e,n){let o,r,{title:l}=e,{value:i=l}=e,{thresholds:s="90,100"}=e,{colors:c="#7CFC00,#FFD700,#FF0000"}=e;return t.$$set=t=>{"title"in t&&n(3,l=t.title),"value"in t&&n(0,i=t.value),"thresholds"in t&&n(4,s=t.thresholds),"colors"in t&&n(5,c=t.colors)},t.$$.update=()=>{if(49&t.$$.dirty){const t=String(s).split(","),e=t.indexOf(t.find((t=>i({}),Tt=t=>({});function Et(t){let e,n,o,r,i,s,a,p,h,m,g,v,$;const y=t[4].header,x=c(y,t,t[3],Tt),k=t[4].default,j=c(k,t,t[3],null);return{c(){e=S("div"),n=O(),o=S("div"),x&&x.c(),r=O(),i=S("hr"),s=O(),j&&j.c(),a=O(),p=S("hr"),h=O(),m=S("button"),m.textContent="Close",P(e,"class","modal-background svelte-5a6h72"),I(m,"float","right"),I(m,"margin-right","2%"),I(m,"border","1px solid lightblue"),I(m,"border-radius","8px"),m.autofocus=!0,P(m,"class","svelte-5a6h72"),P(o,"class","modal svelte-5a6h72"),P(o,"role","dialog"),P(o,"aria-modal","true")},m(l,c){_(l,e,c),_(l,n,c),_(l,o,c),x&&x.m(o,null),b(o,r),b(o,i),b(o,s),j&&j.m(o,null),b(o,a),b(o,p),b(o,h),b(o,m),t[5](o),g=!0,m.focus(),v||($=[N(window,"keydown",t[2]),N(e,"click",t[1]),N(m,"click",t[1])],v=!0)},p(t,[e]){x&&x.p&&(!g||8&e)&&f(x,y,t,t[3],g?u(y,t[3],e,It):d(t[3]),Tt),j&&j.p&&(!g||8&e)&&f(j,k,t,t[3],g?u(k,t[3],e,null):d(t[3]),null)},i(t){g||(dt(x,t),dt(j,t),g=!0)},o(t){pt(x,t),pt(j,t),g=!1},d(r){r&&w(e),r&&w(n),r&&w(o),x&&x.d(r),j&&j.d(r),t[5](null),v=!1,l($)}}}function Rt(t,e,n){let{$$slots:o={},$$scope:r}=e;const l=function(){const t=z();return(e,n,{cancelable:o=!1}={})=>{const r=t.$$.callbacks[e];if(r){const l=R(e,n,{cancelable:o});return r.slice().forEach((e=>{e.call(t,l)})),!l.defaultPrevented}return!0}}(),i=()=>l("close");let s;const c="undefined"!=typeof document&&document.activeElement;var a;return c&&(a=()=>{c.focus()},z().$$.on_destroy.push(a)),t.$$set=t=>{"$$scope"in t&&n(3,r=t.$$scope)},[s,i,t=>{if("Escape"!==t.key){if("Tab"===t.key){const e=s.querySelectorAll("*"),n=Array.from(e).filter((t=>t.tabIndex>=0));let o=n.indexOf(document.activeElement);-1===o&&t.shiftKey&&(o=0),o+=n.length+(t.shiftKey?-1:1),o%=n.length,n[o].focus(),t.preventDefault()}}else i()},r,o,function(t){H[t?"unshift":"push"]((()=>{s=t,n(0,s)}))}]}class Gt extends St{constructor(t){super(),kt(this,t,Rt,Et,s,{})}}function Bt(t){let e,n;const o=t[1].default,r=c(o,t,t[0],null);return{c(){e=S("div"),r&&r.c(),P(e,"class","box svelte-1q88uwi")},m(t,o){_(t,e,o),r&&r.m(e,null),n=!0},p(t,[e]){r&&r.p&&(!n||1&e)&&f(r,o,t,t[0],n?u(o,t[0],e,null):d(t[0]),null)},i(t){n||(dt(r,t),n=!0)},o(t){pt(r,t),n=!1},d(t){t&&w(e),r&&r.d(t)}}}function Ut(t,e,n){let{$$slots:o={},$$scope:r}=e;return t.$$set=t=>{"$$scope"in t&&n(0,r=t.$$scope)},[r,o]}class Dt extends St{constructor(t){super(),kt(this,t,Ut,Bt,s,{})}}function At(t,e,n){const o=t.slice();return o[62]=e[n],o}function Ft(t,e,n){const o=t.slice();return o[65]=e[n],o}function Lt(t,e,n){const o=t.slice();return o[68]=e[n][0],o[69]=e[n][1],o}function Jt(t,e,n){const o=t.slice();return o[68]=e[n][0],o[69]=e[n][1],o}function Vt(t,e,n){const o=t.slice();return o[74]=e[n],o[75]=e,o[76]=n,o}function zt(t,e,n){const o=t.slice();return o[68]=e[n][0],o[69]=e[n][1],o}function Kt(t,e,n){const o=t.slice();return o[74]=e[n],o[79]=e,o[80]=n,o}function Ht(t,e,n){const o=t.slice();return o[81]=e[n],o}function Qt(t){let e,n,o,r;function l(e){t[19](e)}let i={};return void 0!==t[1]&&(i.value=t[1]),e=new Mt({props:i}),H.push((()=>bt(e,"value",l))),{c(){yt(e.$$.fragment),o=j("                   ")},m(t,n){xt(e,t,n),_(t,o,n),r=!0},p(t,o){const r={};!n&&2&o[0]&&(n=!0,r.value=t[1],tt((()=>n=!1))),e.$set(r)},i(t){r||(dt(e.$$.fragment,t),r=!0)},o(t){pt(e.$$.fragment,t),r=!1},d(t){_t(e,t),t&&w(o)}}}function Wt(t){let e,n,o,r;function l(e){t[20](e)}let i={};return void 0!==t[1]&&(i.value=t[1]),e=new Mt({props:i}),H.push((()=>bt(e,"value",l))),{c(){yt(e.$$.fragment),o=j("                   ")},m(t,n){xt(e,t,n),_(t,o,n),r=!0},p(t,o){const r={};!n&&2&o[0]&&(n=!0,r.value=t[1],tt((()=>n=!1))),e.$set(r)},i(t){r||(dt(e.$$.fragment,t),r=!0)},o(t){pt(e.$$.fragment,t),r=!1},d(t){_t(e,t),t&&w(o)}}}function Xt(t){let e,n,o,r;function l(e){t[21](e)}let i={};return void 0!==t[1]&&(i.value=t[1]),e=new Mt({props:i}),H.push((()=>bt(e,"value",l))),{c(){yt(e.$$.fragment),o=j("                   ")},m(t,n){xt(e,t,n),_(t,o,n),r=!0},p(t,o){const r={};!n&&2&o[0]&&(n=!0,r.value=t[1],tt((()=>n=!1))),e.$set(r)},i(t){r||(dt(e.$$.fragment,t),r=!0)},o(t){pt(e.$$.fragment,t),r=!1},d(t){_t(e,t),t&&w(o)}}}function Yt(t){let e,n,o,r;function l(e){t[22](e)}let i={};return void 0!==t[1]&&(i.value=t[1]),e=new Mt({props:i}),H.push((()=>bt(e,"value",l))),{c(){yt(e.$$.fragment),o=j("                   ")},m(t,n){xt(e,t,n),_(t,o,n),r=!0},p(t,o){const r={};!n&&2&o[0]&&(n=!0,r.value=t[1],tt((()=>n=!1))),e.$set(r)},i(t){r||(dt(e.$$.fragment,t),r=!0)},o(t){pt(e.$$.fragment,t),r=!1},d(t){_t(e,t),t&&w(o)}}}function Zt(e){let n,o,r,l=e[81].text+"";return{c(){n=S("option"),o=j(l),r=O(),n.__value=e[81],n.value=n.__value},m(t,e){_(t,n,e),b(n,o),b(n,r)},p:t,d(t){t&&w(n)}}}function te(e){let n,o,r,l=e[81].text+"";return{c(){n=S("option"),o=j(l),r=O(),n.__value=e[81],n.value=n.__value},m(t,e){_(t,n,e),b(n,o),b(n,r)},p:t,d(t){t&&w(n)}}}function ee(e){let n,o,r,l=e[81].text+"";return{c(){n=S("option"),o=j(l),r=O(),n.__value=e[81],n.value=n.__value},m(t,e){_(t,n,e),b(n,o),b(n,r)},p:t,d(t){t&&w(n)}}}function ne(t){let e;let n=function(t,e){return"esp8266_1mb"==t[81].text?ee:"esp8266_1mb_ota"==t[81].text?te:Zt}(t),o=n(t);return{c(){o.c(),e=C()},m(t,n){o.m(t,n),_(t,e,n)},p(t,e){o.p(t,e)},d(t){o.d(t),t&&w(e)}}}function oe(t){let e,n,o,r,i,s,c,a,u,f,d,p,h,m,g,v,$,y,x,k,C,q,T,E,R,G,B,U,D,A,F,L,J,V,z,K,H,Q;return{c(){e=S("p"),n=j("wifi сеть      "),o=S("input"),r=S("br"),i=O(),s=S("p"),c=j("пароль       "),a=S("input"),u=S("br"),f=O(),d=S("p"),p=j("MQTT сервер      "),h=S("input"),m=S("br"),g=O(),v=S("p"),$=j("порт       "),y=S("input"),x=S("br"),k=O(),C=S("p"),q=j("префикс      "),T=S("input"),E=S("br"),R=O(),G=S("p"),B=j("логин       "),U=S("input"),D=S("br"),A=O(),F=S("p"),L=j("пароль       "),J=S("input"),V=S("br"),z=O(),K=S("br"),I(o,"float","right"),P(o,"class","svelte-185rejv"),I(e,"color","#ccc"),I(a,"float","right"),P(a,"class","svelte-185rejv"),I(s,"color","#ccc"),I(h,"float","right"),P(h,"class","svelte-185rejv"),I(d,"color","#ccc"),I(y,"float","right"),P(y,"class","svelte-185rejv"),I(v,"color","#ccc"),I(T,"float","right"),P(T,"class","svelte-185rejv"),I(C,"color","#ccc"),I(U,"float","right"),P(U,"class","svelte-185rejv"),I(G,"color","#ccc"),I(J,"float","right"),P(J,"class","svelte-185rejv"),I(F,"color","#ccc")},m(l,w){_(l,e,w),b(e,n),b(e,o),M(o,t[0].iotmSettings.routerssid),b(e,r),_(l,i,w),_(l,s,w),b(s,c),b(s,a),M(a,t[0].iotmSettings.routerpass),b(s,u),_(l,f,w),_(l,d,w),b(d,p),b(d,h),M(h,t[0].iotmSettings.mqttServer),b(d,m),_(l,g,w),_(l,v,w),b(v,$),b(v,y),M(y,t[0].iotmSettings.mqttPort),b(v,x),_(l,k,w),_(l,C,w),b(C,q),b(C,T),M(T,t[0].iotmSettings.mqttPrefix),b(C,E),_(l,R,w),_(l,G,w),b(G,B),b(G,U),M(U,t[0].iotmSettings.mqttUser),b(G,D),_(l,A,w),_(l,F,w),b(F,L),b(F,J),M(J,t[0].iotmSettings.mqttPass),b(F,V),_(l,z,w),_(l,K,w),H||(Q=[N(o,"input",t[29]),N(a,"input",t[30]),N(h,"input",t[31]),N(y,"input",t[32]),N(T,"input",t[33]),N(U,"input",t[34]),N(J,"input",t[35])],H=!0)},p(t,e){1&e[0]&&o.value!==t[0].iotmSettings.routerssid&&M(o,t[0].iotmSettings.routerssid),1&e[0]&&a.value!==t[0].iotmSettings.routerpass&&M(a,t[0].iotmSettings.routerpass),1&e[0]&&h.value!==t[0].iotmSettings.mqttServer&&M(h,t[0].iotmSettings.mqttServer),1&e[0]&&y.value!==t[0].iotmSettings.mqttPort&&M(y,t[0].iotmSettings.mqttPort),1&e[0]&&T.value!==t[0].iotmSettings.mqttPrefix&&M(T,t[0].iotmSettings.mqttPrefix),1&e[0]&&U.value!==t[0].iotmSettings.mqttUser&&M(U,t[0].iotmSettings.mqttUser),1&e[0]&&J.value!==t[0].iotmSettings.mqttPass&&M(J,t[0].iotmSettings.mqttPass)},d(t){t&&w(e),t&&w(i),t&&w(s),t&&w(f),t&&w(d),t&&w(g),t&&w(v),t&&w(k),t&&w(C),t&&w(R),t&&w(G),t&&w(A),t&&w(F),t&&w(z),t&&w(K),H=!1,l(Q)}}}function re(t){let e,n,o,r,i,s,c,a,u,f,d,p,h,m,g,v,$,y,x,C,q,E,R,G,B,U,D,A,F,L="esp8266_4mb"==t[9].text&&Qt(t),J="esp32_4mb"==t[9].text&&Wt(t),V="esp8266_1mb"==t[9].text&&Xt(t),z="esp8266_1mb_ota"==t[9].text&&Yt(t),K=t[16],H=[];for(let e=0;et[23].call(s))),P(f,"align","right"),void 0===t[9]&&Z((()=>t[25].call(f))),P(e,"align","right"),I(m,"display",t[8]?"none":"inline"),I($,"display",t[8]?"inline":"none"),E.hidden=!0,P(E,"type","text"),P(E,"name","configpost"),P(E,"class","svelte-185rejv"),P(G,"align","center"),P(q,"action","index.php"),P(q,"method","post"),P(q,"name","fwbuilder"),P(U,"type","submit"),I(U,"border","1px solid lightblue"),I(U,"border-radius","8px"),P(U,"name","send"),U.value="Сохранить myProfile.json",P(U,"class","svelte-185rejv")},m(l,w){_(l,e,w),L&&L.m(e,null),b(e,n),J&&J.m(e,null),b(e,o),V&&V.m(e,null),b(e,r),z&&z.m(e,null),b(e,i),b(e,s),b(s,c),b(s,a),T(s,t[7]),b(e,u),b(e,f);for(let t=0;t{L=null})),ft()),"esp32_4mb"==t[9].text?J?(J.p(t,l),512&l[0]&&dt(J,1)):(J=Wt(t),J.c(),dt(J,1),J.m(e,o)):J&&(ut(),pt(J,1,1,(()=>{J=null})),ft()),"esp8266_1mb"==t[9].text?V?(V.p(t,l),512&l[0]&&dt(V,1)):(V=Xt(t),V.c(),dt(V,1),V.m(e,r)):V&&(ut(),pt(V,1,1,(()=>{V=null})),ft()),"esp8266_1mb_ota"==t[9].text?z?(z.p(t,l),512&l[0]&&dt(z,1)):(z=Yt(t),z.c(),dt(z,1),z.m(e,i)):z&&(ut(),pt(z,1,1,(()=>{z=null})),ft()),128&l[0]&&T(s,t[7]),65536&l[0]){let e;for(K=t[16],e=0;e{d&&d.end(1),f=mt(o,n[11],{key:n[74].path}),f.start()})),h=!0)},o(t){f&&f.invalidate(),d=gt(o,n[10],{key:n[74].path}),h=!1},d(t){t&&w(o),t&&d&&d.end(),m=!1,l(g)}}}function ie(t){let e,n,o,r,l,i,s=t[68]+"",c=[],a=new Map,u=t[0].modules[t[68]].filter($e);const f=t=>t[74].path;for(let e=0;e{d&&d.end(1),f=mt(o,n[11],{key:n[74].path}),f.start()})),h=!0)},o(t){f&&f.invalidate(),d=gt(o,n[10],{key:n[74].path}),h=!1},d(t){t&&w(o),t&&d&&d.end(),m=!1,l(g)}}}function ce(t){let e,n,o,r,l,i,s=t[68]+"",c=[],a=new Map,u=t[0].modules[t[68]].filter(be);const f=t=>t[74].path;for(let e=0;e2,d=f&&de(),p=t[62].params,h=[];for(let e=0;e2),f?d||(d=de(),d.c(),d.m(e,s)):d&&(d.d(1),d=null),16&n[0]){let o;for(p=t[62].params,o=0;opt(g[t],1,1,(()=>{g[t]=null}));let $=Object.entries(t[0].modules),y=[];for(let e=0;e<$.length;e+=1)y[e]=ce(Jt(t,$,e));const x=t=>pt(y[t],1,1,(()=>{y[t]=null}));let j=t[6]&&ae(t);return{c(){e=S("p"),yt(n.$$.fragment),o=O(),r=S("div"),l=S("div"),i=S("h2"),i.textContent="доступные модули",s=O();for(let t=0;t{j=null})),ft())},i(t){if(!h){dt(n.$$.fragment,t);for(let t=0;t!t.active,be=t=>t.active;function ye(t,e,n){let o="https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4dev/myProfile.json?1",r={},l=0,i={},s=[],c=[],a=[],u=[],f=!1,d="dev",p={};async function h(t){try{let e=await fetch(t,{mode:"cors",method:"GET"});e.ok?n(0,r=await e.json()):console.log("error",e.statusText)}catch(t){console.log(t)}}r={iotmSettings:{},projectProp:{platformio:{}},modules:{"Виртуальные элементы":[],"Сенсоры":[],"Исполнительные устройства":[],"Экраны":[]}},h(o),async function(t){try{let e=await fetch(t,{mode:"cors",method:"GET"});e.ok?p=await e.json():console.log("error",e.statusText)}catch(t){console.log(t)}}("https://iotmanager.org/firmwarebuilder/moduleSize.json?1");const[m,g]=Ct({fallback(t,e){const n=getComputedStyle(t),o="none"===n.transform?"":n.transform;return{duration:600,easing:Ot,css:t=>`\n\t\t\t\t\ttransform: ${o} scale(${t});\n\t\t\t\t\topacity: ${t}\n\t\t\t\t`}}});function v(t){console.log(t.path),o="https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4stable/"+JSON.parse(JSON.stringify(t.path).replace(/\\\\/g,"/"))+"/modinfo.json",i=[],n(2,s=[]),n(3,c=[]),n(3,c.name="Данные загружаются...",c),n(4,a=[]),n(5,u=[]),async function(t){i=[];try{let e=await fetch(t,{mode:"cors",method:"GET"});e.ok?(i=await e.json(),n(2,s=i),n(3,c=i.configItem[0]),n(4,a=i.about),n(5,u=a.propInfo)):console.log("error",e.statusText)}catch(t){console.log(t)}}(o),n(6,f=!0)}let $=!0;function b(t){n(8,$=t)}function y(){o="dev"==d?"https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4dev/myProfile.json?1":"https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4stable/myProfile.json?1",h(o)}function x(){n(1,l=0),n(0,r.projectProp.platformio.default_envs=_.text,r),k()}let _=[],w=[{id:1,text:"esp8266_4mb"},{id:2,text:"esp32_4mb"},{id:3,text:"esp8266_1mb"},{id:4,text:"esp8266_1mb_ota"}];function k(){n(1,l=0);for(const[t,e]of Object.entries(r.modules))for(const[e,o]of Object.entries(r.modules[t].filter((t=>t.active)))){let t=o.path.slice(o.path.lastIndexOf("/")+1,o.path.length);p[t]&&n(1,l+=Math.round(p[t][_.text]))}"esp8266_4mb"==_.text&&n(1,l=Math.round(l/6110)),"esp32_4mb"==_.text&&n(1,l=Math.round(l/3243)),"esp8266_1mb"==_.text&&n(1,l=Math.round(l/3237)),"esp8266_1mb_ota"==_.text&&n(1,l=Math.round(l/2601))}function S(){var t=document.createElement("a");t.href=window.URL.createObjectURL(new Blob([JSON.stringify(r)],{type:"text/plain"})),t.download="myProfile.json",console.log(JSON.stringify(r))}return[r,l,s,c,a,u,f,d,$,_,m,g,v,b,y,x,w,k,S,function(t){l=t,n(1,l)},function(t){l=t,n(1,l)},function(t){l=t,n(1,l)},function(t){l=t,n(1,l)},function(){d=E(this),n(7,d)},()=>y(),function(){_=E(this),n(9,_),n(16,w)},()=>x(),()=>b(!0),()=>b(!1),function(){r.iotmSettings.routerssid=this.value,n(0,r)},function(){r.iotmSettings.routerpass=this.value,n(0,r)},function(){r.iotmSettings.mqttServer=this.value,n(0,r)},function(){r.iotmSettings.mqttPort=this.value,n(0,r)},function(){r.iotmSettings.mqttPrefix=this.value,n(0,r)},function(){r.iotmSettings.mqttUser=this.value,n(0,r)},function(){r.iotmSettings.mqttPass=this.value,n(0,r)},function(){r=this.value,n(0,r)},()=>S(),function(t,e){t[e].active=this.checked,n(0,r)},t=>v(t),function(t,e){t[e].active=this.checked,n(0,r)},t=>v(t),()=>n(6,f=!1)]}return new class extends St{constructor(t){super(),kt(this,t,ye,ve,s,{},null,[-1,-1,-1])}}({target:document.body})}(); + ***************************************************************************** */ function Ct( + t + ) { + var { fallback: e } = t, + o = (function (t, e) { + var n = {}; + for (var o in t) + Object.prototype.hasOwnProperty.call(t, o) && + e.indexOf(o) < 0 && + (n[o] = t[o]); + if (null != t && "function" == typeof Object.getOwnPropertySymbols) { + var r = 0; + for (o = Object.getOwnPropertySymbols(t); r < o.length; r++) + e.indexOf(o[r]) < 0 && + Object.prototype.propertyIsEnumerable.call(t, o[r]) && + (n[o[r]] = t[o[r]]); + } + return n; + })(t, ["fallback"]); + const r = new Map(), + l = new Map(); + function s(t, r, l) { + return (s, c) => ( + t.set(c.key, { rect: s.getBoundingClientRect() }), + () => { + if (r.has(c.key)) { + const { rect: t } = r.get(c.key); + return ( + r.delete(c.key), + (function (t, e, r) { + const { + delay: l = 0, + duration: s = (t) => 30 * Math.sqrt(t), + easing: c = jt, + } = n(n({}, o), r), + a = e.getBoundingClientRect(), + u = t.left - a.left, + f = t.top - a.top, + d = t.width / a.width, + p = t.height / a.height, + h = Math.sqrt(u * u + f * f), + m = getComputedStyle(e), + g = "none" === m.transform ? "" : m.transform, + v = +m.opacity; + return { + delay: l, + duration: i(s) ? s(h) : s, + easing: c, + css: (t, e) => + `\n\t\t\t\topacity: ${ + t * v + };\n\t\t\t\ttransform-origin: top left;\n\t\t\t\ttransform: ${g} translate(${ + e * u + }px,${e * f}px) scale(${t + (1 - t) * d}, ${ + t + (1 - t) * p + });\n\t\t\t`, + }; + })(t, s, c) + ); + } + return t.delete(c.key), e && e(s, c, l); + } + ); + } + return [s(l, r, !1), s(r, l, !0)]; + } + function Nt(t, { from: e, to: n }, o = {}) { + const r = getComputedStyle(t), + l = "none" === r.transform ? "" : r.transform, + [s, c] = r.transformOrigin.split(" ").map(parseFloat), + a = e.left + (e.width * s) / n.width - (n.left + s), + u = e.top + (e.height * c) / n.height - (n.top + c), + { + delay: f = 0, + duration: d = (t) => 120 * Math.sqrt(t), + easing: p = jt, + } = o; + return { + delay: f, + duration: i(d) ? d(Math.sqrt(a * a + u * u)) : d, + easing: p, + css: (t, o) => { + const r = o * a, + i = o * u, + s = t + (o * e.width) / n.width, + c = t + (o * e.height) / n.height; + return `transform: ${l} translate(${r}px, ${i}px) scale(${s}, ${c});`; + }, + }; + } + function Pt(e) { + let n, o, r, l, i, s, c, a, u, f, d; + return { + c() { + (n = S("div")), + (o = S("div")), + (r = S("div")), + (l = O()), + (i = S("div")), + (s = O()), + (c = S("div")), + (a = O()), + (u = S("div")), + (f = j(e[0])), + (d = j("%")), + P(r, "class", "gauge-fill gauge-bg svelte-1er7lpr"), + P(i, "class", "gauge-fill svelte-1er7lpr"), + I(i, "transform", "rotate(-" + e[2] + "deg)"), + I(i, "box-shadow", "inset 0 0 60px -5px " + e[1]), + P(c, "class", "gauge-white svelte-1er7lpr"), + P(u, "class", "gauge-value svelte-1er7lpr"), + P(o, "class", "gauge-overflow svelte-1er7lpr"), + P(n, "class", "gauge svelte-1er7lpr"); + }, + m(t, e) { + _(t, n, e), + b(n, o), + b(o, r), + b(o, l), + b(o, i), + b(o, s), + b(o, c), + b(o, a), + b(o, u), + b(u, f), + b(u, d); + }, + p(t, [e]) { + 4 & e && I(i, "transform", "rotate(-" + t[2] + "deg)"), + 2 & e && I(i, "box-shadow", "inset 0 0 60px -5px " + t[1]), + 1 & e && q(f, t[0]); + }, + i: t, + o: t, + d(t) { + t && w(n); + }, + }; + } + function qt(t, e, n) { + let o, + r, + { title: l } = e, + { value: i = l } = e, + { thresholds: s = "90,100" } = e, + { colors: c = "#7CFC00,#FFD700,#FF0000" } = e; + return ( + (t.$$set = (t) => { + "title" in t && n(3, (l = t.title)), + "value" in t && n(0, (i = t.value)), + "thresholds" in t && n(4, (s = t.thresholds)), + "colors" in t && n(5, (c = t.colors)); + }), + (t.$$.update = () => { + if (49 & t.$$.dirty) { + const t = String(s).split(","), + e = t.indexOf(t.find((t) => i < parseInt(t, 10))), + r = String(c).split(","); + n(1, (o = r[e] || r[r.length - 1])); + } + 1 & t.$$.dirty && n(2, (r = 1.8 * (100 - i))); + }), + [i, o, r, l, s, c] + ); + } + class Mt extends St { + constructor(t) { + super(), + kt(this, t, qt, Pt, s, { + title: 3, + value: 0, + thresholds: 4, + colors: 5, + }); + } + } + const It = (t) => ({}), + Tt = (t) => ({}); + function Et(t) { + let e, n, o, r, i, s, a, p, h, m, g, v, $; + const y = t[4].header, + x = c(y, t, t[3], Tt), + k = t[4].default, + j = c(k, t, t[3], null); + return { + c() { + (e = S("div")), + (n = O()), + (o = S("div")), + x && x.c(), + (r = O()), + (i = S("hr")), + (s = O()), + j && j.c(), + (a = O()), + (p = S("hr")), + (h = O()), + (m = S("button")), + (m.textContent = "Close"), + P(e, "class", "modal-background svelte-5a6h72"), + I(m, "float", "right"), + I(m, "margin-right", "2%"), + I(m, "border", "1px solid lightblue"), + I(m, "border-radius", "8px"), + (m.autofocus = !0), + P(m, "class", "svelte-5a6h72"), + P(o, "class", "modal svelte-5a6h72"), + P(o, "role", "dialog"), + P(o, "aria-modal", "true"); + }, + m(l, c) { + _(l, e, c), + _(l, n, c), + _(l, o, c), + x && x.m(o, null), + b(o, r), + b(o, i), + b(o, s), + j && j.m(o, null), + b(o, a), + b(o, p), + b(o, h), + b(o, m), + t[5](o), + (g = !0), + m.focus(), + v || + (($ = [ + N(window, "keydown", t[2]), + N(e, "click", t[1]), + N(m, "click", t[1]), + ]), + (v = !0)); + }, + p(t, [e]) { + x && + x.p && + (!g || 8 & e) && + f(x, y, t, t[3], g ? u(y, t[3], e, It) : d(t[3]), Tt), + j && + j.p && + (!g || 8 & e) && + f(j, k, t, t[3], g ? u(k, t[3], e, null) : d(t[3]), null); + }, + i(t) { + g || (dt(x, t), dt(j, t), (g = !0)); + }, + o(t) { + pt(x, t), pt(j, t), (g = !1); + }, + d(r) { + r && w(e), + r && w(n), + r && w(o), + x && x.d(r), + j && j.d(r), + t[5](null), + (v = !1), + l($); + }, + }; + } + function Rt(t, e, n) { + let { $$slots: o = {}, $$scope: r } = e; + const l = (function () { + const t = z(); + return (e, n, { cancelable: o = !1 } = {}) => { + const r = t.$$.callbacks[e]; + if (r) { + const l = R(e, n, { cancelable: o }); + return ( + r.slice().forEach((e) => { + e.call(t, l); + }), + !l.defaultPrevented + ); + } + return !0; + }; + })(), + i = () => l("close"); + let s; + const c = "undefined" != typeof document && document.activeElement; + var a; + return ( + c && + ((a = () => { + c.focus(); + }), + z().$$.on_destroy.push(a)), + (t.$$set = (t) => { + "$$scope" in t && n(3, (r = t.$$scope)); + }), + [ + s, + i, + (t) => { + if ("Escape" !== t.key) { + if ("Tab" === t.key) { + const e = s.querySelectorAll("*"), + n = Array.from(e).filter((t) => t.tabIndex >= 0); + let o = n.indexOf(document.activeElement); + -1 === o && t.shiftKey && (o = 0), + (o += n.length + (t.shiftKey ? -1 : 1)), + (o %= n.length), + n[o].focus(), + t.preventDefault(); + } + } else i(); + }, + r, + o, + function (t) { + H[t ? "unshift" : "push"](() => { + (s = t), n(0, s); + }); + }, + ] + ); + } + class Gt extends St { + constructor(t) { + super(), kt(this, t, Rt, Et, s, {}); + } + } + function Bt(t) { + let e, n; + const o = t[1].default, + r = c(o, t, t[0], null); + return { + c() { + (e = S("div")), r && r.c(), P(e, "class", "box svelte-1q88uwi"); + }, + m(t, o) { + _(t, e, o), r && r.m(e, null), (n = !0); + }, + p(t, [e]) { + r && + r.p && + (!n || 1 & e) && + f(r, o, t, t[0], n ? u(o, t[0], e, null) : d(t[0]), null); + }, + i(t) { + n || (dt(r, t), (n = !0)); + }, + o(t) { + pt(r, t), (n = !1); + }, + d(t) { + t && w(e), r && r.d(t); + }, + }; + } + function Ut(t, e, n) { + let { $$slots: o = {}, $$scope: r } = e; + return ( + (t.$$set = (t) => { + "$$scope" in t && n(0, (r = t.$$scope)); + }), + [r, o] + ); + } + class Dt extends St { + constructor(t) { + super(), kt(this, t, Ut, Bt, s, {}); + } + } + function At(t, e, n) { + const o = t.slice(); + return (o[62] = e[n]), o; + } + function Ft(t, e, n) { + const o = t.slice(); + return (o[65] = e[n]), o; + } + function Lt(t, e, n) { + const o = t.slice(); + return (o[68] = e[n][0]), (o[69] = e[n][1]), o; + } + function Jt(t, e, n) { + const o = t.slice(); + return (o[68] = e[n][0]), (o[69] = e[n][1]), o; + } + function Vt(t, e, n) { + const o = t.slice(); + return (o[74] = e[n]), (o[75] = e), (o[76] = n), o; + } + function zt(t, e, n) { + const o = t.slice(); + return (o[68] = e[n][0]), (o[69] = e[n][1]), o; + } + function Kt(t, e, n) { + const o = t.slice(); + return (o[74] = e[n]), (o[79] = e), (o[80] = n), o; + } + function Ht(t, e, n) { + const o = t.slice(); + return (o[81] = e[n]), o; + } + function Qt(t) { + let e, n, o, r; + function l(e) { + t[19](e); + } + let i = {}; + return ( + void 0 !== t[1] && (i.value = t[1]), + (e = new Mt({ props: i })), + H.push(() => bt(e, "value", l)), + { + c() { + yt(e.$$.fragment), (o = j("                   ")); + }, + m(t, n) { + xt(e, t, n), _(t, o, n), (r = !0); + }, + p(t, o) { + const r = {}; + !n && 2 & o[0] && ((n = !0), (r.value = t[1]), tt(() => (n = !1))), + e.$set(r); + }, + i(t) { + r || (dt(e.$$.fragment, t), (r = !0)); + }, + o(t) { + pt(e.$$.fragment, t), (r = !1); + }, + d(t) { + _t(e, t), t && w(o); + }, + } + ); + } + function Wt(t) { + let e, n, o, r; + function l(e) { + t[20](e); + } + let i = {}; + return ( + void 0 !== t[1] && (i.value = t[1]), + (e = new Mt({ props: i })), + H.push(() => bt(e, "value", l)), + { + c() { + yt(e.$$.fragment), (o = j("                   ")); + }, + m(t, n) { + xt(e, t, n), _(t, o, n), (r = !0); + }, + p(t, o) { + const r = {}; + !n && 2 & o[0] && ((n = !0), (r.value = t[1]), tt(() => (n = !1))), + e.$set(r); + }, + i(t) { + r || (dt(e.$$.fragment, t), (r = !0)); + }, + o(t) { + pt(e.$$.fragment, t), (r = !1); + }, + d(t) { + _t(e, t), t && w(o); + }, + } + ); + } + function Xt(t) { + let e, n, o, r; + function l(e) { + t[21](e); + } + let i = {}; + return ( + void 0 !== t[1] && (i.value = t[1]), + (e = new Mt({ props: i })), + H.push(() => bt(e, "value", l)), + { + c() { + yt(e.$$.fragment), (o = j("                   ")); + }, + m(t, n) { + xt(e, t, n), _(t, o, n), (r = !0); + }, + p(t, o) { + const r = {}; + !n && 2 & o[0] && ((n = !0), (r.value = t[1]), tt(() => (n = !1))), + e.$set(r); + }, + i(t) { + r || (dt(e.$$.fragment, t), (r = !0)); + }, + o(t) { + pt(e.$$.fragment, t), (r = !1); + }, + d(t) { + _t(e, t), t && w(o); + }, + } + ); + } + function Yt(t) { + let e, n, o, r; + function l(e) { + t[22](e); + } + let i = {}; + return ( + void 0 !== t[1] && (i.value = t[1]), + (e = new Mt({ props: i })), + H.push(() => bt(e, "value", l)), + { + c() { + yt(e.$$.fragment), (o = j("                   ")); + }, + m(t, n) { + xt(e, t, n), _(t, o, n), (r = !0); + }, + p(t, o) { + const r = {}; + !n && 2 & o[0] && ((n = !0), (r.value = t[1]), tt(() => (n = !1))), + e.$set(r); + }, + i(t) { + r || (dt(e.$$.fragment, t), (r = !0)); + }, + o(t) { + pt(e.$$.fragment, t), (r = !1); + }, + d(t) { + _t(e, t), t && w(o); + }, + } + ); + } + function Zt(e) { + let n, + o, + r, + l = e[81].text + ""; + return { + c() { + (n = S("option")), + (o = j(l)), + (r = O()), + (n.__value = e[81]), + (n.value = n.__value); + }, + m(t, e) { + _(t, n, e), b(n, o), b(n, r); + }, + p: t, + d(t) { + t && w(n); + }, + }; + } + function te(e) { + let n, + o, + r, + l = e[81].text + ""; + return { + c() { + (n = S("option")), + (o = j(l)), + (r = O()), + (n.__value = e[81]), + (n.value = n.__value); + }, + m(t, e) { + _(t, n, e), b(n, o), b(n, r); + }, + p: t, + d(t) { + t && w(n); + }, + }; + } + function ee(e) { + let n, + o, + r, + l = e[81].text + ""; + return { + c() { + (n = S("option")), + (o = j(l)), + (r = O()), + (n.__value = e[81]), + (n.value = n.__value); + }, + m(t, e) { + _(t, n, e), b(n, o), b(n, r); + }, + p: t, + d(t) { + t && w(n); + }, + }; + } + function ne(t) { + let e; + let n = (function (t, e) { + return "esp8266_1mb" == t[81].text + ? ee + : "esp8266_1mb_ota" == t[81].text + ? te + : Zt; + })(t), + o = n(t); + return { + c() { + o.c(), (e = C()); + }, + m(t, n) { + o.m(t, n), _(t, e, n); + }, + p(t, e) { + o.p(t, e); + }, + d(t) { + o.d(t), t && w(e); + }, + }; + } + function oe(t) { + let e, + n, + o, + r, + i, + s, + c, + a, + u, + f, + d, + p, + h, + m, + g, + v, + $, + y, + x, + k, + C, + q, + T, + E, + R, + G, + B, + U, + D, + A, + F, + L, + J, + V, + z, + K, + H, + Q; + return { + c() { + (e = S("p")), + (n = j("wifi сеть      ")), + (o = S("input")), + (r = S("br")), + (i = O()), + (s = S("p")), + (c = j("пароль       ")), + (a = S("input")), + (u = S("br")), + (f = O()), + (d = S("p")), + (p = j("MQTT сервер      ")), + (h = S("input")), + (m = S("br")), + (g = O()), + (v = S("p")), + ($ = j("порт       ")), + (y = S("input")), + (x = S("br")), + (k = O()), + (C = S("p")), + (q = j("префикс      ")), + (T = S("input")), + (E = S("br")), + (R = O()), + (G = S("p")), + (B = j("логин       ")), + (U = S("input")), + (D = S("br")), + (A = O()), + (F = S("p")), + (L = j("пароль       ")), + (J = S("input")), + (V = S("br")), + (z = O()), + (K = S("br")), + I(o, "float", "right"), + P(o, "class", "svelte-185rejv"), + I(e, "color", "#ccc"), + I(a, "float", "right"), + P(a, "class", "svelte-185rejv"), + I(s, "color", "#ccc"), + I(h, "float", "right"), + P(h, "class", "svelte-185rejv"), + I(d, "color", "#ccc"), + I(y, "float", "right"), + P(y, "class", "svelte-185rejv"), + I(v, "color", "#ccc"), + I(T, "float", "right"), + P(T, "class", "svelte-185rejv"), + I(C, "color", "#ccc"), + I(U, "float", "right"), + P(U, "class", "svelte-185rejv"), + I(G, "color", "#ccc"), + I(J, "float", "right"), + P(J, "class", "svelte-185rejv"), + I(F, "color", "#ccc"); + }, + m(l, w) { + _(l, e, w), + b(e, n), + b(e, o), + M(o, t[0].iotmSettings.routerssid), + b(e, r), + _(l, i, w), + _(l, s, w), + b(s, c), + b(s, a), + M(a, t[0].iotmSettings.routerpass), + b(s, u), + _(l, f, w), + _(l, d, w), + b(d, p), + b(d, h), + M(h, t[0].iotmSettings.mqttServer), + b(d, m), + _(l, g, w), + _(l, v, w), + b(v, $), + b(v, y), + M(y, t[0].iotmSettings.mqttPort), + b(v, x), + _(l, k, w), + _(l, C, w), + b(C, q), + b(C, T), + M(T, t[0].iotmSettings.mqttPrefix), + b(C, E), + _(l, R, w), + _(l, G, w), + b(G, B), + b(G, U), + M(U, t[0].iotmSettings.mqttUser), + b(G, D), + _(l, A, w), + _(l, F, w), + b(F, L), + b(F, J), + M(J, t[0].iotmSettings.mqttPass), + b(F, V), + _(l, z, w), + _(l, K, w), + H || + ((Q = [ + N(o, "input", t[29]), + N(a, "input", t[30]), + N(h, "input", t[31]), + N(y, "input", t[32]), + N(T, "input", t[33]), + N(U, "input", t[34]), + N(J, "input", t[35]), + ]), + (H = !0)); + }, + p(t, e) { + 1 & e[0] && + o.value !== t[0].iotmSettings.routerssid && + M(o, t[0].iotmSettings.routerssid), + 1 & e[0] && + a.value !== t[0].iotmSettings.routerpass && + M(a, t[0].iotmSettings.routerpass), + 1 & e[0] && + h.value !== t[0].iotmSettings.mqttServer && + M(h, t[0].iotmSettings.mqttServer), + 1 & e[0] && + y.value !== t[0].iotmSettings.mqttPort && + M(y, t[0].iotmSettings.mqttPort), + 1 & e[0] && + T.value !== t[0].iotmSettings.mqttPrefix && + M(T, t[0].iotmSettings.mqttPrefix), + 1 & e[0] && + U.value !== t[0].iotmSettings.mqttUser && + M(U, t[0].iotmSettings.mqttUser), + 1 & e[0] && + J.value !== t[0].iotmSettings.mqttPass && + M(J, t[0].iotmSettings.mqttPass); + }, + d(t) { + t && w(e), + t && w(i), + t && w(s), + t && w(f), + t && w(d), + t && w(g), + t && w(v), + t && w(k), + t && w(C), + t && w(R), + t && w(G), + t && w(A), + t && w(F), + t && w(z), + t && w(K), + (H = !1), + l(Q); + }, + }; + } + function re(t) { + let e, + n, + o, + r, + i, + s, + c, + a, + u, + f, + d, + p, + h, + m, + g, + v, + $, + y, + x, + C, + q, + E, + R, + G, + B, + U, + D, + A, + F, + L = "esp8266_4mb" == t[9].text && Qt(t), + J = "esp32_4mb" == t[9].text && Wt(t), + V = "esp8266_1mb" == t[9].text && Xt(t), + z = "esp8266_1mb_ota" == t[9].text && Yt(t), + K = t[16], + H = []; + for (let e = 0; e < K.length; e += 1) H[e] = ne(Ht(t, K, e)); + let Q = t[8] && oe(t); + return { + c() { + (e = S("form")), + L && L.c(), + (n = O()), + J && J.c(), + (o = O()), + V && V.c(), + (r = O()), + z && z.c(), + (i = O()), + (s = S("select")), + (c = S("option")), + (c.textContent = "ver4dev"), + (a = S("option")), + (a.textContent = "stabile"), + (u = O()), + (f = S("select")); + for (let t = 0; t < H.length; t += 1) H[t].c(); + (d = O()), + (p = S("br")), + (h = O()), + (m = S("p")), + (g = j("⏬")), + (v = O()), + ($ = S("p")), + (y = j("⏫")), + (x = O()), + Q && Q.c(), + (C = O()), + (q = S("form")), + (E = S("input")), + (R = O()), + (G = S("p")), + (B = O()), + (U = S("input")), + (c.__value = "dev"), + (c.value = c.__value), + (a.__value = "stabile"), + (a.value = a.__value), + P(s, "align", "right"), + void 0 === t[7] && Z(() => t[23].call(s)), + P(f, "align", "right"), + void 0 === t[9] && Z(() => t[25].call(f)), + P(e, "align", "right"), + I(m, "display", t[8] ? "none" : "inline"), + I($, "display", t[8] ? "inline" : "none"), + (E.hidden = !0), + P(E, "type", "text"), + P(E, "name", "configpost"), + P(E, "class", "svelte-185rejv"), + P(G, "align", "center"), + P(q, "action", "index.php"), + P(q, "method", "post"), + P(q, "name", "fwbuilder"), + P(U, "type", "submit"), + I(U, "border", "1px solid lightblue"), + I(U, "border-radius", "8px"), + P(U, "name", "send"), + (U.value = "Сохранить myProfile.json"), + P(U, "class", "svelte-185rejv"); + }, + m(l, w) { + _(l, e, w), + L && L.m(e, null), + b(e, n), + J && J.m(e, null), + b(e, o), + V && V.m(e, null), + b(e, r), + z && z.m(e, null), + b(e, i), + b(e, s), + b(s, c), + b(s, a), + T(s, t[7]), + b(e, u), + b(e, f); + for (let t = 0; t < H.length; t += 1) H[t].m(f, null); + T(f, t[9]), + _(l, d, w), + _(l, p, w), + _(l, h, w), + _(l, m, w), + b(m, g), + _(l, v, w), + _(l, $, w), + b($, y), + _(l, x, w), + Q && Q.m(l, w), + _(l, C, w), + _(l, q, w), + b(q, E), + M(E, t[0]), + b(q, R), + b(q, G), + _(l, B, w), + _(l, U, w), + (D = !0), + A || + ((F = [ + N(s, "change", t[23]), + N(s, "change", t[24]), + N(f, "change", t[25]), + N(f, "change", t[26]), + N(m, "click", t[27]), + N($, "click", t[28]), + N(E, "input", t[36]), + N(U, "click", t[37]), + ]), + (A = !0)); + }, + p(t, l) { + if ( + ("esp8266_4mb" == t[9].text + ? L + ? (L.p(t, l), 512 & l[0] && dt(L, 1)) + : ((L = Qt(t)), L.c(), dt(L, 1), L.m(e, n)) + : L && + (ut(), + pt(L, 1, 1, () => { + L = null; + }), + ft()), + "esp32_4mb" == t[9].text + ? J + ? (J.p(t, l), 512 & l[0] && dt(J, 1)) + : ((J = Wt(t)), J.c(), dt(J, 1), J.m(e, o)) + : J && + (ut(), + pt(J, 1, 1, () => { + J = null; + }), + ft()), + "esp8266_1mb" == t[9].text + ? V + ? (V.p(t, l), 512 & l[0] && dt(V, 1)) + : ((V = Xt(t)), V.c(), dt(V, 1), V.m(e, r)) + : V && + (ut(), + pt(V, 1, 1, () => { + V = null; + }), + ft()), + "esp8266_1mb_ota" == t[9].text + ? z + ? (z.p(t, l), 512 & l[0] && dt(z, 1)) + : ((z = Yt(t)), z.c(), dt(z, 1), z.m(e, i)) + : z && + (ut(), + pt(z, 1, 1, () => { + z = null; + }), + ft()), + 128 & l[0] && T(s, t[7]), + 65536 & l[0]) + ) { + let e; + for (K = t[16], e = 0; e < K.length; e += 1) { + const n = Ht(t, K, e); + H[e] ? H[e].p(n, l) : ((H[e] = ne(n)), H[e].c(), H[e].m(f, null)); + } + for (; e < H.length; e += 1) H[e].d(1); + H.length = K.length; + } + 66048 & l[0] && T(f, t[9]), + (!D || 256 & l[0]) && I(m, "display", t[8] ? "none" : "inline"), + (!D || 256 & l[0]) && I($, "display", t[8] ? "inline" : "none"), + t[8] + ? Q + ? Q.p(t, l) + : ((Q = oe(t)), Q.c(), Q.m(C.parentNode, C)) + : Q && (Q.d(1), (Q = null)), + 1 & l[0] && E.value !== t[0] && M(E, t[0]); + }, + i(t) { + D || (dt(L), dt(J), dt(V), dt(z), (D = !0)); + }, + o(t) { + pt(L), pt(J), pt(V), pt(z), (D = !1); + }, + d(t) { + t && w(e), + L && L.d(), + J && J.d(), + V && V.d(), + z && z.d(), + k(H, t), + t && w(d), + t && w(p), + t && w(h), + t && w(m), + t && w(v), + t && w($), + t && w(x), + Q && Q.d(t), + t && w(C), + t && w(q), + t && w(B), + t && w(U), + (A = !1), + l(F); + }, + }; + } + function le(e, n) { + let o, + r, + i, + s, + c, + a, + u, + f, + d, + p, + h, + m, + g, + v = + n[74].path.slice(n[74].path.lastIndexOf("/") + 1, n[74].path.length) + + "", + $ = t; + function y() { + n[38].call(r, n[79], n[80]); + } + function x() { + return n[39](n[74]); + } + return { + key: e, + first: null, + c() { + (o = S("label")), + (r = S("input")), + (i = O()), + (s = j(v)), + (c = O()), + (a = S("button")), + (a.textContent = "?"), + (u = O()), + P(r, "type", "checkbox"), + P(r, "class", "svelte-185rejv"), + P(a, "class", "svelte-185rejv"), + P(o, "class", "svelte-185rejv"), + (this.first = o); + }, + m(t, e) { + _(t, o, e), + b(o, r), + (r.checked = n[74].active), + b(o, i), + b(o, s), + b(o, c), + b(o, a), + b(o, u), + (h = !0), + m || + ((g = [ + N(r, "change", y), + N(r, "click", n[17]()), + N(a, "click", x), + ]), + (m = !0)); + }, + p(t, e) { + (n = t), + 1 & e[0] && (r.checked = n[74].active), + (!h || 1 & e[0]) && + v !== + (v = + n[74].path.slice( + n[74].path.lastIndexOf("/") + 1, + n[74].path.length + ) + "") && + q(s, v); + }, + r() { + p = o.getBoundingClientRect(); + }, + f() { + L(o), $(), J(o, p); + }, + a() { + $(), ($ = F(o, p, Nt, {})); + }, + i(t) { + h || + (Z(() => { + d && d.end(1), (f = mt(o, n[11], { key: n[74].path })), f.start(); + }), + (h = !0)); + }, + o(t) { + f && f.invalidate(), (d = gt(o, n[10], { key: n[74].path })), (h = !1); + }, + d(t) { + t && w(o), t && d && d.end(), (m = !1), l(g); + }, + }; + } + function ie(t) { + let e, + n, + o, + r, + l, + i, + s = t[68] + "", + c = [], + a = new Map(), + u = t[0].modules[t[68]].filter($e); + const f = (t) => t[74].path; + for (let e = 0; e < u.length; e += 1) { + let n = Kt(t, u, e), + o = f(n); + a.set(o, (c[e] = le(o, n))); + } + return { + c() { + (e = S("b")), (n = j(s)), (o = S("br")), (r = O()); + for (let t = 0; t < c.length; t += 1) c[t].c(); + l = C(); + }, + m(t, s) { + _(t, e, s), b(e, n), _(t, o, s), _(t, r, s); + for (let e = 0; e < c.length; e += 1) c[e].m(t, s); + _(t, l, s), (i = !0); + }, + p(t, e) { + if ( + ((!i || 1 & e[0]) && s !== (s = t[68] + "") && q(n, s), 135169 & e[0]) + ) { + (u = t[0].modules[t[68]].filter($e)), ut(); + for (let t = 0; t < c.length; t += 1) c[t].r(); + c = $t(c, e, f, 1, t, u, a, l.parentNode, vt, le, l, Kt); + for (let t = 0; t < c.length; t += 1) c[t].a(); + ft(); + } + }, + i(t) { + if (!i) { + for (let t = 0; t < u.length; t += 1) dt(c[t]); + i = !0; + } + }, + o(t) { + for (let t = 0; t < c.length; t += 1) pt(c[t]); + i = !1; + }, + d(t) { + t && w(e), t && w(o), t && w(r); + for (let e = 0; e < c.length; e += 1) c[e].d(t); + t && w(l); + }, + }; + } + function se(e, n) { + let o, + r, + i, + s, + c, + a, + u, + f, + d, + p, + h, + m, + g, + v = + n[74].path.slice(n[74].path.lastIndexOf("/") + 1, n[74].path.length) + + "", + $ = t; + function y() { + n[40].call(r, n[75], n[76]); + } + function x() { + return n[41](n[74]); + } + return { + key: e, + first: null, + c() { + (o = S("label")), + (r = S("input")), + (i = O()), + (s = j(v)), + (c = O()), + (a = S("button")), + (a.textContent = "?"), + (u = O()), + P(r, "type", "checkbox"), + P(r, "class", "svelte-185rejv"), + P(a, "class", "svelte-185rejv"), + P(o, "class", "svelte-185rejv"), + (this.first = o); + }, + m(t, e) { + _(t, o, e), + b(o, r), + (r.checked = n[74].active), + b(o, i), + b(o, s), + b(o, c), + b(o, a), + b(o, u), + (h = !0), + m || + ((g = [ + N(r, "change", y), + N(r, "click", n[17]()), + N(a, "click", x), + ]), + (m = !0)); + }, + p(t, e) { + (n = t), + 1 & e[0] && (r.checked = n[74].active), + (!h || 1 & e[0]) && + v !== + (v = + n[74].path.slice( + n[74].path.lastIndexOf("/") + 1, + n[74].path.length + ) + "") && + q(s, v); + }, + r() { + p = o.getBoundingClientRect(); + }, + f() { + L(o), $(), J(o, p); + }, + a() { + $(), ($ = F(o, p, Nt, {})); + }, + i(t) { + h || + (Z(() => { + d && d.end(1), (f = mt(o, n[11], { key: n[74].path })), f.start(); + }), + (h = !0)); + }, + o(t) { + f && f.invalidate(), (d = gt(o, n[10], { key: n[74].path })), (h = !1); + }, + d(t) { + t && w(o), t && d && d.end(), (m = !1), l(g); + }, + }; + } + function ce(t) { + let e, + n, + o, + r, + l, + i, + s = t[68] + "", + c = [], + a = new Map(), + u = t[0].modules[t[68]].filter(be); + const f = (t) => t[74].path; + for (let e = 0; e < u.length; e += 1) { + let n = Vt(t, u, e), + o = f(n); + a.set(o, (c[e] = se(o, n))); + } + return { + c() { + (e = S("b")), (n = j(s)), (o = S("br")), (r = O()); + for (let t = 0; t < c.length; t += 1) c[t].c(); + l = C(); + }, + m(t, s) { + _(t, e, s), b(e, n), _(t, o, s), _(t, r, s); + for (let e = 0; e < c.length; e += 1) c[e].m(t, s); + _(t, l, s), (i = !0); + }, + p(t, e) { + if ( + ((!i || 1 & e[0]) && s !== (s = t[68] + "") && q(n, s), 135169 & e[0]) + ) { + (u = t[0].modules[t[68]].filter(be)), ut(); + for (let t = 0; t < c.length; t += 1) c[t].r(); + c = $t(c, e, f, 1, t, u, a, l.parentNode, vt, se, l, Vt); + for (let t = 0; t < c.length; t += 1) c[t].a(); + ft(); + } + }, + i(t) { + if (!i) { + for (let t = 0; t < u.length; t += 1) dt(c[t]); + i = !0; + } + }, + o(t) { + for (let t = 0; t < c.length; t += 1) pt(c[t]); + i = !1; + }, + d(t) { + t && w(e), t && w(o), t && w(r); + for (let e = 0; e < c.length; e += 1) c[e].d(t); + t && w(l); + }, + }; + } + function ae(t) { + let e, n; + return ( + (e = new Gt({ + props: { $$slots: { default: [ge] }, $$scope: { ctx: t } }, + })), + e.$on("close", t[42]), + { + c() { + yt(e.$$.fragment); + }, + m(t, o) { + xt(e, t, o), (n = !0); + }, + p(t, n) { + const o = {}; + (60 & n[0]) | (4194304 & n[2]) && (o.$$scope = { dirty: n, ctx: t }), + e.$set(o); + }, + i(t) { + n || (dt(e.$$.fragment, t), (n = !0)); + }, + o(t) { + pt(e.$$.fragment, t), (n = !1); + }, + d(t) { + _t(e, t); + }, + } + ); + } + function ue(t) { + let e, + n, + o, + r, + l, + i = t[68] + "", + s = t[69] + ""; + return { + c() { + (e = S("p")), + (n = S("b")), + (o = j(i)), + (r = j(" - ")), + (l = j(s)), + I(e, "margin-left", "20px"); + }, + m(t, i) { + _(t, e, i), b(e, n), b(n, o), b(e, r), b(e, l); + }, + p(t, e) { + 32 & e[0] && i !== (i = t[68] + "") && q(o, i), + 32 & e[0] && s !== (s = t[69] + "") && q(l, s); + }, + d(t) { + t && w(e); + }, + }; + } + function fe(t) { + let e, + n, + o, + r, + l = t[4].funcInfo, + i = []; + for (let e = 0; e < l.length; e += 1) i[e] = he(At(t, l, e)); + return { + c() { + (e = j("Использование в сценариях: ")), (n = S("br")), (o = O()); + for (let t = 0; t < i.length; t += 1) i[t].c(); + r = C(); + }, + m(t, l) { + _(t, e, l), _(t, n, l), _(t, o, l); + for (let e = 0; e < i.length; e += 1) i[e].m(t, l); + _(t, r, l); + }, + p(t, e) { + if (16 & e[0]) { + let n; + for (l = t[4].funcInfo, n = 0; n < l.length; n += 1) { + const o = At(t, l, n); + i[n] + ? i[n].p(o, e) + : ((i[n] = he(o)), i[n].c(), i[n].m(r.parentNode, r)); + } + for (; n < i.length; n += 1) i[n].d(1); + i.length = l.length; + } + }, + d(t) { + t && w(e), t && w(n), t && w(o), k(i, t), t && w(r); + }, + }; + } + function de(t) { + let e, n, o; + return { + c() { + (e = S("br")), + (n = O()), + (o = S("lable")), + (o.textContent = "параметры:"), + I(o, "margin-left", "5px"); + }, + m(t, r) { + _(t, e, r), _(t, n, r), _(t, o, r); + }, + d(t) { + t && w(e), t && w(n), t && w(o); + }, + }; + } + function pe(t) { + let e, + n, + o, + r, + l, + i = t[65] + ""; + return { + c() { + (e = S("br")), + (n = O()), + (o = S("lable")), + (r = j("- ")), + (l = j(i)), + I(o, "margin-left", "40px"); + }, + m(t, i) { + _(t, e, i), _(t, n, i), _(t, o, i), b(o, r), b(o, l); + }, + p(t, e) { + 16 & e[0] && i !== (i = t[65] + "") && q(l, i); + }, + d(t) { + t && w(e), t && w(n), t && w(o); + }, + }; + } + function he(t) { + let e, + n, + o, + r, + l, + i, + s, + c, + a = (t[62].name ? t[62].name : "") + "", + u = (t[62].descr ? t[62].descr : "") + "", + f = JSON.stringify(t[62].params).length > 2, + d = f && de(), + p = t[62].params, + h = []; + for (let e = 0; e < p.length; e += 1) h[e] = pe(Ft(t, p, e)); + return { + c() { + (e = S("p")), + (n = S("b")), + (o = j(a)), + (r = j(" - ")), + (l = j(u)), + (i = O()), + d && d.c(), + (s = O()); + for (let t = 0; t < h.length; t += 1) h[t].c(); + (c = O()), I(e, "margin-left", "20px"); + }, + m(t, a) { + _(t, e, a), + b(e, n), + b(n, o), + b(e, r), + b(e, l), + b(e, i), + d && d.m(e, null), + b(e, s); + for (let t = 0; t < h.length; t += 1) h[t].m(e, null); + b(e, c); + }, + p(t, n) { + if ( + (16 & n[0] && + a !== (a = (t[62].name ? t[62].name : "") + "") && + q(o, a), + 16 & n[0] && + u !== (u = (t[62].descr ? t[62].descr : "") + "") && + q(l, u), + 16 & n[0] && (f = JSON.stringify(t[62].params).length > 2), + f ? d || ((d = de()), d.c(), d.m(e, s)) : d && (d.d(1), (d = null)), + 16 & n[0]) + ) { + let o; + for (p = t[62].params, o = 0; o < p.length; o += 1) { + const r = Ft(t, p, o); + h[o] ? h[o].p(r, n) : ((h[o] = pe(r)), h[o].c(), h[o].m(e, c)); + } + for (; o < h.length; o += 1) h[o].d(1); + h.length = p.length; + } + }, + d(t) { + t && w(e), d && d.d(), k(h, t); + }, + }; + } + function me(t) { + let e, n, o, r; + return { + c() { + (e = S("a")), + (n = j( + "Описание модуля в WIKI. Примеры подключения, использования." + )), + (r = S("br")), + P(e, "href", (o = t[4].exampleURL)); + }, + m(t, o) { + _(t, e, o), b(e, n), _(t, r, o); + }, + p(t, n) { + 16 & n[0] && o !== (o = t[4].exampleURL) && P(e, "href", o); + }, + d(t) { + t && w(e), t && w(r); + }, + }; + } + function ge(t) { + let e, + n, + o, + r, + l, + i, + s, + c, + a, + u, + f, + d, + p, + h, + m, + g, + v, + $, + y, + x, + C, + N, + M, + T, + E, + R, + G, + B, + U, + D, + A, + F, + L, + J, + V, + z, + K, + H, + Q, + W, + X, + Y, + Z, + tt, + et, + nt, + ot, + rt, + lt, + it, + st, + ct, + at, + ut = (t[4].title ? t[4].title : "") + "", + ft = (t[4].moduleName ? t[4].moduleName : "") + "", + dt = (t[3].name ? t[3].name : "") + "", + pt = (t[4].moduleDesc ? t[4].moduleDesc : "") + "", + ht = t[2].menuSection ? "Тип: " : "", + mt = (t[2].menuSection ? t[2].menuSection : "") + "", + gt = t[4].moduleVersion ? "Версия: " : "", + vt = (t[4].moduleVersion ? t[4].moduleVersion : "") + "", + $t = t[4].propInfo ? "Параметры: " : "", + bt = (t[4].authorName, ""), + yt = t[4].authorName ? "Автор: " : "", + xt = (t[4].authorName ? t[4].authorName : "") + "", + _t = (t[4].authorContact ? t[4].authorContact : "") + "", + wt = t[4].authorGit ? "GitHub: " : "", + kt = (t[4].authorGit ? t[4].authorGit : "") + "", + St = t[4].specialThanks ? "Благодарности: " : "", + jt = (t[4].specialThanks ? t[4].specialThanks : "") + "", + Ot = Object.entries(t[5]), + Ct = []; + for (let e = 0; e < Ot.length; e += 1) Ct[e] = ue(Lt(t, Ot, e)); + let Nt = t[4].funcInfo && fe(t), + Pt = t[4].exampleURL && me(t); + return { + c() { + (e = S("h2")), + (n = j(ut)), + (o = O()), + (r = S("strong")), + (l = j(ft)), + (i = j("\n(")), + (s = j(dt)), + (c = j(")\n")), + (a = S("div")), + (u = j(pt)), + (f = S("br")), + (d = O()), + (p = S("div")), + (h = j(ht)), + (m = O()), + (g = j(mt)), + (v = O()), + ($ = j(gt)), + (y = O()), + (x = j(vt)), + (C = S("br")), + (N = S("br")), + (M = O()), + (T = j($t)), + (E = S("br")), + (R = O()); + for (let t = 0; t < Ct.length; t += 1) Ct[t].c(); + (G = O()), + Nt && Nt.c(), + (B = O()), + (U = j(bt)), + (D = S("br")), + (A = O()), + Pt && Pt.c(), + (F = O()), + (L = j(yt)), + (J = O()), + (V = j(xt)), + (z = S("br")), + (K = O()), + (H = S("a")), + (Q = j(_t)), + (X = S("br")), + (Y = O()), + (Z = j(wt)), + (tt = O()), + (et = S("a")), + (nt = j(kt)), + (rt = S("br")), + (lt = O()), + (it = j(St)), + (st = O()), + (ct = j(jt)), + (at = S("br")), + P(e, "class", "svelte-185rejv"), + I(a, "margin-left", "20px"), + P(H, "href", (W = t[4].authorContact ? t[4].authorContact : "")), + P(et, "href", (ot = t[4].authorGit ? t[4].authorGit : "")); + }, + m(t, w) { + _(t, e, w), + b(e, n), + _(t, o, w), + _(t, r, w), + b(r, l), + _(t, i, w), + _(t, s, w), + _(t, c, w), + _(t, a, w), + b(a, u), + _(t, f, w), + _(t, d, w), + _(t, p, w), + b(p, h), + b(p, m), + b(p, g), + _(t, v, w), + _(t, $, w), + _(t, y, w), + _(t, x, w), + _(t, C, w), + _(t, N, w), + _(t, M, w), + _(t, T, w), + _(t, E, w), + _(t, R, w); + for (let e = 0; e < Ct.length; e += 1) Ct[e].m(t, w); + _(t, G, w), + Nt && Nt.m(t, w), + _(t, B, w), + _(t, U, w), + _(t, D, w), + _(t, A, w), + Pt && Pt.m(t, w), + _(t, F, w), + _(t, L, w), + _(t, J, w), + _(t, V, w), + _(t, z, w), + _(t, K, w), + _(t, H, w), + b(H, Q), + _(t, X, w), + _(t, Y, w), + _(t, Z, w), + _(t, tt, w), + _(t, et, w), + b(et, nt), + _(t, rt, w), + _(t, lt, w), + _(t, it, w), + _(t, st, w), + _(t, ct, w), + _(t, at, w); + }, + p(t, e) { + if ( + (16 & e[0] && + ut !== (ut = (t[4].title ? t[4].title : "") + "") && + q(n, ut), + 16 & e[0] && + ft !== (ft = (t[4].moduleName ? t[4].moduleName : "") + "") && + q(l, ft), + 8 & e[0] && + dt !== (dt = (t[3].name ? t[3].name : "") + "") && + q(s, dt), + 16 & e[0] && + pt !== (pt = (t[4].moduleDesc ? t[4].moduleDesc : "") + "") && + q(u, pt), + 4 & e[0] && ht !== (ht = t[2].menuSection ? "Тип: " : "") && q(h, ht), + 4 & e[0] && + mt !== (mt = (t[2].menuSection ? t[2].menuSection : "") + "") && + q(g, mt), + 16 & e[0] && + gt !== (gt = t[4].moduleVersion ? "Версия: " : "") && + q($, gt), + 16 & e[0] && + vt !== (vt = (t[4].moduleVersion ? t[4].moduleVersion : "") + "") && + q(x, vt), + 16 & e[0] && + $t !== ($t = t[4].propInfo ? "Параметры: " : "") && + q(T, $t), + 32 & e[0]) + ) { + let n; + for (Ot = Object.entries(t[5]), n = 0; n < Ot.length; n += 1) { + const o = Lt(t, Ot, n); + Ct[n] + ? Ct[n].p(o, e) + : ((Ct[n] = ue(o)), Ct[n].c(), Ct[n].m(G.parentNode, G)); + } + for (; n < Ct.length; n += 1) Ct[n].d(1); + Ct.length = Ot.length; + } + t[4].funcInfo + ? Nt + ? Nt.p(t, e) + : ((Nt = fe(t)), Nt.c(), Nt.m(B.parentNode, B)) + : Nt && (Nt.d(1), (Nt = null)), + 16 & e[0] && bt !== (t[4].authorName, (bt = "")) && q(U, bt), + t[4].exampleURL + ? Pt + ? Pt.p(t, e) + : ((Pt = me(t)), Pt.c(), Pt.m(F.parentNode, F)) + : Pt && (Pt.d(1), (Pt = null)), + 16 & e[0] && + yt !== (yt = t[4].authorName ? "Автор: " : "") && + q(L, yt), + 16 & e[0] && + xt !== (xt = (t[4].authorName ? t[4].authorName : "") + "") && + q(V, xt), + 16 & e[0] && + _t !== (_t = (t[4].authorContact ? t[4].authorContact : "") + "") && + q(Q, _t), + 16 & e[0] && + W !== (W = t[4].authorContact ? t[4].authorContact : "") && + P(H, "href", W), + 16 & e[0] && + wt !== (wt = t[4].authorGit ? "GitHub: " : "") && + q(Z, wt), + 16 & e[0] && + kt !== (kt = (t[4].authorGit ? t[4].authorGit : "") + "") && + q(nt, kt), + 16 & e[0] && + ot !== (ot = t[4].authorGit ? t[4].authorGit : "") && + P(et, "href", ot), + 16 & e[0] && + St !== (St = t[4].specialThanks ? "Благодарности: " : "") && + q(it, St), + 16 & e[0] && + jt !== (jt = (t[4].specialThanks ? t[4].specialThanks : "") + "") && + q(ct, jt); + }, + d(t) { + t && w(e), + t && w(o), + t && w(r), + t && w(i), + t && w(s), + t && w(c), + t && w(a), + t && w(f), + t && w(d), + t && w(p), + t && w(v), + t && w($), + t && w(y), + t && w(x), + t && w(C), + t && w(N), + t && w(M), + t && w(T), + t && w(E), + t && w(R), + k(Ct, t), + t && w(G), + Nt && Nt.d(t), + t && w(B), + t && w(U), + t && w(D), + t && w(A), + Pt && Pt.d(t), + t && w(F), + t && w(L), + t && w(J), + t && w(V), + t && w(z), + t && w(K), + t && w(H), + t && w(X), + t && w(Y), + t && w(Z), + t && w(tt), + t && w(et), + t && w(rt), + t && w(lt), + t && w(it), + t && w(st), + t && w(ct), + t && w(at); + }, + }; + } + function ve(t) { + let e, n, o, r, l, i, s, c, a, u, f, d, p, h; + n = new Dt({ props: { $$slots: { default: [re] }, $$scope: { ctx: t } } }); + let m = Object.entries(t[0].modules), + g = []; + for (let e = 0; e < m.length; e += 1) g[e] = ie(zt(t, m, e)); + const v = (t) => + pt(g[t], 1, 1, () => { + g[t] = null; + }); + let $ = Object.entries(t[0].modules), + y = []; + for (let e = 0; e < $.length; e += 1) y[e] = ce(Jt(t, $, e)); + const x = (t) => + pt(y[t], 1, 1, () => { + y[t] = null; + }); + let j = t[6] && ae(t); + return { + c() { + (e = S("p")), + yt(n.$$.fragment), + (o = O()), + (r = S("div")), + (l = S("div")), + (i = S("h2")), + (i.textContent = "доступные модули"), + (s = O()); + for (let t = 0; t < g.length; t += 1) g[t].c(); + (c = O()), + (a = S("div")), + (u = S("h2")), + (u.textContent = "в прошивке"), + (f = O()); + for (let t = 0; t < y.length; t += 1) y[t].c(); + (d = O()), + j && j.c(), + (p = C()), + P(e, "align", "center"), + P(i, "class", "svelte-185rejv"), + P(l, "class", "left svelte-185rejv"), + P(u, "class", "svelte-185rejv"), + P(a, "class", "right svelte-185rejv"), + P(r, "class", "board svelte-185rejv"); + }, + m(t, m) { + _(t, e, m), + xt(n, e, null), + b(e, o), + _(t, r, m), + b(r, l), + b(l, i), + b(l, s); + for (let t = 0; t < g.length; t += 1) g[t].m(l, null); + b(r, c), b(r, a), b(a, u), b(a, f); + for (let t = 0; t < y.length; t += 1) y[t].m(a, null); + _(t, d, m), j && j.m(t, m), _(t, p, m), (h = !0); + }, + p(t, e) { + const o = {}; + if ( + ((899 & e[0]) | (4194304 & e[2]) && + (o.$$scope = { dirty: e, ctx: t }), + n.$set(o), + 135169 & e[0]) + ) { + let n; + for (m = Object.entries(t[0].modules), n = 0; n < m.length; n += 1) { + const o = zt(t, m, n); + g[n] + ? (g[n].p(o, e), dt(g[n], 1)) + : ((g[n] = ie(o)), g[n].c(), dt(g[n], 1), g[n].m(l, null)); + } + for (ut(), n = m.length; n < g.length; n += 1) v(n); + ft(); + } + if (135169 & e[0]) { + let n; + for ($ = Object.entries(t[0].modules), n = 0; n < $.length; n += 1) { + const o = Jt(t, $, n); + y[n] + ? (y[n].p(o, e), dt(y[n], 1)) + : ((y[n] = ce(o)), y[n].c(), dt(y[n], 1), y[n].m(a, null)); + } + for (ut(), n = $.length; n < y.length; n += 1) x(n); + ft(); + } + t[6] + ? j + ? (j.p(t, e), 64 & e[0] && dt(j, 1)) + : ((j = ae(t)), j.c(), dt(j, 1), j.m(p.parentNode, p)) + : j && + (ut(), + pt(j, 1, 1, () => { + j = null; + }), + ft()); + }, + i(t) { + if (!h) { + dt(n.$$.fragment, t); + for (let t = 0; t < m.length; t += 1) dt(g[t]); + for (let t = 0; t < $.length; t += 1) dt(y[t]); + dt(j), (h = !0); + } + }, + o(t) { + pt(n.$$.fragment, t), (g = g.filter(Boolean)); + for (let t = 0; t < g.length; t += 1) pt(g[t]); + y = y.filter(Boolean); + for (let t = 0; t < y.length; t += 1) pt(y[t]); + pt(j), (h = !1); + }, + d(t) { + t && w(e), + _t(n), + t && w(r), + k(g, t), + k(y, t), + t && w(d), + j && j.d(t), + t && w(p); + }, + }; + } + const $e = (t) => !t.active, + be = (t) => t.active; + function ye(t, e, n) { + let o = + "https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4dev/myProfile.json?1", + r = {}, + l = 0, + i = {}, + s = [], + c = [], + a = [], + u = [], + f = !1, + d = "dev", + p = {}; + async function h(t) { + try { + let e = await fetch(t, { mode: "cors", method: "GET" }); + e.ok ? n(0, (r = await e.json())) : console.log("error", e.statusText); + } catch (t) { + console.log(t); + } + } + (r = { + iotmSettings: {}, + projectProp: { platformio: {} }, + modules: { + "Виртуальные элементы": [], + Сенсоры: [], + executive_devices: [], + Экраны: [], + }, + }), + h(o), + (async function (t) { + try { + let e = await fetch(t, { mode: "cors", method: "GET" }); + e.ok ? (p = await e.json()) : console.log("error", e.statusText); + } catch (t) { + console.log(t); + } + })("https://iotmanager.org/firmwarebuilder/moduleSize.json?1"); + const [m, g] = Ct({ + fallback(t, e) { + const n = getComputedStyle(t), + o = "none" === n.transform ? "" : n.transform; + return { + duration: 600, + easing: Ot, + css: (t) => + `\n\t\t\t\t\ttransform: ${o} scale(${t});\n\t\t\t\t\topacity: ${t}\n\t\t\t\t`, + }; + }, + }); + function v(t) { + console.log(t.path), + (o = + "https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4stable/" + + JSON.parse(JSON.stringify(t.path).replace(/\\\\/g, "/")) + + "/modinfo.json"), + (i = []), + n(2, (s = [])), + n(3, (c = [])), + n(3, (c.name = "Данные загружаются..."), c), + n(4, (a = [])), + n(5, (u = [])), + (async function (t) { + i = []; + try { + let e = await fetch(t, { mode: "cors", method: "GET" }); + e.ok + ? ((i = await e.json()), + n(2, (s = i)), + n(3, (c = i.configItem[0])), + n(4, (a = i.about)), + n(5, (u = a.propInfo))) + : console.log("error", e.statusText); + } catch (t) { + console.log(t); + } + })(o), + n(6, (f = !0)); + } + let $ = !0; + function b(t) { + n(8, ($ = t)); + } + function y() { + (o = + "dev" == d + ? "https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4dev/myProfile.json?1" + : "https://raw.githubusercontent.com/IoTManagerProject/IoTManager/ver4stable/myProfile.json?1"), + h(o); + } + function x() { + n(1, (l = 0)), + n(0, (r.projectProp.platformio.default_envs = _.text), r), + k(); + } + let _ = [], + w = [ + { id: 1, text: "esp8266_4mb" }, + { id: 2, text: "esp32_4mb" }, + { id: 3, text: "esp8266_1mb" }, + { id: 4, text: "esp8266_1mb_ota" }, + ]; + function k() { + n(1, (l = 0)); + for (const [t, e] of Object.entries(r.modules)) + for (const [e, o] of Object.entries( + r.modules[t].filter((t) => t.active) + )) { + let t = o.path.slice(o.path.lastIndexOf("/") + 1, o.path.length); + p[t] && n(1, (l += Math.round(p[t][_.text]))); + } + "esp8266_4mb" == _.text && n(1, (l = Math.round(l / 6110))), + "esp32_4mb" == _.text && n(1, (l = Math.round(l / 3243))), + "esp8266_1mb" == _.text && n(1, (l = Math.round(l / 3237))), + "esp8266_1mb_ota" == _.text && n(1, (l = Math.round(l / 2601))); + } + function S() { + var t = document.createElement("a"); + (t.href = window.URL.createObjectURL( + new Blob([JSON.stringify(r)], { type: "text/plain" }) + )), + (t.download = "myProfile.json"), + console.log(JSON.stringify(r)); + } + return [ + r, + l, + s, + c, + a, + u, + f, + d, + $, + _, + m, + g, + v, + b, + y, + x, + w, + k, + S, + function (t) { + (l = t), n(1, l); + }, + function (t) { + (l = t), n(1, l); + }, + function (t) { + (l = t), n(1, l); + }, + function (t) { + (l = t), n(1, l); + }, + function () { + (d = E(this)), n(7, d); + }, + () => y(), + function () { + (_ = E(this)), n(9, _), n(16, w); + }, + () => x(), + () => b(!0), + () => b(!1), + function () { + (r.iotmSettings.routerssid = this.value), n(0, r); + }, + function () { + (r.iotmSettings.routerpass = this.value), n(0, r); + }, + function () { + (r.iotmSettings.mqttServer = this.value), n(0, r); + }, + function () { + (r.iotmSettings.mqttPort = this.value), n(0, r); + }, + function () { + (r.iotmSettings.mqttPrefix = this.value), n(0, r); + }, + function () { + (r.iotmSettings.mqttUser = this.value), n(0, r); + }, + function () { + (r.iotmSettings.mqttPass = this.value), n(0, r); + }, + function () { + (r = this.value), n(0, r); + }, + () => S(), + function (t, e) { + (t[e].active = this.checked), n(0, r); + }, + (t) => v(t), + function (t, e) { + (t[e].active = this.checked), n(0, r); + }, + (t) => v(t), + () => n(6, (f = !1)), + ]; + } + return new (class extends St { + constructor(t) { + super(), kt(this, t, ye, ve, s, {}, null, [-1, -1, -1]); + } + })({ target: document.body }); +})(); //# sourceMappingURL=bundle.js.map