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)
-
+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)[](https://iarduino.ru) [](https://wiki.iarduino.ru) [](https://lesson.iarduino.ru)[](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/)
+
+
+| Модель | Ссылка на магазин|
+|--|--|
+| DS1307  | https://iarduino.ru/shop/Expansion-payments/chasy-realnogo-vremeni-rtc-trema-modul.html|
+| DS3231  | 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>
+ +