diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 65469d13..00000000 --- a/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch -lib/libraies-master - diff --git a/data/.exclude.files b/data/.exclude.files deleted file mode 100644 index 955397fa..00000000 --- a/data/.exclude.files +++ /dev/null @@ -1,2 +0,0 @@ -/*.js.gz -/.exclude.files diff --git a/data/config.json b/data/config.json deleted file mode 100644 index 34c25c56..00000000 --- a/data/config.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "IoTmanager", - "chipID": "", - "apssid": "IoTmanager", - "appass": "", - "routerssid": "rise", - "routerpass": "hostel3333", - "timezone": 2, - "ntp": "pool.ntp.org", - "mqttServer": "91.204.228.124", - "mqttPort": 1883, - "mqttPrefix": "/iotTeam", - "mqttUser": "test", - "mqttPass": "test", - "scen": "1", - "pushingboxid": "v7C133E426B0C69E", - "weblogin": "admin", - "webpass": "admin", - "udponoff": "1", - "blink": "1", - "oneWirePin": "2" -} \ No newline at end of file diff --git a/data/css/build.css.gz b/data/css/build.css.gz deleted file mode 100644 index b62ceaf0..00000000 Binary files a/data/css/build.css.gz and /dev/null differ diff --git a/data/edit.htm b/data/edit.htm deleted file mode 100644 index 1ebb989d..00000000 --- a/data/edit.htm +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - FS Editor - - - - - - - -
-
-
-
- - - - - \ No newline at end of file diff --git a/data/favicon.ico b/data/favicon.ico deleted file mode 100644 index 50d908fa..00000000 Binary files a/data/favicon.ico and /dev/null differ diff --git a/data/icon.jpeg b/data/icon.jpeg deleted file mode 100644 index abdd18c0..00000000 Binary files a/data/icon.jpeg and /dev/null differ diff --git a/data/index.htm.gz b/data/index.htm.gz deleted file mode 100644 index 073ff4ab..00000000 Binary files a/data/index.htm.gz and /dev/null differ diff --git a/data/index.json b/data/index.json deleted file mode 100644 index 1b6413ad..00000000 --- a/data/index.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "configs": [ - "/config.live.json", - "/config.setup.json", - "/lang/lang.ru.json" - ], - "title": "Главная", - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "text", - "class": "alert alert-light", - "title": "
IoT Manager
" - }, - { - "type": "link", - "title": "Конфигурация устройства", - "action": "/?set.device", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Список других устройств в сети", - "action": "/?set.udp", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Конфигурация WIFI", - "action": "/?set.wifi", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Конфигурация MQTT", - "action": "/?set.mqtt", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Конфигурация push", - "action": "/?set.push", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Утилиты", - "action": "/?set.utilities", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Скачать приложение IoT Manager для android", - "action": "https://play.google.com/store/apps/details?id=ru.esp8266.iotmanager", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Скачать приложение IoT Manager для iphone", - "action": "https://apps.apple.com/ru/app/iot-manager/id1155934877", - "class": "btn btn-block btn-default" - } - ] -} diff --git a/data/items/analog-adc.txt b/data/items/analog-adc.txt deleted file mode 100644 index ad72b7f9..00000000 --- a/data/items/analog-adc.txt +++ /dev/null @@ -1 +0,0 @@ -analog-adc;id;anydata;Сенсоры;Аналоговый;order;pin-adc;map[1,1024,1,1024];c[1] \ No newline at end of file diff --git a/data/items/button-in.txt b/data/items/button-in.txt deleted file mode 100644 index ada34b20..00000000 --- a/data/items/button-in.txt +++ /dev/null @@ -1 +0,0 @@ -button-in;id;toggle;Кнопки;Освещение;order;pin;db[20] \ No newline at end of file diff --git a/data/items/button-out-i.txt b/data/items/button-out-i.txt deleted file mode 100644 index c24ec540..00000000 --- a/data/items/button-out-i.txt +++ /dev/null @@ -1 +0,0 @@ -button-out;id;toggle;Кнопки;Освещение;order;pin;inv[1];st[1] \ No newline at end of file diff --git a/data/items/button-out-np.txt b/data/items/button-out-np.txt deleted file mode 100644 index 5fa21dd1..00000000 --- a/data/items/button-out-np.txt +++ /dev/null @@ -1 +0,0 @@ -button-out;id;toggle;Кнопки;Освещение;order;st[0] \ No newline at end of file diff --git a/data/items/button-out-p.txt b/data/items/button-out-p.txt deleted file mode 100644 index d2a213fe..00000000 --- a/data/items/button-out-p.txt +++ /dev/null @@ -1 +0,0 @@ -button-out;id;toggle;Кнопки;Освещение;order;pin;st[0] \ No newline at end of file diff --git a/data/items/input-digit.txt b/data/items/input-digit.txt deleted file mode 100644 index 8c3fbbe0..00000000 --- a/data/items/input-digit.txt +++ /dev/null @@ -1 +0,0 @@ -input-digit;id;inputDigit;Ввод;Введите#цифру;order;st[60] \ No newline at end of file diff --git a/data/items/input-time.txt b/data/items/input-time.txt deleted file mode 100644 index 821e29c3..00000000 --- a/data/items/input-time.txt +++ /dev/null @@ -1 +0,0 @@ -input-time;id;inputTime;Ввод;Введите#время;order;st[10-00-00] \ No newline at end of file diff --git a/data/items/output-text.txt b/data/items/output-text.txt deleted file mode 100644 index fbe97423..00000000 --- a/data/items/output-text.txt +++ /dev/null @@ -1 +0,0 @@ -output-text;id;anydata;Вывод;Сигнализация;order;st[Обнаружено#движение] \ No newline at end of file diff --git a/data/items/pwm-out.txt b/data/items/pwm-out.txt deleted file mode 100644 index b3ecac6e..00000000 --- a/data/items/pwm-out.txt +++ /dev/null @@ -1 +0,0 @@ -pwm-out;id;range;Ползунки;Яркость;order;pin;st[500] \ No newline at end of file diff --git a/data/items/signs.json b/data/items/signs.json deleted file mode 100644 index 7d140d4b..00000000 --- a/data/items/signs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "more":">", - "less":"<", - "eq":"=" -} \ No newline at end of file diff --git a/data/js/build.chart.js.gz b/data/js/build.chart.js.gz deleted file mode 100644 index 654707fd..00000000 Binary files a/data/js/build.chart.js.gz and /dev/null differ diff --git a/data/js/function.js.gz b/data/js/function.js.gz deleted file mode 100644 index 10c9953a..00000000 Binary files a/data/js/function.js.gz and /dev/null differ diff --git a/data/lang/lang.ru.json b/data/lang/lang.ru.json deleted file mode 100644 index 2c089bcb..00000000 --- a/data/lang/lang.ru.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "SetDevConf": "Конфигурация устройства", - "SetDevPreset": "Выберите из списка подходящий пресет кофигурации", - - "ButSave":"Сохранить", - "ButMainPage":"Главная", - - - "SetUDPList": "Список других устройств в сети:", - "SetUDPWarn1": "После нажатия на кнопку переформировать список устройств ждите примерно минуту, а затем обновите страницу и список появится вновь", - - "SetUDPUpdateList":"Переформировать список устройств", - "SetUDPUpdatePage":"Обновить страницу", - "SetUDPNameOfDev":"Имя этого устройства:", - "SetUDPDateExchange":"Включить обмен данными между устройствами", - "SetUDPWarn2":"Если обмен данными включен, то устройства будут обмениваться широковещательными пакетами udp для формирования списка устройств и для осуществления посылки настроек mqtt. Данный обмен создает дополнительную нагрузку на wifi сеть.", - - "SetWiFiNameOfDev":"Имя устройства:", - "SetWiFiRouterConnect":"Подключение к WiFi роутеру:", - "SetWiFiAccessPoint":"Точка доступа:", - "SetWiFiWeb":"Логин и пароль web interface:", - "SetWiFiTimeZone":"Временная зона:", - "SetWiFiNTP":"Сервер NTP:", - "SetWiFiWarn1":"Имя устройства должно состоять из английских букв и иметь длинну от 6 до 12 символов", - "SetWiFiWarn2":"После того как вы введете логин пароль от вашего wifi роутера необходимо нажать кнопку сохранить, а затем обязательно нажать кнопку перезагрузить устройство внизу этой страницы", - "SetWiFiWarn3":"Устройство постоянно сканирует сеть на наличие wifi. Если роутер отключен, то устройство автоматически перейдет в режим точки доступа. Когда wifi появится устройство автоматически подключится к роутеру снова, и выключит точку доступа", - "SetWiFiWarn4":"После изменения поля NTP сервер необходимо перезагрузить устройство", - "SetWiFiWarn5":"Светодиод статуса подключения показывает четыре состояния подключения:
1. мигает редко - идет подключение к wifi
2. мигает часто - идет подключение к серверу mqtt
3. горит постоянно - модуль в режиме точки доступа,
4. не горит - модуль подключен к wifi и к mqtt.
Светодиод подключен к gpio2. Если галочка стоит - то использовать этот пин нельзя", - - "SetMQTTServerName":"Имя сервера:", - "SetMQTTPort":"Номер порта:", - "SetMQTTPrefix":"Префикс:", - "SetMQTTUserName":"Имя пользователя:", - "SetMQTTPassword":"Пароль:", - - "SetMQTTSendSettings":"Отправить настройки MQTT с этого устройства на все остальные", - "SetMQTTWarn1":"Обратите внимание что поле префикс может состоять только из одного слова и одного разделителя: /prefix, вариант вида: /prefix1/prefix2 работать не будет. После изменения поля prefix необходимо перезагрузить устройство", - "SetMQTTWarn2":"Прежде чем нажимать на кнопку Отправить настройки MQTT сохрание их, если Вы их меняли. Настройки получат и перезапишут все устройства в локальной сети" -} \ No newline at end of file diff --git a/data/s.conf.csv b/data/s.conf.csv deleted file mode 100644 index 2d2f8d1d..00000000 --- a/data/s.conf.csv +++ /dev/null @@ -1 +0,0 @@ -Тип элемента;Id;Виджет;Имя вкладки;Имя виджета;Позиция виджета \ No newline at end of file diff --git a/data/s.scen.txt b/data/s.scen.txt deleted file mode 100644 index ab0c0141..00000000 --- a/data/s.scen.txt +++ /dev/null @@ -1 +0,0 @@ -// \ No newline at end of file diff --git a/data/set.device.json b/data/set.device.json deleted file mode 100644 index c8ad7722..00000000 --- a/data/set.device.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/config.option.json", - "/config.live.json", - "/lang/lang.ru.json" - ], - "class": "col-sm-offset-1 col-sm-10", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h4", - "title": "Device ID: {{chipID}}" - }, - { - "type": "h4", - "title": "IP address: {{ip}}" - }, - { - "type": "h4", - "title": "Time: {{time}}" - }, - { - "type": "h4", - "title": "Uptime: {{uptime}}" - }, - { - "type": "h4", - "title": "Build version: {{firmware_version}}" - }, - { - "type": "h4", - "title": "LittleFS version: 2.4.0" - }, - { - "type": "hr" - }, - { - "type": "dropdown", - "name": "help-url", - "class": "btn btn-default", - "style": "display:inline", - "title": { - "#": "Выберите элемент из списка", - "/set?addItem=button-out-p": "1.Кнопка управляющая пином", - "/set?addItem=button-out-np": "2.Кнопка не привязанная к пину", - "/set?addItem=pwm-out": "3.Широтно импульсная модуляция(pwm)", - "/set?addItem=button-in": "4.Физическая кнопка", - "/set?addItem=input-digit": "5.Окно ввода цифровых значений", - "/set?addItem=input-time": "6.Окно ввода времени", - "/set?addItem=output-text": "7.Окно вывода любого текста, предупреждения, цифры", - "/set?addItem=analog-adc": "8.Аналоговый сенсор" - } - }, - { - "type": "hr" - }, - { - "type": "csv", - "title": [ - "html", - "text", - "text", - "text", - "text", - "text" - ], - "state": "s.conf.csv", - "style": "width:100%;", - "action": "/set?saveItems", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "link", - "title": "Удалить все", - "action": "/set?delAllItems", - "class": "btn btn-block btn-default" - }, - { - "type": "h2", - "title": "Сценарии" - }, - { - "type": "checkbox", - "name": "scen", - "title": "Включить сценарии", - "action": "/set?scen=[[scen]]", - "state": "{{scen}}" - }, - { - "type": "file", - "state": "s.scen.txt", - "style": "width:100%;height:350px", - "title": "Сохранить", - "action": "/set?sceninit", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Инструкция к системе автоматизации", - "action": "https://github.com/IoTManagerProject/IoTManager/wiki", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Очистить логи сенсоров", - "action": "/set?cleanlog", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "name": "my-block", - "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", - "class": "hidden" - }, - { - "type": "button", - "title": "Обновить прошивку устройства", - "action": "/check", - "response": "[[my-block]]", - "class": "btn btn-block btn-default" - } - ] -} \ No newline at end of file diff --git a/data/set.deviceold.json b/data/set.deviceold.json deleted file mode 100644 index 340d21b7..00000000 --- a/data/set.deviceold.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/config.option.json", - "/config.live.json", - "/lang/lang.ru.json" - ], - "class": "col-sm-offset-1 col-sm-10", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h4", - "title": "Device ID: {{chipID}}" - }, - { - "type": "h4", - "title": "IP address: {{ip}}" - }, - { - "type": "h4", - "title": "Time: {{time}}" - }, - { - "type": "h4", - "title": "Uptime: {{uptime}}" - }, - { - "type": "h4", - "title": "Build version: {{firmware_version}}" - }, - { - "type": "h4", - "title": "LittleFS version: 2.3.5" - }, - { - "type": "hr" - }, - { - "type": "dropdown", - "name": "help-url", - "class": "btn btn-default", - "style": "display:inline", - "title": { - "#": "{{SetDevPreset}}", - "/set?preset=001": "1.Вкл. выкл. локального реле", - "/set?preset=002": "2.Вкл. выкл. локального реле в определенное время", - "/set?preset=003": "3.Вкл. выкл. локального реле на определенный период времени", - "/set?preset=004": "4.Вкл. выкл. нескольких локальных реле кнопкой в приложении", - "/set?preset=005": "5.Вкл. выкл. локального реле физической кнопкой и кнопкой в приложении параллельно (для выключателя света)", - "/set?preset=006": "6.Вкл. выкл. нескольких удаленных реле кнопкой в приложении (нужно указать Device ID)", - "/set?preset=007": "7.Вкл. выкл. нескольких удаленных реле физической кнопкой (нужно указать Device ID)", - "/set?preset=008": "8.Широтно импульсная модуляция", - "/set?preset=009": "9.Сенсор DHT11 (темп, влажность) и логгирование", - "/set?preset=010": "10.Сенсор DHT22, DHT33, DHT44, AM2302, RHT03 (темп, влажность) и логгирование", - "/set?preset=011": "11.Аналоговый сенсор и логгирование", - "/set?preset=012": "12.Cенсор bmp280 (темп, давление) и логгирование", - "/set?preset=013": "13.Cенсор bme280 (темп, давление, влажность, высота) и логгирование", - "/set?preset=014": "14.Сенсор DS18B20 (темп) и логгирование", - "/set?preset=015": "15.Термостат на DS18B20 с переключением в ручной режим и логгированием", - "/set?preset=016": "16.Котроль уровня в баке (датчик расстояния) на сенсорах: JSN-SR04T, HC-SR04, HY-SRF05 и логгирование", - "/set?preset=017": "17.Датчик движения включающий свет", - "/set?preset=018": "18.Охранный датчик движения", - "/set?preset=019": "19.Система управления шаговыми двигателями на основе драйвера A4988 (открытие закрытие штор)", - "/set?preset=020": "20.Система управления сервоприводами", - "/set?preset=021": "21.Модуль uart (serial). Двухстороняя связь с устройством через uart. Получение данных и отправка команд", - "/set?preset=100": "22.Настройки по умолчанию" - } - }, - { - "type": "h2", - "title": "{{SetDevConf}}" - }, - { - "type": "file", - "state": "dev_conf.txt", - "style": "width:100%;height:350px", - "title": "Сохранить", - "action": "/set?devinit", - "class": "btn btn-block btn-default" - }, - - { - "type": "h2", - "title": "Сценарии" - }, - { - "type": "checkbox", - "name": "scen", - "title": "Включить сценарии", - "action": "/set?scen=[[scen]]", - "state": "{{scen}}" - }, - { - "type": "file", - "state": "dev_scen.txt", - "style": "width:100%;height:350px", - "title": "Сохранить", - "action": "/set?sceninit", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Инструкция к системе автоматизации", - "action": "https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/wiki/Instruction", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "Очистить логи сенсоров", - "action": "/set?cleanlog", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "name": "my-block", - "style": "position:fixed;top:50%;left:50%;width:400px;margin-left:-200px;text-align:center;", - "class": "hidden" - }, - { - "type": "button", - "title": "Обновить прошивку устройства", - "action": "/check", - "response": "[[my-block]]", - "class": "btn btn-block btn-default" - } - ] -} \ No newline at end of file diff --git a/data/set.mqtt.json b/data/set.mqtt.json deleted file mode 100644 index a088c2dc..00000000 --- a/data/set.mqtt.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/lang/lang.ru.json" - ], - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - - { - "type": "h4", - "title": "{{SetMQTTServerName}}", - "style": "width:60%;float:left;" - }, - { - "type": "input", - "title": "", - "name": "mqttServer-arg", - "state": "{{mqttServer}}", - "style": "width:40%;float:right" - }, - { - "type": "h4", - "title": "{{SetMQTTPort}}", - "style": "width:60%;float:left;" - }, - { - "type": "input", - "title": "", - "name": "mqttPort-arg", - "state": "{{mqttPort}}", - "style": "width:40%;float:right" - }, - { - "type": "h4", - "title": "{{SetMQTTPrefix}}", - "style": "width:60%;float:left;" - }, - { - "type": "input", - "title": "", - "name": "mqttPrefix-arg", - "state": "{{mqttPrefix}}", - "style": "width:40%;float:right" - }, - { - "type": "h4", - "title": "{{SetMQTTUserName}}", - "style": "width:60%;float:left;" - }, - { - "type": "input", - "title": "", - "name": "mqttUser-arg", - "state": "{{mqttUser}}", - "style": "width:40%;float:right" - }, - { - "type": "h4", - "title": "{{SetMQTTPassword}}", - "style": "width:60%;float:left;" - }, - { - "type": "input", - "title": "", - "name": "mqttPass-arg", - "state": "{{mqttPass}}", - "style": "width:40%;float:right" - }, - { - "type": "h3", - "name": "my-block", - "style": "position:fixed;top:30%;left:50%;width:400px;margin-left:-200px;text-align:center;", - "class": "hidden" - }, - - { - "type": "button", - "title": "{{ButSave}}", - "style": "width:100%;float:left;", - "action": "set?mqttServer=[[mqttServer-arg]]&mqttPort=[[mqttPort-arg]]&mqttPrefix=[[mqttPrefix-arg]]&mqttUser=[[mqttUser-arg]]&mqttPass=[[mqttPass-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "button", - "style": "width:100%;float:left;", - "title": "{{SetMQTTSendSettings}}", - "action": "set?mqttsend", - "class": "btn btn-block btn-default" - }, - - { - "type": "button", - "style": "width:100%;float:left;", - "title": "Проверить соединение с MQTT", - "action": "set?mqttcheck", - "response": "[[my-block]]", - "class": "btn btn-block btn-default" - }, - { - "type": "text", - "style": "width:100%;float:left;", - "title": "

{{SetMQTTWarn1}}

" - - }, - { - "type": "text", - "style": "width:100%;float:left;", - "title": "

{{SetMQTTWarn2}}

" - }, - { - "type": "link", - "style": "width:100%;float:left;", - "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/restart?device=ok');}", - "class": "btn btn-block btn-danger" - } - ] -} \ No newline at end of file diff --git a/data/set.push.json b/data/set.push.json deleted file mode 100644 index 555797e4..00000000 --- a/data/set.push.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/lang/lang.ru.json" - ], - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h4", - "style": "width:60%;float:left;", - "title": "Device id:" - }, - { - "type": "input", - "title": "", - "name": "push-arg", - "style": "width:40%;float:right", - "state": "{{pushingboxid}}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "set?pushingboxid=[[push-arg]]", - "class": "btn btn-block btn-default", - "style": "width:100%;display:inline" - }, - { - "type": "hr" - }, - { - "type": "link", - "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/restart?device=ok');}", - "class": "btn btn-block btn-danger" - } - ] -} \ No newline at end of file diff --git a/data/set.udp.json b/data/set.udp.json deleted file mode 100644 index 6bea3042..00000000 --- a/data/set.udp.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/lang/lang.ru.json" - ], - "title": "Главная", - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetUDPList}}" - }, - { - "type": "hr" - }, - { - "type": "csv", - "title": [ - "html", - "html", - "html" - ], - "state": "dev.csv", - "style": "width:100%;", - "class": "nan" - }, - { - "type": "hr" - }, - { - "type": "link", - "title": "{{SetUDPUpdateList}}", - "action": "/set?updatelist", - "class": "btn btn-block btn-default" - }, - { - "type": "link", - "title": "{{SetUDPUpdatePage}}", - "action": "/set?updatepage", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "text", - "title": "

{{SetUDPWarn1}}

", - "style": "width:100%;float:left;" - }, - { - "type": "h3", - "title": "{{SetUDPNameOfDev}}" - }, - { - "type": "input", - "title": "{{SetUDPNameOfDev}}", - "name": "devname-arg", - "state": "{{name}}", - "pattern": "[A-Za-z0-9]{6,12}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "/set?devname=[[devname-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "checkbox", - "name": "udponoff", - "title": "{{SetUDPDateExchange}}", - "action": "/set?udponoff=[[udponoff]]", - "state": "{{udponoff}}" - }, - { - "type": "text", - "title": "

{{SetUDPWarn2}}

" - } - ] -} \ No newline at end of file diff --git a/data/set.utilities.json b/data/set.utilities.json deleted file mode 100644 index 9314cd8b..00000000 --- a/data/set.utilities.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "configs": [ - "/config.live.json", - "/config.setup.json", - "/lang/lang.ru.json" - ], - "title": "Главная", - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "Сканирование шины i2c" - }, - { - "type": "h4", - "title": "{{i2c}}" - }, - { - "type": "link", - "title": "Сканировать", - "action": "/set?i2c", - "class": "btn btn-block btn-default" - } - ] -} \ No newline at end of file diff --git a/data/set.wifi.json b/data/set.wifi.json deleted file mode 100644 index d68edc7f..00000000 --- a/data/set.wifi.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "configs": [ - "/config.setup.json", - "/lang/lang.ru.json" - ], - "title": "Конфигурация", - "class": "col-sm-offset-1 col-sm-10 col-md-offset-2 col-md-8 col-lg-offset-3 col-lg-6", - "content": [ - { - "type": "h5", - "title": "{{name}}", - "class": "alert-default" - }, - { - "type": "link", - "title": "{{ButMainPage}}", - "action": "/", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetWiFiNameOfDev}}" - }, - { - "type": "input", - "title": "{{SetWiFiNameOfDev}}", - "name": "devname-arg", - "state": "{{name}}", - "pattern": "[A-Za-z0-9]{6,12}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "set?devname=[[devname-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "text", - "title": "

{{SetWiFiWarn1}}

" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetWiFiRouterConnect}}" - }, - { - "type": "input", - "title": "", - "name": "routerssid-arg", - "state": "{{routerssid}}" - }, - { - "type": "password", - "title": "", - "name": "routerpass-arg", - "state": "{{routerpass}}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "class": "btn btn-block btn-default", - "action": "set?routerssid=[[routerssid-arg]]&routerpass=[[routerpass-arg]]" - }, - { - "type": "text", - "title": "

{{SetWiFiWarn2}}

" - }, - { - "type": "hr" - }, - { - "type": "checkbox", - "name": "blink", - "title": "Включить светодиод статуса подключения", - "action": "/set?blink=[[blink]]", - "state": "{{blink}}" - }, - { - "type": "text", - "title": "

{{SetWiFiWarn5}}

" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetWiFiAccessPoint}}" - }, - { - "type": "input", - "title": "", - "name": "apssid-arg", - "state": "{{apssid}}", - "pattern": ".{1,20}" - }, - { - "type": "password", - "title": "", - "name": "appass-arg", - "state": "{{appass}}", - "pattern": ".{8,20}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "set?apssid=[[apssid-arg]]&appass=[[appass-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "text", - "title": "

{{SetWiFiWarn3}}

" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetWiFiWeb}}" - }, - { - "type": "input", - "title": "Логин", - "name": "weblogin-arg", - "state": "{{weblogin}}", - "pattern": ".{1,20}" - }, - { - "type": "password", - "title": "Пароль", - "name": "webpass-arg", - "state": "{{webpass}}", - "pattern": ".{1,20}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "set?weblogin=[[weblogin-arg]]&webpass=[[webpass-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "hr" - }, - { - "type": "h3", - "title": "{{SetWiFiTimeZone}}" - }, - { - "type": "input", - "title": "", - "name": "timezone-arg", - "state": "{{timezone}}", - "pattern": ".{1,20}" - }, - { - "type": "input", - "title": "", - "name": "ntp-arg", - "state": "{{ntp}}" - }, - { - "type": "button", - "title": "{{ButSave}}", - "action": "set?timezone=[[timezone-arg]]&ntp=[[ntp-arg]]", - "class": "btn btn-block btn-default" - }, - { - "type": "text", - "title": "

{{SetWiFiWarn4}}

" - }, - { - "type": "hr" - }, - { - "type": "link", - "title": "Перезагрузить устройство", - "action": "javascript:if(confirm(renameBlock(jsonResponse,'Перезагрузить?'))){send_request(this,'/set?device=ok');}", - "class": "btn btn-block btn-danger" - } - ] -} \ No newline at end of file diff --git a/data/widgets/alarm.json b/data/widgets/alarm.json deleted file mode 100644 index e0e636c3..00000000 --- a/data/widgets/alarm.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "widget": "anydata", - "icon": "body", - "color": "red", - "descrColor": "red" -} \ No newline at end of file diff --git a/data/widgets/anydata.json b/data/widgets/anydata.json deleted file mode 100644 index 4157b9c9..00000000 --- a/data/widgets/anydata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "widget": "anydata", - "after": "", - "icon": "" -} \ No newline at end of file diff --git a/data/widgets/chart.json b/data/widgets/chart.json deleted file mode 100644 index 47c6473b..00000000 --- a/data/widgets/chart.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "widget": "chart", - "series": "Temperature, °C", - "dateFormat": "HH:mm" -} \ No newline at end of file diff --git a/data/widgets/fillgauge.json b/data/widgets/fillgauge.json deleted file mode 100644 index 34315ea8..00000000 --- a/data/widgets/fillgauge.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "widget": "fillgauge", - "circleColor": "#00FFFF", - "textColor": "#FFFFFF", - "waveTextColor": "#000000", - "waveColor": "#00FFFF" -} \ No newline at end of file diff --git a/data/widgets/inputDate.json b/data/widgets/inputDate.json deleted file mode 100644 index ecc3113d..00000000 --- a/data/widgets/inputDate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "widget" : "input", - "size" : "small", - "color" : "orange", - "type" : "date" -} \ No newline at end of file diff --git a/data/widgets/inputDigit.json b/data/widgets/inputDigit.json deleted file mode 100644 index 20196c6a..00000000 --- a/data/widgets/inputDigit.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "widget" : "input", - "color" : "blue", - "type" : "number" -} \ No newline at end of file diff --git a/data/widgets/inputText.json b/data/widgets/inputText.json deleted file mode 100644 index 3484d53b..00000000 --- a/data/widgets/inputText.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "widget" : "input", - "size" : "small", - "color" : "orange", - "type" : "text" -} \ No newline at end of file diff --git a/data/widgets/inputTime.json b/data/widgets/inputTime.json deleted file mode 100644 index e39942a4..00000000 --- a/data/widgets/inputTime.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "widget" : "input", - "color" : "blue", - "type" : "time" -} \ No newline at end of file diff --git a/data/widgets/progress-line.json b/data/widgets/progress-line.json deleted file mode 100644 index b2a95529..00000000 --- a/data/widgets/progress-line.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "widget": "progress-line", - "icon": "sunny", - "descrColor": "", - "color": "", - "max": "100", - "background": "", - "stroke": "10", - "disabled": "", - "before": "", - "after": "" -} \ No newline at end of file diff --git a/data/widgets/progress-round.json b/data/widgets/progress-round.json deleted file mode 100644 index bf3ddae5..00000000 --- a/data/widgets/progress-round.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "widget": "progress-round", - "descrColor": "", - "max": "100", - "stroke": "20", - "color": "#45ccce", - "background": "#777", - "before": "", - "semicircle": "1", - "after": "" -} \ No newline at end of file diff --git a/data/widgets/range.json b/data/widgets/range.json deleted file mode 100644 index 4d1e0ff3..00000000 --- a/data/widgets/range.json +++ /dev/null @@ -1,9 +0,0 @@ - { - "widget" : "range", - "descrColor": "red", - "after" : "%", - "k" : 0.0977, - "min" : 0, - "max" : 100, - "debounce": 500 -} \ No newline at end of file diff --git a/data/widgets/select.json b/data/widgets/select.json deleted file mode 100644 index c6531b70..00000000 --- a/data/widgets/select.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "widget" : "select", - "size" : "small", - "fill" : "outline", - "options" : "["Zero item", "First item", "Second item"]", - "status" : 2 -} \ No newline at end of file diff --git a/data/widgets/toggle.json b/data/widgets/toggle.json deleted file mode 100644 index 4e9eed5e..00000000 --- a/data/widgets/toggle.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "widget": "toggle", - "icon": "", - "iconOff": "" -} \ No newline at end of file diff --git a/doc/1.txt b/doc/1.txt deleted file mode 100644 index d08be432..00000000 --- a/doc/1.txt +++ /dev/null @@ -1,566 +0,0 @@ -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -## Возможности - - - Объединение различных по типу и назначению устройств: управление, получение данных, и настройка параметров - всё в одном приложении - - - Взаимодействие с устройствами осуществляется через "облачный" сервис с использованием протокола mqtt, позволит контролировать их из любой точки Мира (при наличии доступа в Интернет) - - - Поддержка нескольких профилей и их переключение "на лету", дает возможность объединить устройства в группы - - -Настройка (после "прошивки") производится через веб-интерфейс, чтобы получить к нему доступ необходимо соединиться с WiFi AP устройства и набрать в адресной строке браузера http://192.168.4.1. -Далее выбрать типовой шаблон автоматизации, произвести настройку под свои требования и задачи. -Основные разделы интерфейса: конфигурация и сценарии. -В окне конфигурации задаются "объекты", "элементы управления" устройства (dashboard) - им устройство будет представлено в приложении компаньоне проекта. В окне сценариев задаются реакции на события и изменения в параметрах работы системы. - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -## Команды, назначение и применение - -Команды служат для настройки и управления устройством и его взаимодействия - -**`buttonSet 1 1`** Изменит состояние "кнопки" №1, установит его в значение 1 - -**`pinSet 13 0`** Установит GPIO 13 состояние 0 - -**`pinChange 13`** Состояние GPIO 13 будет изменено на противоположное - -**`pwmSet 1 500`** Настройка pwm №1 будет использовано значение 500 - -**`timeSet 1 08-00-00`** Установит для элемента ввода времени - inputTime значение 08:00:00 - -**`digitSet 1 56`** Элемент №1 (для цифровых параметров) отобразит число 56 - -**`stepperSet 1 100 1`** Шаговый двигатель №1 - вращение 100 "шагов" по часовой стрелке (для движения в обратную сторону используются отрицательные значения параметра) - -**`servoSet 1 180`** Сервопривод №1 принять положение 180° - -**`timerStart 1 60 sec`** Установить для таймера №1 обратный отсчёт в 60 секунд - -**`timerStop 1`** Остановить таймер №1 - -**`textSet 1 Привет`** Установить для элемента текстовое поле №1 - "привет" - -**`push Внимание Протечка`** Отправить push-уведомление с темой "внимание" и содержанием "протечка" - -**`firmwareUpdate`** Обновить прошивку устройства "по воздуху" - -**`firmwareVersion Версия прошивки Системные 1`** Узнать версию прошивки устройстве - -## Сценарии - -Элементарный блок в сценарии состоит из набора команд и триггера - условия для их выполнения - -**temp > 60** -digitSet 1 60 -stepperSet 1 100 1 -textSet 1 Перегрев -**end** - -Условие: когда температура превысит 60° -Запустит: команда шаговому двигателю, в приложение отправить сообщение и цифровое значение температуры. - -В сценарии может быть несколько блоков, при необходимости из приложения есть возможность "выключать" часть из них. -Неактивные блоки сценария будут проигнорированы. - -Для взаимодействия устройств между собой предусмотрены команды mqtt и http - -**temp > 60** -mqtt 154348-134 digitSet_1_56 -mqtt 154348-136 stepperSet _1_100_1 -http 192.168.1.10 textSet_1_Перегрев -**end** - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 1.1 Объект "кнопка" - -(эти строки мы пишем в "конфигурации устройства") - -### a) кнопка управляющая выходом (пином). Пины нумеруются по системе нумирации gpio для esp контроллеров. - -`button 1 13 кухня освещение 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"13"** это номер пина которым будет управлять данная кнопка -**"кухня"** это название кнопки в приложении -**"освещение"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - - -### б) виртуальная кнопка - кнопка реакцию на которую можно задать в сценариях: - -`button 1 na запустить таймеры 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"na"** абривиатура not available означающая что эта кнопка виртуальная и что пин не установлен -**"запустить"** это название кнопки в приложении -**"таймеры"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -### в) кнопка включающая и выключающая все сценарии: - -`button 1 scenario запустить таймеры 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"scenario"** слово означающее что эта кнопка для управления сценариями -**"запустить"** это название кнопки в приложении -**"таймеры"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - - -### г) кнопка включающая выключающая определенные блоки сценариев: - -`button 1 line1,line3, Включить#отправку#push Оповещение 0 1` - -**"button"** это объект создающий кнопку в приложении -**"1"** это номер этой кнопки (необходимый для ее аутентификации) -**"line1,line3,"** это блоки сценариев нумирация сверху вниз. Блоком считается выражение от начала до слова end -**"Включить#отправку#push"** это название кнопки в приложении -**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка -**"0"** это начальное состояние кнопки при старте модуля (выкд 0, вкл 1) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -## 1.2 Команды управления объектом "кнопка" - -(эти строки мы пишем в "сценариях") - - ### а) Команда включения выключения кнопки по ее номеру - -`buttonSet 1 1` - -**"buttonSet"** команда управления объектом button -**"1"** номер кнопки которой будем управлять -**"1"** состояние включено, 0 - выключено - -### б) Команда изменения состояния кнопки на противоположное - -`buttonChange 1` - -**"buttonChange"** команда управления объектом button -**"1"** номер кнопки которой будем управлять - -## 1.3 Вызов событий объектом "кнопока" - -(эти строки мы пишем в "сценариях") - -объект button может быть равен либо 0 либо 1 - -`button1 = 1` -`button2 = 0` - -Пример использования: - -`button1 = 1` -`buttonSet 2 1` -`buttonSet 3 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 2.1 Объект "физическая кнопка" - -`switch 1 0 10` - -**switch** это объект создающий физическую кнопку -**1** номер кнопки -**0** пин кнопки (при подключении необходим подтягивающий резистор) -**10** задержка для избавления от дребезга с мили секундах - -## 2.2 Вызов событий объектом "физическая кнопка" - -`switch1` может быть равна нулю или единицы, ноль - событие отбрасывания кнопки, единица - событие нажатия - -`switch1 = 1` -`buttonChange 1` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 3.1 Объект "широтноимпульсная модуляция" - -`pwm 1 12 яркость освещение 1023 1` - -**"pwm"** это объект создающий управление шим в приложении в виде ползунка -**"1"** это номер этого объекта -**"12"** это номер пина на котором будет генерироваться шим заданной в приложении величены -**"Яркость"** это название кнопки в приложении -**"Оповещение"** это название вкладки в приложении на которой появится данная кнопка -**"1023"** это начальное значение шим сигнала и ползунка (изменяется от 0 до 1023) -**"1"** это уникальный номер и номер сортировки данной кнопки. Этот номер должен быть уникален для каждого объекта - -## 3.2 Команда управления объектом "широтноимпульсная модуляция" - -`pwmSet 1 500` - -**"pwmSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять -**"500"** значение которое установится после выполнения команды (от 0 до 1023) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 4.1 Объект "окно ввода времени" - -`inputTime time1 Во#сколько#включить? Таймеры 20-30-00 1` - -**inputTime** это объект создающий окно ввода в приложении -**time1** переменная в которую будет записано время введенное в окно -**Во#сколько#включить?** это название окна в приложении -**Таймеры** это название вкладки в приложении -**20-30-00** начальное значение времени после загрузки устройства -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 4.2 Управление объектом "окно ввода времени" - -`timeSet 1 08-00-00` - -**"timeSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `time1` -**"08-00-00"** время которое хотим установить - -В окно ввода можно вводить время в приложении но если необходимо изменить время автоматически -по какому нибудь событию то можно использовать команду выше - **timeSet**. - -## 4.3 Вызов событий объектом "окно ввода времени" - -`timenow = time1` -`buttonSet 1 1` -`end` - -`timenow` всегда хранит в себе текущее время, и поэтому исходя из данного сценария кнопка номер 1 включится в то время которое будет введено в окно ввода `time1` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 5.1 Объект "окно ввода цифры" - -`inputDigit digit1 Через#сколько#секунд#выключить? Таймеры 5 2` - -**inputDigit** это объект создающий окно ввода в приложении -**digit1** переменная в которую будет записана цифра, введенная в окно -**Через#сколько#секунд#выключить?** это название окна в приложении -**Таймеры** это название вкладки в приложении -**5** цифра по умолчанию, после загрузки модуля -**2** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 5.2 Управление объектом "окно ввода цифры": - -`digitSet 1 56` - -**"digitSet"** команда управления объектом -**"1"** номер объекта, которым будем управлять в данном случае окном ввода с `digit1` -**"56"** цифра которую хотим установить - -В окно ввода можно вводить цифры в приложении, но если необходимо изменить цифру автоматически -по какому нибудь событию, то можно использовать команду выше - **digitSet**. - -## 5.3 Вызов событий объектом "окно ввода цифры" - -`dallas > digit1` -`buttonSet 1 0` -`end` - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 6.1 Объект "dallas" (сенсор температуры ds18b20) - -`dallas temp1 2 123456 Водонагреватель,#t°C Термостат any-data 1` - -**dallas** это объект чтения датчика температуры -**2** пин датчика температуры -**Водонагреватель,#t°C** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 6.2 Вызов событий объектом "dallas" - -В сценариях dallas можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`dallas > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`dallas > 60` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 7.1 Объект "analog" (аналоговый вход контроллера) - -`analog adc 0 Аналоговый#вход,#% Датчики progress-round 310 620 1 100 1` - -**analog** это объект чтения аналогового входа -**adc** это переменная -**0** пин аналогового входа (для esp8266 всегда 0, для esp32 пока что не доделал читаться будет всегда пин 34) -**Аналоговый#вход,#%** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта виджета отображения -**310** начальная величина читаемого диапазона -**620** конечная величина читаемого диапазона -**1** начальная величина выводимого диапазона -**100** конечная величина выводимого диапазона -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 7.2 Вызов событий объектом "analog" - -В сценариях analog можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`analog > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`analog > 50` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 8.1 Объект "level" (ультразвуковой дальномер JSN-SR04T, HC-SR04, HY-SRF05) - -`level Вода#в#баке,#% Датчики any-data 125 20 1` - -**level** это объект чтения датчика расстояния -**Вода#в#баке** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета -**125** расстояние от датчика до дна бака в сантиметрах -**20** расстояние от датчика до поверхности воды, когда бак полный, в сантиметрах -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -Подключать дальномер нужно: - -| | trig | echo | -| :-: | :-: | :-: | -| wemos | D5 | D6 | -| esp | 14 | 12 | - -## 8.2 Вызов событий объектом "level" - -В сценариях level можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`level > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`level > 95` -`buttonSet 1 0` -`end` - - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 9.1 Объект "dht" (Сенсоры DHT11, DHT22, DHT33, DHT44, AM2302, RHT03) - -dhtT DHT11 2 Температура#DHT,#t°C Датчики any-data 1 -dhtH DHT11 2 Влажность#DHT,#% Датчики any-data 2 -dhtComfort Степень#комфорта: Датчики 3 -dhtPerception Восприятие: Датчики 4 -dhtDewpoint Точка#росы: Датчики 5 - -**dhtT** или **dhtH** температура или влажность -**DHT11** или **DHT22** чтение DHT11 или DHT22, DHT33, DHT44, AM2302, RHT03 соответственно -**2** пин датчика -**Температура#DHT,#t°C** это название виджета в приложении -**Датчики** название вкладки в приложении -**any-data** или **progress-round** или **progress-line** три разных варианта отображения виджета -**1** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -## 9.2 Вызов событий объектом "dhtT" или "dhtH" - -В сценариях "dhtT" или "dhtH" можно сравнивать с переменной окна ввода `digit1` (>,<,>=,<=,=): - -`dhtT > digit1` -`buttonSet 1 0` -`end` - -`dhtH > digit1` -`buttonSet 1 0` -`end` - -Или можно сравнивать с постоянной цифрой (>,<,>=,<=,=): - -`dhtT > 50` -`buttonSet 1 0` -`end` - -`dhtH < 40` -`buttonSet 1 0` -`end` - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 10.1 Объект "stepper" (Драйвер шагового двигателя A4988) - -stepper 1 12 4 -stepper 2 13 5 - -**stepper** объект создающий шаговый двигатель -**1** номер шаговика -**12** номер пина количества шагов -**4** номер пина направления - -## 10.2 управление объектом "stepper" - -`stepperSet 1 200 1` - -**stepperSet** команда управления шаговым двигателем -**1** номер шагового двигателя (их может быть не более двух) -**200** количество шагов (обратное направление отрицательное значение параметра) -**1** интервал между шагами (мс) - -`button1 = 1` -`stepperSet 1 200 1` -`end` -`button1 = 0` -`stepperSet 1 -200 1` -`end` - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 11.1 Объект "обратный таймер" - -Прежде чем читать этот раздел запустите пресет №3 на устройстве. -Нажав на кнопку "Выберите во что вы хотите превратить esp" - -Можно использовать цифры из окон ввода: - -`timerStart 1 digit1 sec` - -Можно писать цифры прям в объект: - -`timerStart 1 10 sec` - -Можно установить часы минуты или секунды: - -`timerStart 1 10 sec` -`timerStart 1 10 min` -`timerStart 1 10 hours` - -Используем это объект в сценариях вот так: - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` - -Смысл в том что при нажатии на кнопку один запуститься обратный отчет, на величину digit1 секунд. Если напишем например: - -`dallas < 60` -`timerStart 1 digit1 sec` -`end` - -то такой же отчет запустится когда значение температуры вырастит больше 60 градусов. Таким образом обратный отчет можно запустить реакцией на любое событие. Итак теперь обратный отчет запущен, обратный таймер уменьшается, и нам надо назначить действие на тот момент когда он обнулится. Для этого я придумал выражение: `timer1 = 0` - -Используем его и в общем получаем вот такой сценарий: - -`button1 = 1` -`timerStart 1 digit1 sec` -`end` -`timer1 = 0` -`buttonSet 1 0` -`end` - -Когда таймер закончит отсчёт, кнопка станет "неактивной". Используйте преет №3, как пример подобного сценария -Например: - -`dallas < 60` -`buttonSet 1 0` -`buttonSet 2 0` -`pwmSet 1 1023` -`mqtt 2653450020 buttonChange_1` -`mqtt 2653450020 pinSet_13_1` -`http 192.168.1.32 pinSet_14_1` -`end` - -Вот что может произойти на разных устройствах по одному событию повышения температуры... - - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 12 Журнал (лог) данных - - -`logging analog 1 100 slow Аналоговый#вход Датчики 7` - -**logging** объект для логирования -**analog** или **dhtT** или **dhtH** какой сенсор будем логировать, можно указать любой -**1** период между точками в минутах -**100** количество точек (старые точки будут удаляться по мере добавления новых) -**slow** или **fast** метод выгрузки графика в приложение, slow - выгружает график по одной точке (меньше расходуется оперативка, лучше использовать для esp8266), fast - выгрузка графика сразу (больше расход оперативки, подходит для esp32) -**Аналоговый#вход** название графика в приложении -**Датчики** название вкладки в приложении -**7** это уникальный номер и номер сортировки. Этот номер должен быть уникален для каждого объекта - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) -## 13 Взаимодействие устройств между собой - -Устройства могут между собой обмениваться командами. Команды можно отправлять по http или по mqtt. -По событию на одном устройстве можно вызвать действие на другом. Например на esp01 стоит датчик температуры, реле стоит на esp02. - -Настройки esp01: - -`dhtT temp 2 dht11 Температура#DHT,#t°C Датчики any-data 1` - -`temp < 40` -`http 192.168.10.25 buttonSet_1_1` -`end` - -Настройки esp02: - -`button 1 13 Включить#реле Реле 0 1` - -И теперь когда температура датчика на esp01 станет меньше 40 градусов то на esp02 будет отправлена команда на включение кнопки: buttonSet_1_1 - -Если вы хотите отправить команду через mqtt то сценарий будет выглядеть следующим образом: - -`temp < 40` -`mqtt 12343442-12413131 buttonSet_1_1` -`end` - -где `12343442-12413131` id esp02 той на которую отправляем команду. Id можно взять в веб интерфейсе на странице конфигурация устройства. Или в списке устройств в сети. - -Теперь рассмотрим вариант внешнего управления esp с помощью get запросов. - -`http://192.168.88.239/cmd?command=buttonSet%201%201` - -Разберем эту строку. Сама команда в ней выглядит вот так: buttonSet%201%201. `%20` заменяют пробел. - -То есть что бы составить get запрос на изменение например pwm нужно: - -Взять команду `pwmSet 1 500` -Заменить в ней пробелы на `%20` получится так: `pwmSet%201%20500` -И добавить ее в конец строки `http://192.168.88.239/cmd?command=` где указывается ip адрес устройства - -В итоге получится http://192.168.88.239/cmd?command=pwmSet%201%20500 - - - - - - \ No newline at end of file diff --git a/doc/2.txt b/doc/2.txt deleted file mode 100644 index 529f22bb..00000000 --- a/doc/2.txt +++ /dev/null @@ -1,85 +0,0 @@ -# В этой инструкции будет описано как с esp отправлять email и push - -# Часть 1. Привязать email и pushbullet к сайту pushingbox - -### 1. Необходимо перейти на сайт: [pushingbox](https://www.pushingbox.com/) -### 2. Войти с помощью google -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_1.png) -### 3. Перейти в мои сервисы и добавить новый сервис -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_2.png) -### 4. Нас интересуют два сервиса email и pushbullet -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_4%2B.png) -### 5. Выбираем сначало сервис для отправки email. В окно `Name of your email configuration` - вводим слово "email". В окно `Email address` - вводим ваш email адрес. жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_5.png) -manager_modules_firmware/blob/master/push_instruction/Screenshot_6.png) -### 6.1 Привязываем pushbullet. Переходим на сайт [pushbullet.com](https://www.pushbullet.com/) -### 6.2 Входим с гуглом или фейсбуком -### 6.3 Идем в настройки -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_7.png) -### 6.4 Создаем токен -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_8.png) -### 6.5 Идем опять в сервисы и теперь выбираем сервис pushbullet [pushingbox.com/services](https://www.pushingbox.com/services.php) нажимаем add service -### Берем токен, и вставляем его в окно Access token. -### Окно Device token (optional) оставляем пустым. -### В окно Name of your Pushbullet configuration пишем слово "push". -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_6.png) - -### 7. Теперь наш email и pushbullet привязаны к pushingbox. Далее можно скачать приложение pushbullet на телефон и войти с гуглом или фейсбуком сответственно с пунктом 6.3 этой инструкции -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_9.png) - -# Часть 2. Создание сценариев отправки email - -### 8.1. Сценарий для отправки email. Заходим в My Scenarios: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_10.png) - -### 8.2 Пишем слово email (это имя сценария отправки email) жмем add: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_12.png) -### 8.3 Нажимаем add an action -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_14.png) -### 8.4 Выбираем наш email который мы зарегестрировали ранее и нажимаем Add an action with this service -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_11.png) -### 8.5 Делаем все как на скриншоте и жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_15.png) -### 8.6 Возвращаемся на мои сценарии -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_17.png) -### 8.7 Вставляем токен в веб интерфейс esp -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_18.png) - -# Часть 3. Создание сценариев отправки push - -### 9.1. Сценарий для отправки push. Заходим в My Scenarios: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_10.png) - -### 9.2 Пишем слово push (это имя сценария отправки email) жмем add: -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_19.png) -### 9.3 Нажимаем add an action -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_20.png) -### 9.4 Выбираем наш pushbullet который мы зарегестрировали ранее и нажимаем Add an action with this service -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_11.png) -### 9.5 Делаем все как на скриншоте и жмем submit -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_15.png) -### 9.6 Возвращаемся на мои сценарии -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_21.png) -### 9.7 Вставляем токен в веб интерфейс esp -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_18.png) - -# Часть 4. Итог - -При создании такой конфигурации как на картинке: - -`button 1 na Отправить#push Push 0 1` - - -`button1 = 1` -`push внимание кнопка#нажата` -`end` - -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_22.png) - -Если мы введем токен для email то будут приходить email -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_17.png) - -Если для push то будут приходить push в pushbullet -![](https://github.com/IoTManagerProject/Wiki/tree/master/pictures/push_instruction/Screenshot_21.png) - -Способ описанный в данной инструкции более сложный в настройке но зато очень надежный. \ No newline at end of file diff --git a/doc/3.txt b/doc/3.txt deleted file mode 100644 index 31226847..00000000 --- a/doc/3.txt +++ /dev/null @@ -1,32 +0,0 @@ -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 1. Скачать архив из [релизов](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/releases) или из закрепленного сообщения группы телеграм с последней версией прошивки - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - - -### 2. Для ESP8266 c 4 и больше мб памяти (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_2.png) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 2. Для ESP8266 c 1 мб памяти (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1mb_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp8266_1mb_2.png) - -*** -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/1.png?raw=true) - -### 2. Для ESP32 (все сделать как на скриншотах) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp32_1.png) - -![](https://github.com/DmitryBorisenko33/esp32-esp8266_iot-manager_modules_firmware/blob/master/pictures/esp32_2.png) \ No newline at end of file diff --git a/doc/conf/c001.txt b/doc/conf/c001.txt deleted file mode 100644 index 733f4c95..00000000 --- a/doc/conf/c001.txt +++ /dev/null @@ -1,3 +0,0 @@ -button 1 5 Включить#реле Реле 0 1 - -//это простая кнопка номер 1 управляющая пином 5 имеющая начальное состояние 0 \ No newline at end of file diff --git a/doc/conf/c002.txt b/doc/conf/c002.txt deleted file mode 100644 index 1e54f5dd..00000000 --- a/doc/conf/c002.txt +++ /dev/null @@ -1,6 +0,0 @@ -inputTime time1 Во#сколько#включить? Таймеры 20-30-00 1 -inputTime time2 Во#сколько#выключить? Таймеры 20-35-00 2 -button 1 5 Кнопка#(по#таймеру) Таймеры 0 3 - -//время в приложение необходимо вводить в строгом формате: ЧЧ-ММ-СС -//можно создавать любое количество таймеров, копируя строку inputTime... \ No newline at end of file diff --git a/doc/conf/c003.txt b/doc/conf/c003.txt deleted file mode 100644 index 9f0c5c45..00000000 --- a/doc/conf/c003.txt +++ /dev/null @@ -1,4 +0,0 @@ -button 1 5 Вкл#на#время Таймеры 0 1 -inputDigit digit1 Через#сколько#секунд#выключить? Таймеры 5 2 - -//в сценариях можно поменять на sec, min или hours если нужны другие размерности времени \ No newline at end of file diff --git a/doc/conf/c004.txt b/doc/conf/c004.txt deleted file mode 100644 index b0978b0d..00000000 --- a/doc/conf/c004.txt +++ /dev/null @@ -1,3 +0,0 @@ -button 1 na Включить#все Освещение 0 1 - -//при нажатии на эту кнопку пины номер 5 и 13 поведут себя как установленно в сценариях \ No newline at end of file diff --git a/doc/conf/c005.txt b/doc/conf/c005.txt deleted file mode 100644 index 5ef7e5f5..00000000 --- a/doc/conf/c005.txt +++ /dev/null @@ -1,4 +0,0 @@ -button 1 13 Включить#реле Реле 0 1 -switch 1 0 10 - -//можно управлять реле на пине 13 кнопкой на пине 0 или кнопкой в приложении \ No newline at end of file diff --git a/doc/conf/c006.txt b/doc/conf/c006.txt deleted file mode 100644 index db812455..00000000 --- a/doc/conf/c006.txt +++ /dev/null @@ -1,6 +0,0 @@ -button 1 5 Включить#все Реле 0 1 - - -//что бы использовать эту конфигурацию на другой esp необходимо активировать пресет -//"Вкл. выкл. локального реле", затем в сценарии данного модуля подставить Device ID -//того esp, кнопка на этом девайсе будет выключать другие устройства по воздуху \ No newline at end of file diff --git a/doc/conf/c007.txt b/doc/conf/c007.txt deleted file mode 100644 index 739a1424..00000000 --- a/doc/conf/c007.txt +++ /dev/null @@ -1,6 +0,0 @@ -switch 1 0 10 - -//что бы использовать эту конфигурацию на другой esp необходимо активировать пресет -//"Вкл. выкл. локального реле", затем в сценарии данного модуля подставить Device ID -//того esp, к данному модулю нужно подключить кнопку к пину 0 и тогда -//один девайс будет управлять другим по воздуху \ No newline at end of file diff --git a/doc/conf/c008.txt b/doc/conf/c008.txt deleted file mode 100644 index 09eef72c..00000000 --- a/doc/conf/c008.txt +++ /dev/null @@ -1,6 +0,0 @@ -pwm 1 3 Яркость#коредор: Реле 1023 1 -pwm 2 4 Яркость#ванная: Реле 510 2 - -//в приложении появятся ползунки, соответствующее значение pwm -//будет установленно на пинах 3 и 4 -//1023 и 510 это начальные значения после загрузки модуля \ No newline at end of file diff --git a/doc/conf/c009.txt b/doc/conf/c009.txt deleted file mode 100644 index 496b4761..00000000 --- a/doc/conf/c009.txt +++ /dev/null @@ -1,7 +0,0 @@ -dhtT t 2 dht11 Температура#DHT,#t°C Датчики anydata 1 -dhtH h 2 dht11 Влажность#DHT,#t°C Датчики anydata 2 -dhtComfort Степень#комфорта: Датчики 3 -dhtPerception Восприятие: Датчики 4 -dhtDewpoint Точка#росы: Датчики 5 -logging t 1 50 Температура Датчики 6 -logging h 1 50 Влажность Датчики 7 \ No newline at end of file diff --git a/doc/conf/c010.txt b/doc/conf/c010.txt deleted file mode 100644 index 0972be00..00000000 --- a/doc/conf/c010.txt +++ /dev/null @@ -1,7 +0,0 @@ -dhtT t 2 dht22 Температура#DHT,#t°C Датчики anydata 1 -dhtH h 2 dht22 Влажность#DHT,#t°C Датчики anydata 2 -dhtComfort Степень#комфорта: Датчики 3 -dhtPerception Восприятие: Датчики 4 -dhtDewpoint Точка#росы: Датчики 5 -logging t 1 50 Температура Датчики 6 -logging h 1 50 Влажность Датчики 7 \ No newline at end of file diff --git a/doc/conf/c011.txt b/doc/conf/c011.txt deleted file mode 100644 index acf8f5e3..00000000 --- a/doc/conf/c011.txt +++ /dev/null @@ -1,8 +0,0 @@ -analog adc 0 Аналоговый#вход,#% Датчики progress-round 310 620 1 100 1 -logging adc 5 100 Аналоговый#вход Датчики 2 - -//если датчик углекислого газа выдает напряжение от 1 вольта до 2 вольт, то значит -//значение чтения аналогового входа будут примерно равным -//при 1 вольте - 310, а при 2 вольтах - 620 (считаем по пропорции) -//данная строка переведет диапазон 310-620 в диапазон 1-100 и отобразит в приложении -//варианты отображения: anydata, progress-round, progress-line, fillgauge diff --git a/doc/conf/c012.txt b/doc/conf/c012.txt deleted file mode 100644 index 3793a877..00000000 --- a/doc/conf/c012.txt +++ /dev/null @@ -1,6 +0,0 @@ -bmp280T temp1 0x76 Температура#bmp280 Датчики anydata 1 -bmp280P press1 0x76 Давление#bmp280 Датчики anydata 2 -logging temp1 1 100 Температура Датчики 3 -logging press1 1 100 Давление Датчики 4 - -//Чтение и логгирование датчика bmp280. Датчик подключается к шине i2c (esp8266 - gpio 5, 4) \ No newline at end of file diff --git a/doc/conf/c013.txt b/doc/conf/c013.txt deleted file mode 100644 index 78630cfe..00000000 --- a/doc/conf/c013.txt +++ /dev/null @@ -1,9 +0,0 @@ -bme280T temp1 0x76 Температура#bmp280 Датчики anydata 1 -bme280P pres1 0x76 Давление#bmp280 Датчики anydata 2 -bme280H hum1 0x76 Влажность#bmp280 Датчики anydata 3 -bme280A altit1 0x76 Высота#bmp280 Датчики anydata 4 -logging temp1 1 100 Температура Датчики 5 -logging press1 1 100 Давление Датчики 6 -logging hum1 1 100 Влажность Датчики 7 - -//Чтение и логгирование датчика bme280. Датчик подключается к шине i2c (esp8266 - gpio 5, 4) \ No newline at end of file diff --git a/doc/conf/c014.txt b/doc/conf/c014.txt deleted file mode 100644 index c850b3e2..00000000 --- a/doc/conf/c014.txt +++ /dev/null @@ -1,7 +0,0 @@ -dallas temp1 2 1 Температура Датчики anydata 1 -dallas temp2 2 2 Температура Датчики anydata 2 -logging temp1 1 100 Температура Датчики 3 -logging temp2 1 100 Температура Датчики 4 - -//2 - номер пина датчика -//варианты отображения: anydata, progress-round, progress-line, fillgauge \ No newline at end of file diff --git a/doc/conf/c015.txt b/doc/conf/c015.txt deleted file mode 100644 index 6567cf6d..00000000 --- a/doc/conf/c015.txt +++ /dev/null @@ -1,12 +0,0 @@ -dallas 2 Водонагреватель,#t°C Термостат anydata 1 -logging dallas 5 100 Температура Термостат 2 -inputDigit digit1 При#скольки#выключить? Термостат 40 3 -inputDigit digit2 При#скольки#включить? Термостат 20 4 -button 1 5 Нагреватель Термостат 0 5 -button 2 line1,line2, Автоматический#режим Термостат 1 6 - -//2 - номер пина датчика -//5 - номер пина реле -//это термостат который будет держать температуру между двумя -//установленными в приложении значениями, так же можно выключить -//автоматический режим, и тогда нагреватель будет управляться в ручную \ No newline at end of file diff --git a/doc/conf/c016.txt b/doc/conf/c016.txt deleted file mode 100644 index 0b82f2b9..00000000 --- a/doc/conf/c016.txt +++ /dev/null @@ -1,12 +0,0 @@ -levelPr p 14 12 Уровень#в#баке,#% Датчики fillgauge 125 25 1 -ultrasonicCm cm 14 12 Дистанция,#см Датчики anydata 2 -inputDigit digit1 При#скольки#выключить? Датчики 95 3 -inputDigit digit2 При#скольки#включить? Датчики 10 4 -button 1 5 Насос Датчики 0 5 -logging p 1 100 Вода#в#баке Датчики 6 - -//125 - это расстояние от датчика до дна бака в сантиметрах -//25 - это расстояние от датчика до поверхности воды когда бак полный в сантиметрах -//distancePr - эта строка выводит процент заполнения бака -//distanceCm - эта строка выводит расстояние в сантиметрах -//варианты отображения: anydata, progress-round, progress-line, fillgauge \ No newline at end of file diff --git a/doc/conf/c017.txt b/doc/conf/c017.txt deleted file mode 100644 index fa0b78ac..00000000 --- a/doc/conf/c017.txt +++ /dev/null @@ -1,11 +0,0 @@ -button 1 5 Прихожая Освещение 0 1 -inputDigit digit1 Задержка#выключения Освещение 30 2 -switch 1 0 10 - -//0 - номер пина датчика движения -//5 - номер пина реле -//при срабатывании датчика движения включится реле и обратный таймер на 30 сек -//если движение не будет обнаружено повтороно в течении 30 секунд - свет выключится -//если движение повторится в течении 30 секунд то таймер продлится опять на 30 сек -//свет выключится только в том случае если в комнате все замрет на 30 сек -//задержку выключения можно будет настраивать в приложении \ No newline at end of file diff --git a/doc/conf/c018.txt b/doc/conf/c018.txt deleted file mode 100644 index bc7d8192..00000000 --- a/doc/conf/c018.txt +++ /dev/null @@ -1,12 +0,0 @@ -switch 1 0 20 -text 1 Вход: Охрана 1 -textSet 1 не#обнаружено-time -button 1 na Сбросить Охрана 0 2 -button 2 line3, Включить#push Охрана 1 3 - -//0 - номер пина датчика -//при срабатывании датчика движения устройство пошлет пуш и в приложении будет -//написано в текстовом поле, что движение было обнаружено -//так же будет зафиксирован момент времени срабатывания датчика -//в приложении можно отключать отправку пуш сообщений на тот случай если дома хозяин -//перевести датчик снова в режим ожидания движения можно нажав кнопку сброса в приложении \ No newline at end of file diff --git a/doc/conf/c019.txt b/doc/conf/c019.txt deleted file mode 100644 index 82b41ae2..00000000 --- a/doc/conf/c019.txt +++ /dev/null @@ -1,17 +0,0 @@ -stepper 1 12 4 -stepper 2 13 5 -button 1 na Открыть#штору#1 Шторы 0 1 -button 2 na Открыть#штору#2 Шторы 0 2 - -//для подключения необходим драйвер шагового двигателя A4988 - -//stepper 1 12 4 шаговый двигатель с параметрами: 1 - номер шагового двигателя, -//12 - номер пина количества шагов, 4 - номер пина направления - -//stepper 2 13 5 шаговый двигатель с параметрами: 2 - номер шагового двигателя, -//13 - номер пина количества шагов, 5 - номер пина направления - -//stepperSet 1 200 5 - прокрутить шаговик номер 1 на 200 шагов по часовой стрелке -//с задержкой между шагами 5 милисекунд (чем меньше задержка тем больше скорость) -//если поставить -200 то будет вращаться против часовой стрелки -//можно подключить не более двух шаговиков \ No newline at end of file diff --git a/doc/conf/c020.txt b/doc/conf/c020.txt deleted file mode 100644 index 5d45aea0..00000000 --- a/doc/conf/c020.txt +++ /dev/null @@ -1,17 +0,0 @@ -servo 1 12 50 Мой#сервопривод Сервоприводы 0 100 0 180 1 -servo 2 13 50 Мой#сервопривод Сервоприводы 0 100 0 180 2 -button 1 na Открыть1 Сервоприводы 0 3 -button 2 na Открыть2 Сервоприводы 0 4 - -//Можно создавать не более двух сервоприводов на одном устройстве. -//1 - номер привода -//12 - номер пина -//50 - начальное значение в процентах - -//0 - 100 диапазон ползунка -//0 - 180 диапазон угла - -//Представим ситуацию когда есть некая заслонка и при угле в 30 градусов она закрыта, -//а при угле в 90 градусов открыта. В этом случае необходимо написать -//0 100 30 90 и тогда поставив ползунок в 0 % серва встанет в положение 30 градусов, -//а если поставить ползунок в 100 % серва встанет в положение 90 градусов. \ No newline at end of file diff --git a/doc/conf/c021.txt b/doc/conf/c021.txt deleted file mode 100644 index 615742f3..00000000 --- a/doc/conf/c021.txt +++ /dev/null @@ -1,7 +0,0 @@ -serialBegin 9600 12 13 -button 1 na Управляется#из#arduino Serial 0 1 -button 2 na Отправить#в#arduino Serial 0 2 -text 1 Текст#из#arduino Serial 3 - -//12 13 это пины uart к которым вы можете подключить arduino. Скетч для arduino выложен в группу. -//команда serialWrite ard-on отправит в arduino текст ard-on \ No newline at end of file diff --git a/doc/conf/c100.txt b/doc/conf/c100.txt deleted file mode 100644 index c943b43b..00000000 --- a/doc/conf/c100.txt +++ /dev/null @@ -1,12 +0,0 @@ -button 1 na Включить#все Реле 0 1 -button 2 13 Прихожая Реле 0 2 -button 3 14 Кухня Реле 0 3 -pwm 1 3 Яркость#коредор: Реле 1023 4 -pwm 2 4 Яркость#ванная: Реле 510 5 -analog adc 0 Аналоговый#вход Датчики fillgauge 1 1023 1 1023 6 -logging adc 1 100 Аналоговый#вход Датчики 7 - -//Это демо конфигурация. В ней показано как связать кнопки c помощью сценариев -//Кнопка номер 1 связана с кнопкой 2, 3 и с pwm 2 -//Так же продемонстрированна система логгирования данных строкой logging -//1 - это интервал между точками в минутах, 100 это количество точек \ No newline at end of file diff --git a/doc/conf/s001.txt b/doc/conf/s001.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s001.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s002.txt b/doc/conf/s002.txt deleted file mode 100644 index 5deba589..00000000 --- a/doc/conf/s002.txt +++ /dev/null @@ -1,6 +0,0 @@ -timenow = time1 -buttonSet 1 1 -end -timenow = time2 -buttonSet 1 0 -end diff --git a/doc/conf/s003.txt b/doc/conf/s003.txt deleted file mode 100644 index f8ced990..00000000 --- a/doc/conf/s003.txt +++ /dev/null @@ -1,6 +0,0 @@ -button1 = 1 -timerStart 1 digit1 sec -end -timer1 = 0 -buttonSet 1 0 -end \ No newline at end of file diff --git a/doc/conf/s004.txt b/doc/conf/s004.txt deleted file mode 100644 index 276282c3..00000000 --- a/doc/conf/s004.txt +++ /dev/null @@ -1,8 +0,0 @@ -button1 = 1 -pinSet 5 1 -pinSet 13 0 -end -button1 = 0 -pinSet 5 0 -pinSet 13 1 -end \ No newline at end of file diff --git a/doc/conf/s005.txt b/doc/conf/s005.txt deleted file mode 100644 index 093de1fe..00000000 --- a/doc/conf/s005.txt +++ /dev/null @@ -1,3 +0,0 @@ -switch1 = 1 -buttonChange 1 -end \ No newline at end of file diff --git a/doc/conf/s006.txt b/doc/conf/s006.txt deleted file mode 100644 index 9eb30933..00000000 --- a/doc/conf/s006.txt +++ /dev/null @@ -1,8 +0,0 @@ -button1 = 1 -mqtt 3233662-1589485 buttonSet_1_1 -mqtt 2233662-1589486 buttonSet_1_1 -end -button1 = 0 -mqtt 3233662-1589485 buttonSet_1_0 -mqtt 2233662-1589486 buttonSet_1_0 -end \ No newline at end of file diff --git a/doc/conf/s007.txt b/doc/conf/s007.txt deleted file mode 100644 index 0c48cbdf..00000000 --- a/doc/conf/s007.txt +++ /dev/null @@ -1,4 +0,0 @@ -switch1 = 1 -mqtt 3233662-1589485 buttonChange_1 -mqtt 2233662-1589486 buttonChange_1 -end \ No newline at end of file diff --git a/doc/conf/s008.txt b/doc/conf/s008.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s008.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s009.txt b/doc/conf/s009.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s009.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s010.txt b/doc/conf/s010.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s010.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s011.txt b/doc/conf/s011.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s011.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s012.txt b/doc/conf/s012.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s012.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s013.txt b/doc/conf/s013.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s013.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s014.txt b/doc/conf/s014.txt deleted file mode 100644 index 8b137891..00000000 --- a/doc/conf/s014.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/conf/s015.txt b/doc/conf/s015.txt deleted file mode 100644 index 60782623..00000000 --- a/doc/conf/s015.txt +++ /dev/null @@ -1,6 +0,0 @@ -dallas > digit1 -buttonSet 1 0 -end -dallas < digit2 -buttonSet 1 1 -end \ No newline at end of file diff --git a/doc/conf/s016.txt b/doc/conf/s016.txt deleted file mode 100644 index babd78ab..00000000 --- a/doc/conf/s016.txt +++ /dev/null @@ -1,6 +0,0 @@ -p > digit1 -buttonSet 1 0 -end -p < digit2 -buttonSet 1 1 -end \ No newline at end of file diff --git a/doc/conf/s017.txt b/doc/conf/s017.txt deleted file mode 100644 index 899b7035..00000000 --- a/doc/conf/s017.txt +++ /dev/null @@ -1,7 +0,0 @@ -switch1 = 1 -timerStart 1 digit1 sec -buttonSet 1 1 -end -timer1 = 0 -buttonSet 1 0 -end \ No newline at end of file diff --git a/doc/conf/s018.txt b/doc/conf/s018.txt deleted file mode 100644 index f764dc9d..00000000 --- a/doc/conf/s018.txt +++ /dev/null @@ -1,10 +0,0 @@ -switch1 = 1 -textSet 1 обнаружено#движение-time -end -button1 = 1 -textSet 1 не#обнаружено-time -buttonSet 1 0 -end -switch1 = 1 -push Внимание обнаружено#движение! -end \ No newline at end of file diff --git a/doc/conf/s019.txt b/doc/conf/s019.txt deleted file mode 100644 index 16e7cfa6..00000000 --- a/doc/conf/s019.txt +++ /dev/null @@ -1,12 +0,0 @@ -button1 = 1 -stepperSet 1 200 1 -end -button1 = 0 -stepperSet 1 -200 1 -end -button2 = 1 -stepperSet 2 200 1 -end -button2 = 0 -stepperSet 2 -200 1 -end \ No newline at end of file diff --git a/doc/conf/s020.txt b/doc/conf/s020.txt deleted file mode 100644 index b4340463..00000000 --- a/doc/conf/s020.txt +++ /dev/null @@ -1,12 +0,0 @@ -button1 = 1 -servoSet 1 100 -end -button1 = 0 -servoSet 1 0 -end -button2 = 1 -servoSet 2 100 -end -button2 = 0 -servoSet 2 0 -end \ No newline at end of file diff --git a/doc/conf/s021.txt b/doc/conf/s021.txt deleted file mode 100644 index 9ae72453..00000000 --- a/doc/conf/s021.txt +++ /dev/null @@ -1,6 +0,0 @@ -button2 = 1 -serialWrite ard-11-1 -end -button2 = 0 -serialWrite ard-11-0 -end \ No newline at end of file diff --git a/doc/conf/s100.txt b/doc/conf/s100.txt deleted file mode 100644 index 4927ac1e..00000000 --- a/doc/conf/s100.txt +++ /dev/null @@ -1,13 +0,0 @@ -button1 = 1 -buttonSet 2 1 -buttonSet 3 1 -pwmSet 2 1024 -end -button1 = 0 -buttonSet 2 0 -buttonSet 3 0 -pwmSet 2 0 -end -adc > 50 -buttonSet 2 1 -end \ No newline at end of file diff --git a/doc/orders.xlsm b/doc/orders.xlsm deleted file mode 100644 index d86f040e..00000000 Binary files a/doc/orders.xlsm and /dev/null differ diff --git a/doc/pictures/001 iot manager.jpeg b/doc/pictures/001 iot manager.jpeg deleted file mode 100644 index 39914774..00000000 Binary files a/doc/pictures/001 iot manager.jpeg and /dev/null differ diff --git a/doc/pictures/002 iot manager.jpeg b/doc/pictures/002 iot manager.jpeg deleted file mode 100644 index 80b9d67c..00000000 Binary files a/doc/pictures/002 iot manager.jpeg and /dev/null differ diff --git a/doc/pictures/003 iot manager.jpeg b/doc/pictures/003 iot manager.jpeg deleted file mode 100644 index 3f23d111..00000000 Binary files a/doc/pictures/003 iot manager.jpeg and /dev/null differ diff --git a/doc/pictures/007 iot manager.jpg b/doc/pictures/007 iot manager.jpg deleted file mode 100644 index 2178d634..00000000 Binary files a/doc/pictures/007 iot manager.jpg and /dev/null differ diff --git a/include/Bus/BusScanner.h b/include/Bus/BusScanner.h deleted file mode 100644 index 659aa501..00000000 --- a/include/Bus/BusScanner.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -class BusScanner { - public: - BusScanner(const char* tag, String& out, size_t tries) : _found{0}, - _tries{tries}, - _out{&out} { - _tag = new char(strlen(tag) + 1); - strcpy(_tag, tag); - } - - void scan() { - init(); - bool res; - do { - res = syncScan(); - } while (!res && --_tries); - - if (!_found) { - addResult("не найдено"); - } - } - - const char* tag() { - return _tag; - } - - protected: - virtual void init(){}; - - virtual boolean syncScan() = 0; - - protected: - void addResult(const String& str) { - _out->concat(str); - } - - void addResult(uint8_t addr, boolean last = true) { - _found++; - String str = "0x"; - if (addr < 16) { - str += "0"; - } - str += String(addr, HEX); - str += !last ? ", " : ", "; - addResult(str); - }; - - private: - char* _tag; - size_t _found; - size_t _tries; - String* _out; -}; diff --git a/include/Bus/BusScannerFactory.h b/include/Bus/BusScannerFactory.h deleted file mode 100644 index 867cf860..00000000 --- a/include/Bus/BusScannerFactory.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Bus/BusScanner.h" -#include "Bus/I2CScanner.h" -#include "Consts.h" -#include "Utils/JsonUtils.h" - -class BusScannerFactory { - public: - static BusScanner* get(String& config, BusScanner_t type, String& str) { - switch (type) { - case BS_I2C: - return new I2CScanner(str); - default: - return nullptr; - } - } -}; diff --git a/include/Bus/I2CScanner.h b/include/Bus/I2CScanner.h deleted file mode 100644 index 9678d089..00000000 --- a/include/Bus/I2CScanner.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "Bus/BusScanner.h" - -class I2CScanner : public BusScanner { - public: - I2CScanner(String& out); - - protected: - virtual void init() override; - virtual boolean syncScan() override; -}; \ No newline at end of file diff --git a/include/Class/CallBackTest.h b/include/Class/CallBackTest.h deleted file mode 100644 index 6632bfe0..00000000 --- a/include/Class/CallBackTest.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include - -// Декларируем тип - сигнатуру метода , который мы готовы принять в данном случае это -// должен быть метод без результата и без параметров. -// Новый тип мы называем AsynсActionCb - хотя можешь назвать вообще как нравиться а что значит callBack - -typedef std::function AsyncActionCb; //метод без результата и параметров -typedef std::function AsyncParamActionCb; //метод без результата и параметров - -class CallBackTest { - private: - long count; - AsyncActionCb _cb; - AsyncParamActionCb _pcb; - - - public: - CallBackTest(); - void loop(); - void setCallback(AsyncActionCb cb); - void setCallback(AsyncParamActionCb pcb); -}; -//extern CallBackTest* CB; \ No newline at end of file diff --git a/include/Class/Input.h b/include/Class/Input.h deleted file mode 100644 index 4f5bdec0..00000000 --- a/include/Class/Input.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class Input : public LineParsing { - public: - Input() : LineParsing(){}; - - void inputSetDefaultFloat() { - inputSetFloat(_key, _state); - } - - void inputSetDefaultStr() { - inputSetStr(_key, _state); - } - - void inputSetFloat(String key, String state) { - eventGen(key, ""); - jsonWriteFloat(configLiveJson, key, state.toFloat()); - MqttClient::publishStatus(key, state); - } - - void inputSetStr(String key, String state) { - eventGen(key, ""); - jsonWriteStr(configLiveJson, key, state); - MqttClient::publishStatus(key, state); - } -}; - -extern Input* myInput; \ No newline at end of file diff --git a/include/Class/LineParsing.h b/include/Class/LineParsing.h deleted file mode 100644 index 006282b1..00000000 --- a/include/Class/LineParsing.h +++ /dev/null @@ -1,177 +0,0 @@ -#pragma once - -#include - -#include "Global.h" -#include "Utils/JsonUtils.h" - -class LineParsing { - protected: - String _key; - String _file; - String _page; - String _descr; - String _order; - - String _addr; - String _pin; - String _map; - String _c; - String _inv; - String _state; - String _db; - - public: - LineParsing() : - - _key{""}, - _file{""}, - _page{""}, - _descr{""}, - _order{""}, - _addr{""}, - _pin{""}, - _map{""}, - _c{""}, - _inv{""}, - _state{""}, - _db{""} - - {}; - - void update() { - //String order = sCmd.order(); - //pm.info("create '" + order + "'"); - for (int i = 1; i < 12; i++) { - if (i == 1) _key = sCmd.next(); - if (i == 2) _file = sCmd.next(); - if (i == 3) _page = sCmd.next(); - if (i == 4) _descr = sCmd.next(); - if (i == 5) _order = sCmd.next(); - } - - for (int i = 1; i < 10; i++) { - String arg = sCmd.next(); - if (arg != "") { - if (arg.indexOf("pin[") != -1) { - _pin = extractInner(arg); - } - if (arg.indexOf("inv[") != -1) { - _inv = extractInner(arg); - } - if (arg.indexOf("st[") != -1) { - _state = extractInner(arg); - } - if (arg.indexOf("db[") != -1) { - _db = extractInner(arg); - } - if (arg.indexOf("map[") != -1) { - _map = extractInner(arg); - } - if (arg.indexOf("c[") != -1) { - _c = extractInner(arg); - } - } - } - - _page.replace("#", " "); - - _descr.replace("#", " "); - - createWidgetClass(_descr, _page, _order, _file, _key); - } - - //jsonWriteStr(configOptionJson, _key + "_pin", _pin); - - String gkey() { - return _key; - } - String gfile() { - return _file; - } - String gpage() { - return _page; - } - String gdescr() { - return _descr; - } - String gorder() { - return _order; - } - String gpin() { - return _pin; // - } - String ginv() { - return _inv; // - } - String gstate() { - return _state; - } - String gmap() { - return _map; - } - String gc() { - return _c; - } - - void clear() { - _key = ""; - _file = ""; - _page = ""; - _descr = ""; - _order = ""; - _addr = ""; - _pin = ""; - _map = ""; - _c = ""; - _inv = ""; - _state = ""; - _db = ""; - } - - String extractInnerDigit(String str) { - int p1 = str.indexOf("["); - int p2 = str.indexOf("]"); - return str.substring(p1 + 1, p2); - } - - void createWidgetClass(String descr, String page, String order, String filename, String topic) { - String buf = "{}"; - if (!loadWidgetClass(filename, buf)) { - return; - } - - jsonWriteStr(buf, "page", page); - jsonWriteStr(buf, "order", order); - jsonWriteStr(buf, "descr", descr); - jsonWriteStr(buf, "topic", prex + "/" + topic); - -#ifdef LAYOUT_IN_RAM - all_widgets += widget + "\r\n"; -#else - addFileLn("layout.txt", buf); -#endif - } - - bool loadWidgetClass(const String& filename, String& buf) { - buf = readFile(getWidgetFileClass(filename), 2048); - bool res = !(buf == "Failed" || buf == "Large"); - if (!res) { - //pm.error("on load" + filename); - } - return res; - } - - const String getWidgetFileClass(const String& name) { - return "/widgets/" + name + ".json"; - } - - //String jsonWriteStr1(String& json, String name, String value) { - // DynamicJsonBuffer jsonBuffer; - // JsonObject& root = jsonBuffer.parseObject(json); - // root[name] = value; - // json = ""; - // root.printTo(json); - // return json; - //} -}; diff --git a/include/Class/NotAsinc.h b/include/Class/NotAsinc.h deleted file mode 100644 index 73d45196..00000000 --- a/include/Class/NotAsinc.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include - -#include - -typedef std::function NotAsincCb; - -struct NotAsincItem { - bool test; - NotAsincCb cb; - void * cb_arg; - volatile bool is_used = false; -}; - -class NotAsinc { - private: - uint8_t size; - uint8_t task = 0; - NotAsincItem* items = NULL; - void handle(NotAsincCb f, void* arg); - - public: - NotAsinc(uint8_t size); - ~NotAsinc(); - - void add(uint8_t i, NotAsincCb, void* arg); - void make(uint8_t task); - void loop(); -}; -extern NotAsinc* myNotAsincActions; \ No newline at end of file diff --git a/include/Class/OutputModule.h b/include/Class/OutputModule.h deleted file mode 100644 index 7e4adc68..00000000 --- a/include/Class/OutputModule.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include "Class/LineParsing.h" -#include "Global.h" - -class OutputModule : public LineParsing { - public: - OutputModule() : LineParsing(){}; - - void OutputModuleStateSetDefault() { - if (_state != "") { - OutputModuleChange(_key, _state); - } - } - - void OutputModuleChange(String key, String state) { - state.replace("#", " "); - eventGen(key, ""); - jsonWriteStr(configLiveJson, key, state); - MqttClient::publishStatus(key, state); - } -}; -extern OutputModule* myText; \ No newline at end of file diff --git a/include/Class/Pwm.h b/include/Class/Pwm.h deleted file mode 100644 index 15353b32..00000000 --- a/include/Class/Pwm.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class Pwm : public LineParsing { - public: - Pwm() : LineParsing(){}; - - void pwmModeSet() { - if (_pin != "") { - pinMode(_pin.toInt(), INPUT); - } - } - - void pwmStateSetDefault() { - if (_state != "") { - pwmChange(_key, _pin, _state); - } - } - - void pwmChange(String key, String pin, String state) { - int pinInt = pin.toInt(); - analogWrite(pinInt, state.toInt()); - eventGen(key, ""); - jsonWriteInt(configLiveJson, key, state.toInt()); - MqttClient::publishStatus(key, state); - } -}; - -extern Pwm* myPwm; \ No newline at end of file diff --git a/include/Class/ScenarioClass.h b/include/Class/ScenarioClass.h deleted file mode 100644 index a1499b55..00000000 --- a/include/Class/ScenarioClass.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -#include -#include "Cmd.h" -#include "Global.h" - -class Scenario { - protected: - String _scenarioTmp; - String _condition; - String _conditionParam; - String _conditionSign; - String _conditionValue; - String _scenBlok; - String _event; - String _eventParam; - String _eventValue; - - public: - Scenario() : _scenarioTmp{""}, - _condition{""}, - _conditionParam{""}, - _conditionSign{""}, - _conditionValue{""}, - _scenBlok{""}, - _event{""}, - _eventParam{""}, - _eventValue{""} {}; - - void load() { - _scenarioTmp = scenario; - } - - void calculate1() { - _scenBlok = selectToMarker(_scenarioTmp, "end\n"); - _condition = selectToMarker(_scenBlok, "\n"); - _eventParam = selectToMarker(eventBuf, ","); - } - - bool isIncommingEventInScenario() { - bool ret = false; - if (_condition.indexOf(_eventParam) != -1) { - ret = true; - } - return ret; - } - - void calculate2() { - _scenarioTmp += "\n"; - _scenarioTmp.replace("\r\n", "\n"); - _scenarioTmp.replace("\r", "\n"); - - _conditionParam = selectFromMarkerToMarker(_condition, " ", 0); - _conditionSign = selectFromMarkerToMarker(_condition, " ", 1); - _conditionValue = selectFromMarkerToMarker(_condition, " ", 2); - if (!isDigitStr(_conditionValue)) _conditionValue = jsonReadStr(configLiveJson, _conditionValue); - _eventValue = jsonReadStr(configLiveJson, _conditionParam); - } - - void delOneScenBlock() { - _scenarioTmp = deleteBeforeDelimiter(_scenarioTmp, "end\n"); - } - - void delOneEvent() { - eventBuf = deleteBeforeDelimiter(eventBuf, ","); - } - - bool isConditionSatisfied() { - boolean flag = false; - - if (_conditionSign == "=") { - flag = _eventValue == _conditionValue; - } else if (_conditionSign == "!=") { - flag = _eventValue != _conditionValue; - } else if (_conditionSign == "<") { - flag = _eventValue.toInt() < _conditionValue.toInt(); - } else if (_conditionSign == ">") { - flag = _eventValue.toInt() > _conditionValue.toInt(); - } else if (_conditionSign == ">=") { - flag = _eventValue.toInt() >= _conditionValue.toInt(); - } else if (_conditionSign == "<=") { - flag = _eventValue.toInt() <= _conditionValue.toInt(); - } - - if (flag) Serial.println("[I] Scenario event: " + _condition); - - return flag; - } - - void loop() { - if (!this->isScenarioEnabled()) { - return; - } - this->load(); //после этого мы получили все сценарии - while (_scenarioTmp.length() > 1) { - this->calculate1(); //расчет необходимый для ответа на следующий вопрос - if (this->isIncommingEventInScenario()) { //если вошедшее событие есть в сценарии - this->calculate2(); - if (this->isConditionSatisfied()) { //если вошедшее событие выполняет условие сценария - _scenBlok = deleteBeforeDelimiter(_scenBlok, "\n"); - //Serial.println(" [>] Making: " + _scenBlok); - spaceExecute(_scenBlok); - } - } - this->delOneScenBlock(); //удалим использованный блок - } - this->delOneEvent(); - } - - bool isScenarioEnabled() { - return jsonReadBool(configSetupJson, "scen") && eventBuf != ""; - } -}; -extern Scenario* myScenario; \ No newline at end of file diff --git a/include/Class/SensorAnalog.h b/include/Class/SensorAnalog.h deleted file mode 100644 index 918086f0..00000000 --- a/include/Class/SensorAnalog.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include -#include "Class/LineParsing.h" -#include "Class/SensorConverting.h" -#include "Global.h" - -class SensorAnalog : public SensorConverting { - public: - SensorAnalog() : SensorConverting(){}; - - void SensorAnalogInit(String key) { - } - - int SensorAnalogRead(String key, String pin) { - int pinInt = pin.toInt(); - int value; -#ifdef ESP32 - value = analogRead(pinInt); -#endif -#ifdef ESP8266 - pinInt = pinInt; - value = analogRead(A0); -#endif - value = this->mapping(key, value); - float valueFl = this->correction(key, value); - - eventGen(key, ""); - jsonWriteStr(configLiveJson, key, String(valueFl)); - MqttClient::publishStatus(key, String(valueFl)); - - Serial.println("[I] sensor '" + key + "' data: " + String(valueFl)); - return value; - } -}; -extern SensorAnalog* mySensorAnalog; \ No newline at end of file diff --git a/include/Class/SensorConverting.h b/include/Class/SensorConverting.h deleted file mode 100644 index d5d8d8ce..00000000 --- a/include/Class/SensorConverting.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class SensorConverting : public LineParsing { - public: - SensorConverting() : LineParsing(){}; - - int mapping(String key, int input) { - String map_ = jsonReadStr(configOptionJson, key + "_map"); - if (map_ != "") { - input = map(input, - selectFromMarkerToMarker(map_, ",", 0).toInt(), - selectFromMarkerToMarker(map_, ",", 1).toInt(), - selectFromMarkerToMarker(map_, ",", 2).toInt(), - selectFromMarkerToMarker(map_, ",", 3).toInt()); - } - return input; - } - - float correction(String key, float input) { - String corr = jsonReadStr(configOptionJson, key + "_с"); - if (corr != "") { - float coef = corr.toFloat(); - input = input * coef; - } - return input; - } -}; \ No newline at end of file diff --git a/include/Class/Switch.h b/include/Class/Switch.h deleted file mode 100644 index 57e9f450..00000000 --- a/include/Class/Switch.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#include "Class/LineParsing.h" -#include "Global.h" - -class Switch : public LineParsing { - protected: - int numberEntering = 0; - int state = _state.toInt(); - - public: - Switch() : LineParsing(){}; - - void init() { - if (_pin != "") { - int number = numberEntering++; - buttons[number].attach(_pin.toInt()); - buttons[number].interval(_db.toInt()); - but[number] = true; - jsonWriteStr(configOptionJson, "switch_num_" + String(number), _key); - } - } - - void loop() { - static uint8_t switch_number = 1; - if (but[switch_number]) { - buttons[switch_number].update(); - if (buttons[switch_number].fell()) { - } - if (buttons[switch_number].rose()) { - String key = jsonReadStr(configOptionJson, "switch_num_" + String(switch_number)); - state = !state; - switchChangeVirtual(key, String(state)); - } - } - switch_number++; - if (switch_number == NUM_BUTTONS) { - switch_number = 0; - } - } - void switchStateSetDefault() { - if (_state != "") { - switchChangeVirtual(_key, _state); - } - } - - void switchChangeVirtual(String key, String state) { - eventGen(key, ""); - jsonWriteInt(configLiveJson, key, state.toInt()); - MqttClient::publishStatus(key, state); - } -}; - -extern Switch* mySwitch; \ No newline at end of file diff --git a/include/Class/button.h b/include/Class/button.h deleted file mode 100644 index 4287720a..00000000 --- a/include/Class/button.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include "Class/LineParsing.h" -#include "Global.h" - -class Button1 : public LineParsing { - public: - Button1() : LineParsing(){}; - - void pinModeSet() { - if (_pin != "") { - pinMode(_pin.toInt(), OUTPUT); - } - } - - void pinStateSetDefault() { - if (_inv == "" && _state != "") { - pinChange(_key, _pin, _state, true); - } - } - - void pinStateSetInvDefault() { - if (_inv != "" && _state != "") { - pinChange(_key, _pin, _state, false); - } - } - - void pinChange(String key, String pin, String state, bool rev) { - int pinInt = pin.toInt(); - int stateInt; - if (rev) { - digitalWrite(pinInt, state.toInt()); - } else { - digitalWrite(pinInt, !state.toInt()); - } - eventGen(key, ""); - jsonWriteInt(configLiveJson, key, state.toInt()); - MqttClient::publishStatus(key, state); - } -}; - -extern Button1* myButton; \ No newline at end of file diff --git a/include/Clock.h b/include/Clock.h deleted file mode 100644 index 53685c1f..00000000 --- a/include/Clock.h +++ /dev/null @@ -1,167 +0,0 @@ -#pragma once - -#include "Utils/TimeUtils.h" -#include "Utils/PrintMessage.h" -#include "Global.h" -#include "Clock.h" - -#ifdef ESP8266 -#include "sntp.h" -#endif - -class Clock { - const char* MODULE = "Clock"; - - private: - Time_t _time_local; - Time_t _time_utc; - unsigned long _uptime; - unsigned long _unixtime; - int _timezone; - String _ntp; - bool _hasSynced; - bool _configured; - - public: - Clock() : _uptime{0}, _timezone{0}, _ntp{""}, _hasSynced{false}, _configured{false} {}; - - void loop() { - unsigned long passed = millis_since(_uptime); - if (passed < ONE_SECOND_ms) { - return; - } - _uptime += passed; - - // world time - time_t now = getSystemTime(); - time_t estimated = _unixtime + (passed / ONE_SECOND_ms); - double drift = difftime(now, estimated); - if (drift > 1) { - // Обработать ситуации c дрифтом времени на значительные величины - } - // TODO сохранять время на флеше - - _unixtime = now; - - breakEpochToTime(_unixtime, _time_utc); - - breakEpochToTime(_unixtime + getOffsetInSeconds(_timezone), _time_local); - } - - bool hasSync() { - if (!_hasSynced) { - startSync(); - } - return _hasSynced; - } - - void setNtpPool(String ntp) { - if (!_ntp.equals(ntp)) { - _ntp = ntp; - _configured = false; - } - } - - void setTimezone(int timezone) { - if (_timezone != timezone) { - _timezone = timezone; - _configured = false; - } - } - - void startSync() { - if (!_configured) { - //pm.info("sync to: " + _ntp + " timezone: " + String(_timezone)); - setupSntp(); - _configured = true; - // лучше не ждать, проверим в следующий раз - return; - } - _hasSynced = hasTimeSynced(); - if (_hasSynced) { - //pm.info("synced " + getDateDotFormated() + " " + getTime()); - } else { - //pm.error("failed to obtain"); - } - } - - void setupSntp() { -#ifdef ESP2866 - sntp_setservername(0, _ntp.c_str()); - sntp_setservername(1, "ru.pool.ntp.org"); - sntp_setservername(2, "pool.ntp.org"); - sntp_stop(); - sntp_set_timezone(0); // UTC time - sntp_init(); -#else - configTime(0, 0, _ntp.c_str(), "ru.pool.ntp.org", "pool.ntp.org"); -#endif - } - - bool hasTimeSynced() const { - return _unixtime > MIN_DATETIME; - } - - time_t getSystemTime() const { - timeval tv{0, 0}; - timezone tz = timezone{0, 0}; - time_t epoch = 0; - if (gettimeofday(&tv, &tz) != -1) { - epoch = tv.tv_sec; - } - return epoch; - } - - const String getTimeUnix() { - return String(_unixtime); - } - - /* - * Локальное время "дд.ММ.гг" - */ - const String getDateDotFormated() { - char buf[32]; - sprintf(buf, "%02d.%02d.%02d", _time_local.day_of_month, _time_local.month, _time_local.year); - return String(buf); - } - - /* - * Локальное дата время "дд.ММ.гг чч.мм.cc" - */ - const String getDateTimeDotFormated() { - char buf[32]; - sprintf(buf, "%02d.%02d.%02d %02d:%02d:%02d", _time_local.day_of_month, _time_local.month, _time_local.year, _time_local.hour, _time_local.minute, _time_local.second); - return String(buf); - } - - /* - * Локальное время "чч:мм:cc" - */ - const String getTime() { - char buf[32]; - sprintf(buf, "%02d:%02d:%02d", _time_local.hour, _time_local.minute, _time_local.second); - return String(buf); - } - - const String getTimeJson() { - char buf[32]; - sprintf(buf, "%02d-%02d-%02d", _time_local.hour, _time_local.minute, _time_local.second); - return String(buf); - } - - /* - * Локальное время "чч:мм" - */ - const String getTimeWOsec() { - char buf[32]; - sprintf(buf, "%02d:%02d", _time_local.hour, _time_local.minute); - return String(buf); - } - - /* - * Время с момента запуска "чч:мм:cc" далее "дд чч:мм" - */ - const String getUptime() { - return prettyMillis(_uptime); - } -}; \ No newline at end of file diff --git a/include/Cmd.h b/include/Cmd.h deleted file mode 100644 index 3c49565a..00000000 --- a/include/Cmd.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include - -extern void cmd_init(); - -extern void buttonOut(); -extern void buttonOutSet(); - -extern void pwmOut(); -extern void pwmOutSet(); - -extern void buttonIn(); -extern void buttonInSet(); - -extern void inputDigit(); -extern void inputDigitSet(); - -extern void inputTime(); -extern void inputTimeSet(); - -extern void textOut(); -extern void textOutSet(); - -extern void analogAdc(); -extern void analogReading(); - -extern void ultrasonicCm(); -extern void ultrasonicReading(); - - - - - - - - - -extern void sensorsInit(); - -extern void levelPr(); -extern void ultrasonicCm(); -extern void ultrasonic_reading(); - - -extern void analog_reading1(); -extern void analog_reading2(); -extern void dallas_reading(); -extern void dhtT_reading(); - -extern void dallas(); - -extern void bmp280T(); -extern void bmp280P(); -extern void bmp280T_reading(); -extern void bmp280P_reading(); - -extern void bme280T(); -extern void bme280P(); -extern void bme280H(); -extern void bme280A(); - -extern void bme280T_reading(); -extern void bme280P_reading(); -extern void bme280H_reading(); -extern void bme280A_reading(); - -extern void dhtT(); -extern void dhtH(); -extern void dhtP(); -extern void dhtC(); -extern void dhtD(); -extern void dhtH_reading(); -extern void dhtP_reading(); -extern void dhtC_reading(); -extern void dhtD_reading(); - -extern void handle_time_init(); -extern void stepper(); -extern void stepperSet(); -extern void servo_(); -extern void servoSet(); -extern void serialBegin(); -extern void serialWrite(); -extern void logging(); - - -extern void button(); -extern void timeSet(); - -extern void mqttOrderSend(); -extern void httpOrderSend(); -extern void firmwareVersion(); -extern void firmwareUpdate(); -extern void loadScenario(); - -extern void fileExecute(const String& filename); -extern void csvExecute(String& cmdStr); -extern void spaceExecute(String& cmdStr); \ No newline at end of file diff --git a/include/Consts.h b/include/Consts.h deleted file mode 100644 index 22194c29..00000000 --- a/include/Consts.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once -/* -* Main consts -*/ -#define FIRMWARE_VERSION "2.4.0" -#define NUM_BUTTONS 6 -#define LED_PIN 2 -#define FLASH_4MB true - -#define MQTT_RECONNECT_INTERVAL 20000 - -#define TELEMETRY_UPDATE_INTERVAL 7200000 - -#define DEVICE_CONFIG_FILE "s.conf.csv" -#define DEVICE_SCENARIO_FILE "s.scen.txt" - -#define DEFAULT_PRESET 100 -#define DEFAULT_SCENARIO 100 - -#define TAG_ONE_WIRE "oneWire" -#define TAG_I2C "i2c" -#define TAG_ONE_WIRE_PIN "oneWirePin" - -/* -* Optional -*/ -//#define OTA_UPDATES_ENABLED -//#define MDNS_ENABLED -//#define WEBSOCKET_ENABLED -//#define LAYOUT_IN_RAM -//#define UDP_ENABLED - -/* -* Sensor -*/ -#define TANK_LEVEL_SAMPLES 10 -#define LEVEL_ENABLED - -#define ANALOG_ENABLED - - -#define DALLAS_ENABLED -#define DHT_ENABLED - -#define BMP_ENABLED - -#define BME_ENABLED - -#define FSEditor - -//#define SSDP - -/* -* Gears -*/ -#define STEPPER_ENABLED -//#define SERVO_ENABLED - -/* -* Other -*/ -#define LOGGING_ENABLED -#define SERIAL_ENABLED -#define PUSH_ENABLED - -struct Time_t { - uint8_t second; - uint8_t minute; - uint8_t hour; - uint8_t day_of_week; - uint8_t day_of_month; - uint8_t month; - uint16_t day_of_year; - uint16_t year; - unsigned long days; - unsigned long valid; -}; - -enum TimerTask_t { WIFI_SCAN, - WIFI_MQTT_CONNECTION_CHECK, - SENSORS, - STEPPER1, - STEPPER2, - LOG1, - LOG2, - LOG3, - LOG4, - LOG5, - TIMER_COUNTDOWN, - TIME, - TIME_SYNC, - STATISTICS, - UPTIME, - UDP, - UDP_DB, - TEST }; - -enum notAsincActions { - do_ZERO, - do_UPGRADE, - do_GETLASTVERSION, - do_UDPDATAPARSE, - do_MQTTUDP, - do_BUSSCAN, - do_MQTTPARAMSCHANGED, - do_LAST, -}; - -enum ErrorType_t { - ET_NONE, - ET_FUNCTION, - ET_MODULE, - ET_SYSTEM -}; - -enum ErrorLevel_t { - EL_NONE, - EL_INFO, - EL_WARNING, - EL_ERROR -}; - -enum LedStatus_t { - LED_OFF, - LED_ON, - LED_SLOW, - LED_FAST -}; - -enum ConfigType_t { - CT_CONFIG, - CT_SCENARIO -}; - -enum BusScanner_t { - BS_I2C, - BS_ONE_WIRE -}; diff --git a/include/ESP32.h b/include/ESP32.h deleted file mode 100644 index 1662e906..00000000 --- a/include/ESP32.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#ifdef ESP32 -// don't change order -#include "WiFi.h" -// - -#include "ESPAsyncWebServer.h" -#include "SPIFFSEditor.h" -// don't change order -#include -#include -#include -#include - -// -#include -#include - -#ifdef MDNS_ENABLED -#include -#endif - -extern AsyncUDP udp; - -#endif \ No newline at end of file diff --git a/include/ESP8266.h b/include/ESP8266.h deleted file mode 100644 index 6296fc81..00000000 --- a/include/ESP8266.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#ifdef ESP8266 -#include -#include -#include "ESPAsyncTCP.h" -#include "ESPAsyncWebServer.h" -#include -#include -#include -#include -#include -#ifdef MDNS_ENABLED -#include -#endif - -extern WiFiUDP udp; - -#endif \ No newline at end of file diff --git a/include/Errors.h b/include/Errors.h deleted file mode 100644 index cefac965..00000000 --- a/include/Errors.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include - -String getErrorLevelStr(ErrorLevel_t level); - -class Error : public Printable { - public: - static Error OK() { - return Error(); - } - - static Error InfoMessage(const char *message) { - return Error(EL_INFO, message); - } - - static Error ErrorMessage(const char *message) { - return Error(EL_ERROR, message); - } - - public: - Error() : _type{ET_NONE}, _level{EL_NONE} {}; - - Error(const ErrorLevel_t level, const char *message) : Error(ET_FUNCTION, level, message){}; - - Error(const ErrorType_t type, const ErrorLevel_t level, const char *message) : _type{type}, _level{level} { - strncpy(_message, message, sizeof(_message)); - }; - - const ErrorLevel_t level() const { return _level; } - - const ErrorType_t type() const { return _type; } - - const char *message() const { return _message; } - - operator bool() const { return _level != EL_NONE; } - - const String toString() const { - char buf[128]; - sprintf(buf, "[%s] %s", getErrorLevelStr(_level).c_str(), _message); - return String(buf); - } - - virtual size_t printTo(Print &p) const { - return p.println(toString().c_str()); - } - - private: - char _message[128]; - ErrorType_t _type; - ErrorLevel_t _level; -}; diff --git a/include/FSEditor.h b/include/FSEditor.h deleted file mode 100644 index 394fb0e2..00000000 --- a/include/FSEditor.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#ifdef ESP8266 -#include -#endif - -class FSEditor : public AsyncWebHandler { - private: - fs::FS _fs; - String _username; - String _password; - bool _authenticated; - uint32_t _startTime; - - public: -#ifdef ESP32 - FSEditor(const fs::FS& fs, const String& username = String(), const String& password = String()); -#else - FSEditor(const String& username = String(), const String& password = String(), const fs::FS& fs = LittleFS); -#endif - virtual bool canHandle(AsyncWebServerRequest* request) override final; - virtual void handleRequest(AsyncWebServerRequest* request) override final; - virtual void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final; - virtual bool isRequestHandlerTrivial() override final { return false; } -}; \ No newline at end of file diff --git a/include/Global.h b/include/Global.h deleted file mode 100644 index 4911fdb2..00000000 --- a/include/Global.h +++ /dev/null @@ -1,221 +0,0 @@ -#pragma once - -/* -* Libraries -*/ -#include -#include - -#include "ESP32.h" -#include "ESP8266.h" -// -#include "Consts.h" -#include "Errors.h" -#include "GyverFilters.h" -#include "Upgrade.h" -#include "Clock.h" - -#include "MqttClient.h" -#include "Utils\FileUtils.h" -#include "Utils\JsonUtils.h" -#include "Utils\StringUtils.h" -#include "Utils\SysUtils.h" -#include "Utils\PrintMessage.h" -#include "Utils\WiFiUtils.h" - -//=========ПОДКЛЮЧЕНИЕ ОБЩИХ БИБЛИОТЕК=============== - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef WEBSOCKET_ENABLED -extern AsyncWebSocket ws; -//extern AsyncEventSource events; -#endif - -extern Clock* timeNow; - -extern TickerScheduler ts; - - - -extern WiFiClient espClient; - -extern PubSubClient mqtt; - -extern StringCommand sCmd; - -extern AsyncWebServer server; - -extern DallasTemperature sensors; - -extern OneWire *oneWire; - -extern boolean but[NUM_BUTTONS]; - -extern Bounce* buttons; - -/* -* Global vars -*/ - -extern boolean just_load; - -// Json -extern String configSetupJson; //все настройки -extern String configLiveJson; //все данные с датчиков (связан с mqtt) -extern String configOptionJson; //для трансфера - -// Mqtt -extern String chipId; -extern String prex; - -extern String all_widgets; - -extern String scenario; - -//orders and events -extern String orderBuf; -extern String eventBuf; - -extern String itemsFile; -extern String itemsLine; - -// Sensors -extern String sensorReadingMap; - - - - -extern String analog_value_names_list; -extern int enter_to_analog_counter; - -extern String dallas_value_name; -extern int enter_to_dallas_counter; - -extern String levelPr_value_name; -extern String ultrasonicCm_value_name; - -extern String dhtT_value_name; -extern String dhtH_value_name; - -extern String bmp280T_value_name; -extern String bmp280P_value_name; - -extern String bme280T_value_name; -extern String bme280P_value_name; -extern String bme280H_value_name; -extern String bme280A_value_name; - -extern String logging_value_names_list; -extern int enter_to_logging_counter; - -extern int scenario_line_status[40]; - -extern String lastVersion; - -extern boolean checkUpdatesFlag; -extern boolean updateFlag; -extern boolean mqttParamsChanged; -extern boolean udp_data_parse; -extern boolean mqtt_send_settings_to_udp; -extern boolean delElementFlag; -extern boolean getJsonListFromCsvFlag; - -extern String csvFile; -extern int colum; - -/* -* Запрос на скарнирование шины -*/ -extern boolean busScanFlag; -/* -* Запрос на сканирование шины, указание какую -*/ -extern BusScanner_t busToScan; - -extern boolean fsCheckFlag; - -extern int sensors_reading_map[15]; - -/* -* Global functions -*/ - - - - -// Logging -extern void logging(); -extern void deleteOldDate(String filename, size_t max_lines, String date_to_add); -extern void clean_log_date(); -extern void choose_log_date_and_send(); - -// Main -extern void setChipId(); -extern void saveConfig(); -extern void setConfigParam(const char* param, const String& value); - -extern String getURL(const String& urls); -extern void do_fscheck(); -extern void doBusScan(); -extern void servo_(); -extern void clock_init(); - -extern void setLedStatus(LedStatus_t); - -//Scenario -extern void eventGen(String event_name, String number); -extern String add_set(String param_name); - - - -//Timers -extern void Timer_countdown_init(); -extern void timerStart_(); -extern void addTimer(String number, String time); -extern void timerStop_(); -extern void delTimer(String number); -extern int readTimer(int number); - -extern void upgradeInit(); - -// widget -extern void createWidgetByType(String widget_name, String page_name, String page_number, String file, String topic); -extern void createWidgetParam(String widget_name, String page_name, String page_number, String file, String topic, String name1, String param1, String name2, String param2, String name3, String param3); -extern void createWidget(String widget_name, String page_name, String page_number, String type, String topik); -extern void createChart(String widget_name, String page_name, String page_number, String file, String topic, String maxCount); - -// PushingBox -extern void pushControl(); - -// UDP -extern void udpInit(); -extern void do_udp_data_parse(); -extern void do_mqtt_send_settings_to_udp(); - -extern void addCommandLoop(const String& cmdStr); -extern void loopSerial(); -extern void loopCmd(); -extern void loopScenario(); -extern void loopUdp(); - -extern void do_update(); - -// Init -extern void uptime_init(); - -// Web -extern void web_init(); -extern void telemetry_init(); diff --git a/include/HttpServer.h b/include/HttpServer.h deleted file mode 100644 index fa7a5187..00000000 --- a/include/HttpServer.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "Global.h" - -namespace HttpServer { - -void init(); - -} // namespace HttpServer diff --git a/include/Init.h b/include/Init.h deleted file mode 100644 index 6b4c744e..00000000 --- a/include/Init.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -extern void loadConfig(); -extern void all_init(); -extern void statistics_init(); -extern void loadScenario(); -extern void Device_init(); -extern void prsets_init(); -extern void handle_uptime(); -extern void handle_statistics(); -extern void telemetry_init(); \ No newline at end of file diff --git a/include/ItemsList.h b/include/ItemsList.h deleted file mode 100644 index d973ff60..00000000 --- a/include/ItemsList.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include -#include "Global.h" - -extern void addItem(String name); -extern void delAllItems(); -extern uint8_t getNewElementNumber(String file); -extern uint8_t getFreePinAll(); -extern uint8_t getFreePinAnalog(); diff --git a/include/Module/CharBuffer.h b/include/Module/CharBuffer.h deleted file mode 100644 index 74fd922d..00000000 --- a/include/Module/CharBuffer.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include - -class CharBuffer : Print { - public: - CharBuffer(size_t size) : _capacity(size < 2 ? 2 : size), _write(0), _read(0) { - _pool = new char[_capacity + 1]; - memset(_pool, 0, _capacity + 1); - } - - CharBuffer(const CharBuffer &src) { - _capacity = src._capacity; - _write = src._write; - memcpy(_pool, src._pool, src._write); - } - - CharBuffer(const char *str) : CharBuffer(strlen(str) + 1) { - write((const uint8_t *)str, strlen(str)); - } - - ~CharBuffer() { - delete _pool; - } - - void clear() { - memset(_pool, 0, _capacity); - _write = 0; - _read = 0; - } - - size_t size() const { return _capacity; } - - size_t free() const { return _capacity - _write - 2; } - - size_t available() const { return _write; } - - const char *c_str() { - if (_pool[_write] != '\x00') - _pool[_write] = '\x00'; - return _pool; - } - - size_t write(char ch) { - return write((uint8_t)ch); - }; - - size_t write(const uint8_t ch) { - size_t n = 0; - if (_write < (_capacity - 2)) { - _pool[_write++] = ch; - n = 1; - } - return n; - } - - size_t write(const uint8_t *ptr, const size_t size) { - size_t n = 0; - while (n < size) { - uint8_t ch = ptr[n++]; - if (!write(ch)) - break; - } - return n; - } - - protected: - char *_pool; - size_t _capacity; - size_t _write; - size_t _read; -}; diff --git a/include/Module/CircularBuffer.h b/include/Module/CircularBuffer.h deleted file mode 100644 index 5c197652..00000000 --- a/include/Module/CircularBuffer.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include - -template -class CircularBuffer { - public: - CircularBuffer() : _head{0}, _tail{0}, _full{false} {} - - ~CircularBuffer() {} - - void reset() { - _head = _tail = _full = 0; - } - - bool empty() const { - return _head == _tail && !_full; - } - - bool full() const { - return _full; - } - - size_t size() const { - size_t res = 0; - if (!_full) { - if (_head < _tail) - res = BUFFER_SIZE + _head - _tail; - else - res = _head - _tail; - } else { - res = BUFFER_SIZE; - } - return res; - } - - void push(const T &item) { - if (_full) { - _tail++; - if (_tail == BUFFER_SIZE) - _tail = 0; - } - _pool[_head++] = item; - if (_head == BUFFER_SIZE) - _head = 0; - if (_head == _tail) - _full = true; - } - - bool pop(T &item) { - bool res = false; - if (!empty()) { - item = _pool[_tail++]; - if (_tail == BUFFER_SIZE) _tail = 0; - _full = false; - res = true; - } - return res; - } - - bool pop_back(T &item) { - bool res = false; - if (!empty()) { - item = _pool[--_head]; - _full = false; - res = true; - } - return res; - } - - bool peek(T &item) const { - bool res = false; - if (!empty()) { - item = _pool[_tail]; - - res = true; - } - return res; - } - - private: - T _pool[BUFFER_SIZE]; - size_t _head; - size_t _tail; - bool _full; -}; \ No newline at end of file diff --git a/include/Module/CommandShell.h b/include/Module/CommandShell.h deleted file mode 100644 index 6a986639..00000000 --- a/include/Module/CommandShell.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -#include "Module/Terminal.h" -#include "Module/CircularBuffer.h" -#include "Module/Runner.h" - -class CommandShell { - public: - CommandShell(Runner *runner); - - void setTerm(Terminal *term); - Terminal *term(); - - void showGreetings(bool = true); - void showFarewell(bool = true); - - void clearHistory(); - void addHistory(const char *); - bool getHistoryInput(String &); - void setEditLine(const String &); - bool active(); - void loop(); - - private: - size_t printGreetings(Print *); - size_t printFarewell(Print *); - size_t printPrompt(Print *); - - void onOpen(Print *out); - void onClose(Print *out); - void onData(const char *); - void onHistory(Print *out); - bool getLastInput(String &); - - private: - CircularBuffer _history; - Terminal *_term; - Runner *_runner; - String _path; - bool _active; - bool _greetings; - bool _farewell; -}; diff --git a/include/Module/EditLine.h b/include/Module/EditLine.h deleted file mode 100644 index 4368fb7f..00000000 --- a/include/Module/EditLine.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "Module/CharBuffer.h" - -class EditLine : public CharBuffer { - public: - EditLine(size_t size) : CharBuffer(size){}; - - char &operator[](size_t i) { return _pool[i]; } - - char operator[](size_t i) const { return _pool[i]; } - - EditLine &operator=(const EditLine &src) { - delete[] _pool; - _pool = new char[src._capacity]; - memcpy(_pool, src._pool, src._capacity); - _read = src._read; - _write = src._write; - return *this; - } - - void del() { - size_t i; - for (i = _write; i < _capacity; ++i) - _pool[i] = _pool[i + 1]; - _pool[i] = '\x00'; - } - - bool backspace() { - bool res = false; - if (prev()) { - del(); - res = true; - } - return res; - } - - bool next() { - bool res = false; - if (_write < _capacity - 1) { - _write++; - res = true; - } - return res; - } - - bool prev() { - bool res = false; - if (_write > 0) { - _write--; - res = true; - } - return res; - } - - size_t home() { - size_t res = _write; - _write = 0; - return res; - } - - size_t end() { - size_t n; - for (n = 0; n < _capacity - 1; ++n) - if (_pool[n] == '\x00') break; - return n; - } -}; diff --git a/include/Module/Module.h b/include/Module/Module.h deleted file mode 100644 index 800d5919..00000000 --- a/include/Module/Module.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include - -enum ModuleState_t { - MOD_INIT, - MOD_INIT_FAILED, - MOD_INIT_COMPLETE, - MOD_START_FAILED, - MOD_ACTIVE -}; - -class Module { - protected: - virtual bool onInit() { return true; }; - virtual void onEnd(){}; - virtual bool onStart() { return true; } - virtual void onStop(){}; - virtual void onLoop() = 0; - - protected: - Print *_out; - - public: - Module() : _state{MOD_INIT} {} - - bool init(bool force = false) { - if (_state > MOD_INIT_COMPLETE) { - return true; - } - if (_state == MOD_INIT_FAILED && !force) { - return false; - } - - _state = onInit() ? MOD_INIT_COMPLETE : MOD_INIT_FAILED; - - return _state == MOD_INIT_COMPLETE; - } - - bool start(bool force = false) { - if (_state == MOD_ACTIVE) { - return true; - } - if (_state == MOD_START_FAILED && !force) { - return false; - } - if (_state < MOD_INIT_COMPLETE) { - if (!init(force)) { - return false; - } - } - _state = onStart() ? MOD_ACTIVE : MOD_START_FAILED; - return _state == MOD_ACTIVE; - } - - void stop() { - if (_state < MOD_ACTIVE) { - return; - } - onStop(); - _state = MOD_INIT_COMPLETE; - }; - - void end() { - if (_state < MOD_INIT_FAILED) { - return; - } - onEnd(); - _state = MOD_INIT; - }; - - void loop() { - if (_state == MOD_ACTIVE || start()) onLoop(); - }; - - void setOutput(Print *p) { _out = p; } - - ModuleState_t getState() { - return _state; - } - - private: - ModuleState_t _state; -}; diff --git a/include/Module/Runner.h b/include/Module/Runner.h deleted file mode 100644 index 6d6c97ec..00000000 --- a/include/Module/Runner.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include "Cmd.h" - -class Runner { - public: - virtual void run(const char*, Print*); -}; - -class CmdRunner : public Runner { - public: - void run(const char* cmd, Print* out) override { - String cmdStr{cmd}; - csvExecute(cmdStr); - } -}; \ No newline at end of file diff --git a/include/Module/Telnet.h b/include/Module/Telnet.h deleted file mode 100644 index ade6e4f2..00000000 --- a/include/Module/Telnet.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Global.h" - -#include "Module/Module.h" -#include "Module/Terminal.h" -#include "Module/CommandShell.h" - -#include - -enum TelnetEvent_t { - TE_CONNECTED, - TE_DISCONNECTED -}; - -typedef std::function TelnetEventHandler; - -class Telnet : public Module { - public: - Telnet(uint16_t port) : _port{port}, _lastConnected{false} {}; - - public: - void setEventHandler(TelnetEventHandler); - void sendData(const String&); - bool hasClient(); - bool isShellActive(); - void setCommandShell(CommandShell*); - - protected: - bool onInit() override; - void onEnd() override; - bool onStart() override; - void onStop() override; - void onLoop() override; - - private: - void onConnect(); - void onDisconnect(); - void onData(); - void onOpen(); - void onClose(); - - private: - TelnetEventHandler _eventHandler; - uint16_t _port; - bool _lastConnected; - WiFiClient _client; - WiFiServer* _server; - Terminal* _term; - CommandShell* _shell; -}; diff --git a/include/Module/Terminal.h b/include/Module/Terminal.h deleted file mode 100644 index cbb8a6f8..00000000 --- a/include/Module/Terminal.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include - -#include "Module/EditLine.h" -#include - -#define A_NORMAL 0x0000 // normal -#define A_UNDERLINE 0x0001 // underline -#define A_REVERSE 0x0002 // reverse -#define A_BLINK 0x0004 // blink -#define A_BOLD 0x0008 // bold -#define A_DIM 0x0010 // dim -#define A_STANDOUT A_BOLD // standout (same as bold) - -#define F_BLACK 0x0100 // foreground black -#define F_RED 0x0200 // foreground red -#define F_GREEN 0x0300 // foreground green -#define F_BROWN 0x0400 // foreground brown -#define F_BLUE 0x0500 // foreground blue -#define F_MAGENTA 0x0600 // foreground magenta -#define F_CYAN 0x0700 // foreground cyan -#define F_WHITE 0x0800 // foreground white -#define F_YELLOW F_BROWN // some terminals show brown as yellow (with A_BOLD) -#define F_COLOR 0x0F00 // foreground mask - -#define B_BLACK 0x1000 // background black -#define B_RED 0x2000 // background red -#define B_GREEN 0x3000 // background green -#define B_BROWN 0x4000 // background brown -#define B_BLUE 0x5000 // background blue -#define B_MAGENTA 0x6000 // background magenta -#define B_CYAN 0x7000 // background cyan -#define B_WHITE 0x8000 // background white -#define B_YELLOW B_BROWN // some terminals show brown as yellow (with A_BOLD) -#define B_COLOR 0xF000 // background mask - -#define CHAR_NULL 0x00 -#define CHAR_BEL 0x07 -#define CHAR_BS 0x08 -#define CHAR_SPACE 0x20 -#define CHAR_TAB 0x09 -#define CHAR_LF 0x0a -#define CHAR_CR 0x0d -#define CHR_ZERO 0x30 - -#define KEY_DEL 0x7f -#define KEY_DOWN 0x80 -#define KEY_UP 0x81 -#define KEY_LEFT 0x82 -#define KEY_RIGHT 0x83 -#define KEY_HOME 0x84 -#define KEY_INS 0x86 -#define KEY_PAGE_DOWN 0x87 -#define KEY_PAGE_UP 0x88 -#define KEY_END 0x89 -#define CHAR_LT 0x8b -#define CHAR_CSI 0x9b -#define CHAR_ESC 0x1b -#define CHAR_BIN 0xFF - -#define ESC_CURSOR_HOME "\x1b[H" -#define ESC_SAVE_CURSOR "\x1b[s" -#define ESC_UNSAVE_CURSOR "\x1b[u" -#define ESC_SAVE_CURSOR_AND_ATTRS "\x1b[7" -#define ESC_RESTORE_CURSOR_AND_ATTRS "\x1b[8" - -#define ESC_CLEAR "\x1b[2J" -#define ESC_CLEAR_BOTTOM "\x1b[J" -#define ESC_CLEAR_EOL "\x1b[0K" - -#define ESC_CURSOR_UP "\x1b[1A" -#define ESC_CURSOR_DOWN "\x1b[1B" -#define ESC_CURSOR_FORWARD "\x1b[1C" -#define ESC_CURSOR_BACKWARD "\x1b[1D" - -#define SEQ_CSI PSTR("\033[") // code introducer -#define SEQ_LOAD_G1 PSTR("\033)0") // load G1 character set -#define SEQ_CLEAR PSTR("\033[2J") // clear screen -#define SEQ_ATTRSET PSTR("\033[0") // set attributes, e.g. "\033[0;7;1m" - -#define SEQ_ATTRSET_BOLD PSTR(";1") // bold -#define SEQ_ATTRSET_DIM PSTR(";2") // dim -#define SEQ_ATTRSET_FCOLOR PSTR(";3") // forground color -#define SEQ_ATTRSET_UNDERLINE PSTR(";4") // underline -#define SEQ_ATTRSET_BCOLOR PSTR(";4") // background color -#define SEQ_ATTRSET_BLINK PSTR(";5") // blink -#define SEQ_ATTRSET_REVERSE PSTR(";7") // reverse - -enum TerminalEventEnum { - EVENT_OPEN, - EVENT_CLOSE, - EVENT_TAB -}; - -enum SpecialKeyEnum { SPEC_KEY_UP, - SPEC_KEY_TAB, - SPEC_KEY_ENTER, - SPEC_KEY_ESC }; - -typedef std::function SpecialKeyPressedEvent; - -typedef std::function TerminalEventHandler; - -typedef std::function TerminalInputEventHandler; - -enum EOLType_t { CRLF, - LFCR, - LF, - CR }; - -enum State { ST_INACTIVE, - ST_NORMAL, - ST_ESC_SEQ, - ST_CTRL_SEQ }; - -class Terminal : public Print { - public: - Terminal(Stream *stream = nullptr); - - void setStream(Stream *stream); - void setEOL(EOLType_t code); - void enableControlCodes(bool enabled = true); - void enableEcho(bool enabled = true); - void enableColors(bool enabled = true); - void setOnEvent(TerminalEventHandler); - void setOnSpecKeyPress(SpecialKeyPressedEvent); - void setOnReadLine(TerminalInputEventHandler); - - bool setLine(const uint8_t *bytes, size_t size); - CharBuffer &getLine(); - - void backsp(); - void clear(); - void clear_line(); - size_t println(const char *str); - size_t println(void); - size_t write_P(PGM_P str); - size_t write(uint8_t c); - size_t write(const uint8_t *buf, size_t size); - void writeByDigit(uint8_t i); - bool available(); - void loop(); - void start(); - void quit(); - void initscr(); - void attrset(uint16_t attr); - - private: - void move(uint8_t y, uint8_t x); - TerminalEventHandler eventHandler_; - TerminalInputEventHandler inputHandler_; - - uint8_t attr = 0xff; - uint8_t curY = 0xff; - uint8_t curX = 0xff; - - unsigned long _lastReceived = 0; - State state; - Stream *_stream; - EditLine _line; - char _cc_buf[32] = {0}; - size_t _cc_pos = 0; - bool _color = false; - bool _controlCodes = false; - bool _echo = false; - EOLType_t _eol = CRLF; - - struct ControlCode { - const char *cc; - const char ch; - }; - - ControlCode keyMap[10] = { - {"G", KEY_HOME}, // 71 Home key - {"H", KEY_UP}, // 72 Up arrow - {"I", KEY_PAGE_UP}, // 73 PageUp - {"K", KEY_LEFT}, // 75 Left arrow - {"M", KEY_RIGHT}, // 77 Right arrow - {"O", KEY_END}, // 79 End key - {"P", KEY_DOWN}, // 80 Down arrow - {"Q", KEY_PAGE_DOWN}, // 81 PageDown - {"R", KEY_INS}, // 82 Insert - {"S", KEY_DEL}, // 83 Delete - }; -}; diff --git a/include/MqttClient.h b/include/MqttClient.h deleted file mode 100644 index 74977ef6..00000000 --- a/include/MqttClient.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -namespace MqttClient { - -void init(); -boolean connect(); -void reconnect(); -void loop(); - -void subscribe(); - -boolean publish(const String& topic, const String& data); -boolean publishData(const String& topic, const String& data); -boolean publishChart(const String& topic, const String& data); -boolean publishControl(String id, String topic, String state); -boolean publishChart_test(const String& topic, const String& data); -boolean publishStatus(const String& topic, const String& data); - -void publishWidgets(); -void publishState(); - -void handleSubscribedUpdates(char* topic, uint8_t* payload, size_t length); -const String getStateStr(); - -} // namespace MqttClient diff --git a/include/MqttDiscovery.h b/include/MqttDiscovery.h deleted file mode 100644 index 282b26fc..00000000 --- a/include/MqttDiscovery.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "Utils/SysUtils.h" - -namespace Discovery { - - -} \ No newline at end of file diff --git a/include/SSDP.h b/include/SSDP.h deleted file mode 100644 index 6eaff70c..00000000 --- a/include/SSDP.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#ifdef SSDP -#include - -extern void SsdpInit(); -extern String xmlNode(String tags, String data); -extern String decToHex(uint32_t decValue, byte desiredStringLength); -#endif \ No newline at end of file diff --git a/include/Sensors.h b/include/Sensors.h deleted file mode 100644 index 45dcbb04..00000000 --- a/include/Sensors.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - - diff --git a/include/Servo/Servos.h b/include/Servo/Servos.h deleted file mode 100644 index 2ff3e1ec..00000000 --- a/include/Servo/Servos.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#ifdef SERVO_ENABLED - -#include -#include -#include - -struct Servo_t { - uint8_t num; - uint8_t pin; - Servo* obj; - Servo_t(uint8_t num, uint8_t pin) : num{num}, pin{pin}, obj{nullptr} {}; -}; - -class Servos { - public: - Servos(); - Servo* get(uint8_t num); - Servo* create(uint8_t num, uint8_t pin); - - size_t count(); - - private: - std::vector _items; -}; - -extern Servos myServo; - -#endif \ No newline at end of file diff --git a/include/Strings_.h b/include/Strings_.h deleted file mode 100644 index 21865168..00000000 --- a/include/Strings_.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -//Strings -// #include -#include - -#include -#include - -class char_array { - private: - char *p_stringarray; //initial input array - char *p_inputstring; //holds actual length data - //char **pp_database_string; //allocate data database.cpp - int stringsize; //holds array size to allocate memory - int charinput; //holds array input size - - public: - inline char_array(); //inline so other functions can call on it - inline ~char_array(); - inline void getinput(char *&); //retrieves user input - inline void grabline(char *&); //retrieve line with whitespace included NOT COMPLETE, may not need - inline int sortline(char **&, char *&); //sorts line into an array of strings and returns number of separate strings - inline int arraysize(); //returns size of string array - inline void printinput(); //print input string - inline void changedefaultsize(); //change default input size NOT COMPLETE -}; - -inline char_array::char_array() //constructor -{ - stringsize = 0; - charinput = 64; - p_stringarray = new char[charinput]; -} - -inline char_array::~char_array() //destructor -{ - delete[] p_inputstring; - delete[] p_stringarray; -} - -inline void char_array::getinput(char *&p_stringin) { - stringsize = 0; - - scanf("%63s", p_stringarray); //get input string - - while (p_stringarray[stringsize] != 0) //finding out the size of string array - { - stringsize++; - } - stringsize++; - - p_inputstring = new char[stringsize]; //reallocate memory block and set array inside - for (int i = 0; i < stringsize; i++) { - p_inputstring[i] = p_stringarray[i]; - } - - p_inputstring[stringsize - 1] = '\x00'; - - p_stringin = new char[stringsize]; //set pp_stringin - - p_stringin = p_inputstring; -} - -inline void char_array::grabline(char *&p_stringin) { - stringsize = 0; - std::cin.getline(p_stringarray, charinput); - while (p_stringarray[stringsize] != 0) { - stringsize++; - } - stringsize++; - p_inputstring = new char[stringsize]; - - for (int i = 0; i < stringsize; i++) { - p_inputstring[i] = p_stringarray[i]; - } - - p_stringin = new char[stringsize]; - p_stringin = p_inputstring; -} - -inline int char_array::sortline(char **&pp_stringin, char *&p_string) { - int position = 0; //position in string - int charcount = 1; //how many characters there are - int wordnum; //which word is being processed - int wordcount = 1; //number of words in string - int bookmark = 0; //holds last known position - - while (p_string[position] == ' ') { - position++; - } - - wordnum = position; //borrow wordnum to hold position value - while (p_string[position] != '\x00') //find total inputted string word length - { - if ((p_string[position] == ' ') && ((p_string[position + 1] != ' ') || (p_string[position + 1] != '\x00'))) { - wordcount++; - } - position++; - } - position = wordnum; //set position to original value - for (wordnum = 0; wordnum < wordcount; wordnum++) { - charcount = 1; - while (p_string[position] == ' ') { - position++; - } - while ((p_string[position] != ' ') && (p_string[position] != '\x00')) { - position++; - charcount++; - } - pp_stringin[wordnum] = new char[charcount]; - - for (int i = 0; i < charcount; i++) { - pp_stringin[wordnum][i] = p_string[i + bookmark]; - } - pp_stringin[wordnum][charcount - 1] = '\x00'; - bookmark = position + 1; - } - - return wordcount; -} - -inline int char_array::arraysize() { - if (stringsize != 0) { - return stringsize; - } else { - printf("Array size is set at 0\n"); - return 0; - } -} - -inline void char_array::printinput() { - printf("%s", p_inputstring); -} - -inline void char_array::changedefaultsize() { - printf("Input new default input string size: "); - scanf("%i", charinput); -} diff --git a/include/Upgrade.h b/include/Upgrade.h deleted file mode 100644 index 4d21f53b..00000000 --- a/include/Upgrade.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -const String getAvailableUrl(const char* mcu); -void getLastVersion(); -void upgradeInit(); -void upgrade_firmware(); \ No newline at end of file diff --git a/include/Utils/FileHelper.h b/include/Utils/FileHelper.h deleted file mode 100644 index daa9186c..00000000 --- a/include/Utils/FileHelper.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -#include "FS.h" - -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif - -class FileHelper { - public: - FileHelper(const String filename); - /* - * Проверить существование - */ - void exists(); - /* - * Удалить файл - */ - void remove(); - /* - * Открыть файл установить позицию @position - */ - File seek(size_t position = 0); - - /* - * Чтение строки с содержащей @substr - */ - String readFileString(const String substr); - - /* - * Добовление строки @str в файл - */ - String appendStr(const String str); - - /* - * Запись строки - */ - String writeStr(const String); - - /* - * Чтение в строку - */ - String readStr(size_t); - - /* - * Размер в байтах - */ - size_t getSize(); -}; \ No newline at end of file diff --git a/include/Utils/FileUtils.h b/include/Utils/FileUtils.h deleted file mode 100644 index da2688ac..00000000 --- a/include/Utils/FileUtils.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include - -#include "Consts.h" - -#include "FS.h" - -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif - -/* -* Инициализация ФС -*/ -bool fileSystemInit(); - -/* -* Удалить файл -*/ -void removeFile(const String& filename); - -/* -* Открыть файл на позиции -*/ -File seekFile(const String& filename, size_t position = 0); - -/* -* Чтение строки из файла -* возвращает стоку из файла в которой есть искомое слово found -*/ -const String readFileString(const String& filename, const String& to_find); - -/* -* Добовление строки в файл -*/ -const String addFileLn(const String& filename, const String& str); - -/* -* Добовление строки в файл -*/ -const String addFile(const String& filename, const String& str); - -/* -* Запись строки в файл -*/ -const String writeFile(const String& filename, const String& str); - -/* -* Чтение файла в строку -*/ -const String readFile(const String& filename, size_t max_size); - -/* -* Размер файла -*/ -const String getFileSize(const String& filename); - -bool copyFile(const String& src, const String& dst, bool overwrite = true); - -const String getFSSizeInfo(); - -const String getConfigFile(uint8_t preset, ConfigType_t type); diff --git a/include/Utils/JsonUtils.h b/include/Utils/JsonUtils.h deleted file mode 100644 index c244a14e..00000000 --- a/include/Utils/JsonUtils.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -String jsonReadStr(String& json, String name); - -int jsonReadInt(String& json, String name); - -boolean jsonReadBool(String& json, String name); - -String jsonWriteStr(String& json, String name, String value); - -String jsonWriteInt(String& json, String name, int value); - -String jsonWriteFloat(String& json, String name, float value); - -String jsonWriteBool(String& json, String name, boolean value); - -void saveConfig(); \ No newline at end of file diff --git a/include/Utils/PrintMessage.h b/include/Utils/PrintMessage.h deleted file mode 100644 index 833342b6..00000000 --- a/include/Utils/PrintMessage.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Arduino.h" -#include "Utils\StringUtils.h" -#include "Utils\TimeUtils.h" -#include "Errors.h" -#include "Global.h" - - -#define pm PrintMessage(MODULE) - -class PrintMessage { - public: - PrintMessage(const char* module) { - _module = module; - } - - void error(const String& str) { - print(EL_ERROR, str); - } - - void info(const String& str) { - print(EL_INFO, str); - } - - private: - void print(const ErrorLevel_t level, const String& str) { - Serial.printf("%s [%s] [%s] %s\n", prettyMillis(millis()).c_str(), getErrorLevelStr(level).c_str(), _module, str.c_str()); - } - - private: - const char* _module; -}; diff --git a/include/Utils/StringUtils.h b/include/Utils/StringUtils.h deleted file mode 100644 index 3c23ee4a..00000000 --- a/include/Utils/StringUtils.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -uint8_t hexStringToUint8(String hex); - -uint16_t hexStringToUint16(String hex); - -String selectToMarkerLast(String str, String found); - -String selectToMarker(String str, String found); - -String extractInner(String str); - -String deleteAfterDelimiter(String str, String found); - -String deleteBeforeDelimiter(String str, String found); - -String deleteBeforeDelimiterTo(String str, String found); - -String deleteToMarkerLast(String str, String found); - -String selectFromMarkerToMarker(String str, String found, int number); - -size_t itemsCount(String str, const String& separator); - -boolean isDigitStr(const String&); - -String prettyBytes(size_t size); \ No newline at end of file diff --git a/include/Utils/SysUtils.h b/include/Utils/SysUtils.h deleted file mode 100644 index 1686e51f..00000000 --- a/include/Utils/SysUtils.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include "Global.h" - -const String getChipId(); - -void setLedStatus(LedStatus_t status); - -const String getUniqueId(const String& name); - -const String printMemoryStatus(); - -const String getHeapStats(); - -const String getMacAddress(); - -void setChipId(); \ No newline at end of file diff --git a/include/Utils/TimeUtils.h b/include/Utils/TimeUtils.h deleted file mode 100644 index d20286cf..00000000 --- a/include/Utils/TimeUtils.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include - -#include "Consts.h" - -#define ONE_MINUTE_s 60 -#define ONE_HOUR_m 60 -#define ONE_HOUR_s 60 * ONE_MINUTE_s -#define LEAP_YEAR(Y) (((1970 + Y) > 0) && !((1970 + Y) % 4) && (((1970 + Y) % 100) || !((1970 + Y) % 400))) -#define MIN_DATETIME 1575158400 -#define ONE_SECOND_ms 1000 - -/* -* Время (мс) прошедщее с @since -*/ -unsigned long millis_since(unsigned long sinse); - -/* -* Интерввал времени (мс) между @start и @finish -*/ -unsigned long millis_passed(unsigned long start, unsigned long finish); - -/* -* Форматиронное время интервала (мс) -* "чч:мм:cc", -* "дд чч:мм", если > 24 часов -*/ -const String prettyMillis(unsigned long time_ms = millis()); - -/* -* Форматиронное время интервала (c) -* "чч:мм:cc", -* "дд чч:мм", если > 24 часов -*/ -const String prettySeconds(unsigned long time_s); - -/* -* Тайм зона в секундах -*/ -int getOffsetInSeconds(int timezone); - -/* -* Тайм зона в минутах -*/ -int getOffsetInMinutes(int timezone); - -/* -* Разбивает время на составляющие -*/ -void breakEpochToTime(unsigned long epoch, Time_t& tm); \ No newline at end of file diff --git a/include/Utils/Timings.h b/include/Utils/Timings.h deleted file mode 100644 index c992eeee..00000000 --- a/include/Utils/Timings.h +++ /dev/null @@ -1,71 +0,0 @@ -#include - -enum Timings_t { MT_ONE, - MT_TWO, - NUM_TIMINGS }; - -struct Timing { - unsigned long _total_mu; - unsigned long _min_mu; - unsigned long _max_mu; - - Timing() : _total_mu{0}, _min_mu{999999}, _max_mu{0} {}; - - void reset() { - _total_mu = 0; - _min_mu = 999999; - _max_mu = 0; - } - - void add(unsigned long time_mu) { - if (time_mu == 0) return; - - _total_mu += time_mu; - - if (_min_mu > time_mu) { - _min_mu = time_mu; - } - - if (_max_mu < time_mu) { - _max_mu = time_mu; - } - } -}; - -static const char* module_name[NUM_TIMINGS] = {"strings", "boolean"}; - -struct Timings { - Timing mu[NUM_TIMINGS]; - - unsigned long _counter; - unsigned long _start; - unsigned long long _total; - - Timings() : _counter{0}, _start{0} {}; - - void add(size_t module, unsigned long now = micros()) { - unsigned long time = now - _start; - _total += time; - mu[module].add(time); - _start = now; - } - - void count() { - _counter++; - _start = micros(); - } - - void print() { - if (!_counter) { - return; - }; - Serial.printf("lp/ms: %llu ", _counter / _total); - for (size_t i = 0; i < NUM_TIMINGS; i++) { - Serial.printf("%s: %.2f%% ", module_name[i], ((float)mu[i]._total_mu / _total) * 100); - mu[i].reset(); - } - Serial.println(); - _counter = 0; - _total = 0; - }; -}; \ No newline at end of file diff --git a/include/Utils/WebUtils.h b/include/Utils/WebUtils.h deleted file mode 100644 index 5479314c..00000000 --- a/include/Utils/WebUtils.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "HttpServer.h" -#include - -extern String getURL(const String& urls); -extern const String getMethodName(AsyncWebServerRequest* request); -extern const String getRequestInfo(AsyncWebServerRequest* request); \ No newline at end of file diff --git a/include/Utils/WiFiUtils.h b/include/Utils/WiFiUtils.h deleted file mode 100644 index da020af4..00000000 --- a/include/Utils/WiFiUtils.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "Global.h" - -boolean isNetworkActive(); - -void startSTAMode(); - -bool startAPMode(); - -boolean scanWiFi(String ssid); - diff --git a/include/Web.h b/include/Web.h deleted file mode 100644 index a80a08d2..00000000 --- a/include/Web.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "Arduino.h" - - -void web_init(); -void setConfigParam(const char* param, const String& value); diff --git a/include/main.h b/include/main.h deleted file mode 100644 index 0d48dfc5..00000000 --- a/include/main.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -//void myCallback; \ No newline at end of file diff --git a/include/test.h b/include/test.h deleted file mode 100644 index 6de4739a..00000000 --- a/include/test.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -void setupTest(); -void loopTest(); -//char stringToCharArray(String in); \ No newline at end of file diff --git a/include/udp_.h b/include/udp_.h deleted file mode 100644 index 6583878c..00000000 --- a/include/udp_.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -extern void udpInit(); -extern bool isUdpEnabled(); -extern void loopUdp(); -extern void handleUdp_esp32(); -extern void do_udp_data_parse(); -extern void add_dev_in_list(String filename, String id, String dev_name, String ip); -extern void send_mqtt_to_udp(); \ No newline at end of file diff --git a/lib/ESP8266-StringCommand/StringCommand.cpp b/lib/ESP8266-StringCommand/StringCommand.cpp deleted file mode 100644 index 3d53dcd7..00000000 --- a/lib/ESP8266-StringCommand/StringCommand.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/** - * SerialCommand - A Wiring/Arduino library to tokenize and parse commands - * received over a serial port. - * - * Copyright (C) 2012 Stefan Rado - * Copyright (C) 2011 Steven Cogswell - * http://husks.wordpress.com - * - * Version 20120522 - * - * This library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This library 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library. If not, see . - */ -#include "StringCommand.h" - -/** - * Constructor makes sure some things are set. - */ -StringCommand::StringCommand() - : commandList(NULL), - commandCount(0), - defaultHandler(NULL), - term('\n'), // default terminator for commands, newline character - last(NULL), - main(NULL) -{ - strcpy(delim, " "); // strtok_r needs a null-terminated string - clearBuffer(); -} - -/** - * Adds a "command" and a handler function to the list of available commands. - * This is used for matching a found token in the buffer, and gives the pointer - * to the handler function to deal with it. - */ -void StringCommand::addCommand(const char *command, void (*function)()) { - #ifdef SERIALCOMMAND_DEBUG - Serial.print("Adding command ("); - Serial.print(commandCount); - Serial.print("): "); - Serial.println(command); - #endif - - commandList = (StringCommandCallback *) realloc(commandList, (commandCount + 1) * sizeof(StringCommandCallback)); - strncpy(commandList[commandCount].command, command, SERIALCOMMAND_MAXCOMMANDLENGTH); - commandList[commandCount].function = function; - commandCount++; -} - -/** - * This sets up a handler to be called in the event that the receveived command string - * isn't in the list of commands. - */ -void StringCommand::setDefaultHandler(void (*function)(const char *)) { - defaultHandler = function; -} - - -/** - * This checks the Serial stream for characters, and assembles them into a buffer. - * When the terminator character (default '\n') is seen, it starts parsing the - * buffer for a prefix command, and calls handlers setup by addCommand() member - */ -void StringCommand::readStr(String sBuffer ) { - sBuffer.toCharArray(buffer, SERIALCOMMAND_BUFFER); - #ifdef SERIALCOMMAND_DEBUG - Serial.print("Received: "); - Serial.println(buffer); - #endif - - char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer - if (command != NULL) { - boolean matched = false; - for (int i = 0; i < commandCount; i++) { - #ifdef SERIALCOMMAND_DEBUG - Serial.print("Comparing ["); - Serial.print(command); - Serial.print("] to ["); - Serial.print(commandList[i].command); - Serial.println("]"); - #endif - - - - // Compare the found command against the list of known commands for a match - if (strncmp(command, commandList[i].command, SERIALCOMMAND_MAXCOMMANDLENGTH) == 0) { - #ifdef SERIALCOMMAND_DEBUG - Serial.print("Matched Command: "); - Serial.println(command); - #endif - - // Execute the stored handler function for the command - (*commandList[i].function)(); - matched = true; - break; - } - } - - - if (!matched && (defaultHandler != NULL)) { - (*defaultHandler)(command); - } - - - clearBuffer(); - } -} - -/* - * Clear the input buffer. - */ -void StringCommand::clearBuffer() { - buffer[0] = '\0'; - bufPos = 0; -} - -/** - * Retrieve the next token ("word" or "argument") from the command buffer. - * Returns NULL if no more tokens exist. - */ -char *StringCommand::next() { - return strtok_r(NULL, delim, &last); -} - -char *StringCommand::order() { - return strtok_r(buffer, delim, &main); -} diff --git a/lib/ESP8266-StringCommand/StringCommand.h b/lib/ESP8266-StringCommand/StringCommand.h deleted file mode 100644 index b37d5a1a..00000000 --- a/lib/ESP8266-StringCommand/StringCommand.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * SerialCommand - A Wiring/Arduino library to tokenize and parse commands - * received over a serial port. - * - * Copyright (C) 2012 Stefan Rado - * Copyright (C) 2011 Steven Cogswell - * http://husks.wordpress.com - * - * Version 20120522 - * - * This library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This library 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library. If not, see . - */ -#ifndef StringCommand_h -#define StringCommand_h - -#if defined(WIRING) && WIRING >= 100 - #include -#elif defined(ARDUINO) && ARDUINO >= 100 - #include -#else - #include -#endif -#include - -// Size of the input buffer in bytes (maximum length of one command plus arguments) -#define SERIALCOMMAND_BUFFER 128 //256 -// Maximum length of a command excluding the terminating null -#define SERIALCOMMAND_MAXCOMMANDLENGTH 16 - -// Uncomment the next line to run the library in debug mode (verbose messages) -//#define SERIALCOMMAND_DEBUG - - -class StringCommand { - public: - StringCommand(); // Constructor - void addCommand(const char *command, void(*function)()); // Add a command to the processing dictionary. - void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received. - - void readStr(String sBuffer ); // Main entry point. - void clearBuffer(); // Clears the input buffer. - char *next(); // Returns pointer to next token found in command buffer (for getting arguments to commands). - char *order(); - - private: - // Command/handler dictionary - struct StringCommandCallback { - char command[SERIALCOMMAND_MAXCOMMANDLENGTH + 1]; - void (*function)(); - }; // Data structure to hold Command/Handler function key-value pairs - StringCommandCallback *commandList; // Actual definition for command/handler array - byte commandCount; - - // Pointer to the default handler function - void (*defaultHandler)(const char *); - - char delim[2]; // null-terminated list of character to be used as delimeters for tokenizing (default " ") - char term; // Character that signals end of command (default '\n') - - char buffer[SERIALCOMMAND_BUFFER + 1]; // Buffer of stored characters while waiting for terminator character - byte bufPos; // Current position in the buffer - char *last; // State variable used by strtok_r during processing - char *main; -}; - -#endif //StringCommand_h diff --git a/lib/ESP8266-StringCommand/keywords.txt b/lib/ESP8266-StringCommand/keywords.txt deleted file mode 100644 index c9799b2c..00000000 --- a/lib/ESP8266-StringCommand/keywords.txt +++ /dev/null @@ -1,23 +0,0 @@ -####################################### -# Datatypes (KEYWORD1) -####################################### - -StringCommand KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -addCommand KEYWORD2 -setDefaultHandler KEYWORD2 -readString KEYWORD2 -clearBuffer KEYWORD2 -next KEYWORD2 - -####################################### -# Instances (KEYWORD2) -####################################### - -####################################### -# Constants (LITERAL1) -####################################### diff --git a/lib/ESP8266-StringCommand/readme.md b/lib/ESP8266-StringCommand/readme.md deleted file mode 100644 index 731ee5e2..00000000 --- a/lib/ESP8266-StringCommand/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -StringCommand -============= -Библиотека для ESP8266 позволяющая связать запуск пользовательских функций с строковой переменной. - - diff --git a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp32.sh b/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp32.sh deleted file mode 100644 index cf1026d6..00000000 --- a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp32.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -export ARDUINO_ESP32_PATH="$ARDUINO_USR_PATH/hardware/espressif/esp32" -if [ ! -d "$ARDUINO_ESP32_PATH" ]; then - echo "Installing ESP32 Arduino Core ..." - script_init_path="$PWD" - mkdir -p "$ARDUINO_USR_PATH/hardware/espressif" - cd "$ARDUINO_USR_PATH/hardware/espressif" - - echo "Installing Python Serial ..." - pip install pyserial > /dev/null - - if [ "$OS_IS_WINDOWS" == "1" ]; then - echo "Installing Python Requests ..." - pip install requests > /dev/null - fi - - if [ "$GITHUB_REPOSITORY" == "espressif/arduino-esp32" ]; then - echo "Linking Core..." - ln -s $GITHUB_WORKSPACE esp32 - else - echo "Cloning Core Repository..." - git clone https://github.com/espressif/arduino-esp32.git esp32 > /dev/null 2>&1 - fi - - echo "Updating Submodules ..." - cd esp32 - git submodule update --init --recursive > /dev/null 2>&1 - - echo "Installing Platform Tools ..." - cd tools && python get.py - cd $script_init_path - - echo "ESP32 Arduino has been installed in '$ARDUINO_ESP32_PATH'" - echo "" -fi diff --git a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp8266.sh b/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp8266.sh deleted file mode 100644 index 048cd024..00000000 --- a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-core-esp8266.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -echo "Installing ESP8266 Arduino Core ..." -script_init_path="$PWD" -mkdir -p "$ARDUINO_USR_PATH/hardware/esp8266com" -cd "$ARDUINO_USR_PATH/hardware/esp8266com" - -echo "Installing Python Serial ..." -pip install pyserial > /dev/null - -if [ "$OS_IS_WINDOWS" == "1" ]; then - echo "Installing Python Requests ..." - pip install requests > /dev/null -fi - -echo "Cloning Core Repository ..." -git clone https://github.com/esp8266/Arduino.git esp8266 > /dev/null 2>&1 - -echo "Updating submodules ..." -cd esp8266 -git submodule update --init --recursive > /dev/null 2>&1 - -echo "Installing Platform Tools ..." -cd tools -python get.py > /dev/null -cd $script_init_path - -echo "ESP8266 Arduino has been installed in '$ARDUINO_USR_PATH/hardware/esp8266com'" -echo "" diff --git a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-ide.sh b/lib/ESPAsyncWebServer/.github/scripts/install-arduino-ide.sh deleted file mode 100644 index ce60cb82..00000000 --- a/lib/ESPAsyncWebServer/.github/scripts/install-arduino-ide.sh +++ /dev/null @@ -1,228 +0,0 @@ -#!/bin/bash - -#OSTYPE: 'linux-gnu', ARCH: 'x86_64' => linux64 -#OSTYPE: 'msys', ARCH: 'x86_64' => win32 -#OSTYPE: 'darwin18', ARCH: 'i386' => macos - -OSBITS=`arch` -if [[ "$OSTYPE" == "linux"* ]]; then - export OS_IS_LINUX="1" - ARCHIVE_FORMAT="tar.xz" - if [[ "$OSBITS" == "i686" ]]; then - OS_NAME="linux32" - elif [[ "$OSBITS" == "x86_64" ]]; then - OS_NAME="linux64" - elif [[ "$OSBITS" == "armv7l" || "$OSBITS" == "aarch64" ]]; then - OS_NAME="linuxarm" - else - OS_NAME="$OSTYPE-$OSBITS" - echo "Unknown OS '$OS_NAME'" - exit 1 - fi -elif [[ "$OSTYPE" == "darwin"* ]]; then - export OS_IS_MACOS="1" - ARCHIVE_FORMAT="zip" - OS_NAME="macosx" -elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then - export OS_IS_WINDOWS="1" - ARCHIVE_FORMAT="zip" - OS_NAME="windows" -else - OS_NAME="$OSTYPE-$OSBITS" - echo "Unknown OS '$OS_NAME'" - exit 1 -fi -export OS_NAME - -ARDUINO_BUILD_DIR="$HOME/.arduino/build.tmp" -ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" - -if [ "$OS_IS_MACOS" == "1" ]; then - export ARDUINO_IDE_PATH="/Applications/Arduino.app/Contents/Java" - export ARDUINO_USR_PATH="$HOME/Documents/Arduino" -elif [ "$OS_IS_WINDOWS" == "1" ]; then - export ARDUINO_IDE_PATH="$HOME/arduino_ide" - export ARDUINO_USR_PATH="$HOME/Documents/Arduino" -else - export ARDUINO_IDE_PATH="$HOME/arduino_ide" - export ARDUINO_USR_PATH="$HOME/Arduino" -fi - -if [ ! -d "$ARDUINO_IDE_PATH" ]; then - echo "Installing Arduino IDE on $OS_NAME ..." - echo "Downloading 'arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT' to 'arduino.$ARCHIVE_FORMAT' ..." - if [ "$OS_IS_LINUX" == "1" ]; then - wget -O "arduino.$ARCHIVE_FORMAT" "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1 - echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..." - tar xf "arduino.$ARCHIVE_FORMAT" > /dev/null - mv arduino-nightly "$ARDUINO_IDE_PATH" - else - curl -o "arduino.$ARCHIVE_FORMAT" -L "https://www.arduino.cc/download.php?f=/arduino-nightly-$OS_NAME.$ARCHIVE_FORMAT" > /dev/null 2>&1 - echo "Extracting 'arduino.$ARCHIVE_FORMAT' ..." - unzip "arduino.$ARCHIVE_FORMAT" > /dev/null - if [ "$OS_IS_MACOS" == "1" ]; then - mv "Arduino.app" "/Applications/Arduino.app" - else - mv arduino-nightly "$ARDUINO_IDE_PATH" - fi - fi - rm -rf "arduino.$ARCHIVE_FORMAT" - - mkdir -p "$ARDUINO_USR_PATH/libraries" - mkdir -p "$ARDUINO_USR_PATH/hardware" - - echo "Arduino IDE Installed in '$ARDUINO_IDE_PATH'" - echo "" -fi - -function build_sketch(){ # build_sketch [extra-options] - if [ "$#" -lt 2 ]; then - echo "ERROR: Illegal number of parameters" - echo "USAGE: build_sketch [extra-options]" - return 1 - fi - - local fqbn="$1" - local sketch="$2" - local build_flags="$3" - local xtra_opts="$4" - local win_opts="" - if [ "$OS_IS_WINDOWS" == "1" ]; then - local ctags_version=`ls "$ARDUINO_IDE_PATH/tools-builder/ctags/"` - local preprocessor_version=`ls "$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/"` - win_opts="-prefs=runtime.tools.ctags.path=$ARDUINO_IDE_PATH/tools-builder/ctags/$ctags_version -prefs=runtime.tools.arduino-preprocessor.path=$ARDUINO_IDE_PATH/tools-builder/arduino-preprocessor/$preprocessor_version" - fi - - echo "" - echo "Compiling '"$(basename "$sketch")"' ..." - mkdir -p "$ARDUINO_BUILD_DIR" - mkdir -p "$ARDUINO_CACHE_DIR" - $ARDUINO_IDE_PATH/arduino-builder -compile -logger=human -core-api-version=10810 \ - -fqbn=$fqbn \ - -warnings="all" \ - -tools "$ARDUINO_IDE_PATH/tools-builder" \ - -tools "$ARDUINO_IDE_PATH/tools" \ - -built-in-libraries "$ARDUINO_IDE_PATH/libraries" \ - -hardware "$ARDUINO_IDE_PATH/hardware" \ - -hardware "$ARDUINO_USR_PATH/hardware" \ - -libraries "$ARDUINO_USR_PATH/libraries" \ - -build-cache "$ARDUINO_CACHE_DIR" \ - -build-path "$ARDUINO_BUILD_DIR" \ - -prefs=compiler.cpp.extra_flags="$build_flags" \ - $win_opts $xtra_opts "$sketch" -} - -function count_sketches() # count_sketches -{ - local examples="$1" - rm -rf sketches.txt - if [ ! -d "$examples" ]; then - touch sketches.txt - return 0 - fi - local sketches=$(find $examples -name *.ino) - local sketchnum=0 - for sketch in $sketches; do - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - continue - fi; - if [[ -f "$sketchdir/.test.skip" ]]; then - continue - fi - echo $sketch >> sketches.txt - sketchnum=$(($sketchnum + 1)) - done - return $sketchnum -} - -function build_sketches() # build_sketches [extra-options] -{ - local fqbn=$1 - local examples=$2 - local chunk_idex=$3 - local chunks_num=$4 - local xtra_opts=$5 - - if [ "$#" -lt 2 ]; then - echo "ERROR: Illegal number of parameters" - echo "USAGE: build_sketches [ ] [extra-options]" - return 1 - fi - - if [ "$#" -lt 4 ]; then - chunk_idex="0" - chunks_num="1" - xtra_opts=$3 - fi - - if [ "$chunks_num" -le 0 ]; then - echo "ERROR: Chunks count must be positive number" - return 1 - fi - if [ "$chunk_idex" -ge "$chunks_num" ]; then - echo "ERROR: Chunk index must be less than chunks count" - return 1 - fi - - set +e - count_sketches "$examples" - local sketchcount=$? - set -e - local sketches=$(cat sketches.txt) - rm -rf sketches.txt - - local chunk_size=$(( $sketchcount / $chunks_num )) - local all_chunks=$(( $chunks_num * $chunk_size )) - if [ "$all_chunks" -lt "$sketchcount" ]; then - chunk_size=$(( $chunk_size + 1 )) - fi - - local start_index=$(( $chunk_idex * $chunk_size )) - if [ "$sketchcount" -le "$start_index" ]; then - echo "Skipping job" - return 0 - fi - - local end_index=$(( $(( $chunk_idex + 1 )) * $chunk_size )) - if [ "$end_index" -gt "$sketchcount" ]; then - end_index=$sketchcount - fi - - local start_num=$(( $start_index + 1 )) - echo "Found $sketchcount Sketches"; - echo "Chunk Count : $chunks_num" - echo "Chunk Size : $chunk_size" - echo "Start Sketch: $start_num" - echo "End Sketch : $end_index" - - local sketchnum=0 - for sketch in $sketches; do - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [ "${sketchdirname}.ino" != "$sketchname" ] \ - || [ -f "$sketchdir/.test.skip" ]; then - continue - fi - sketchnum=$(($sketchnum + 1)) - if [ "$sketchnum" -le "$start_index" ] \ - || [ "$sketchnum" -gt "$end_index" ]; then - continue - fi - local sketchBuildFlags="" - if [ -f "$sketchdir/.test.build_flags" ]; then - while read line; do - sketchBuildFlags="$sketchBuildFlags $line" - done < "$sketchdir/.test.build_flags" - fi - build_sketch "$fqbn" "$sketch" "$sketchBuildFlags" "$xtra_opts" - local result=$? - if [ $result -ne 0 ]; then - return $result - fi - done - return 0 -} diff --git a/lib/ESPAsyncWebServer/.github/scripts/install-platformio.sh b/lib/ESPAsyncWebServer/.github/scripts/install-platformio.sh deleted file mode 100644 index 594948ea..00000000 --- a/lib/ESPAsyncWebServer/.github/scripts/install-platformio.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash - -echo "Installing Python Wheel ..." -pip install wheel > /dev/null 2>&1 - -echo "Installing PlatformIO ..." -pip install -U platformio > /dev/null 2>&1 - -echo "PlatformIO has been installed" -echo "" - - -function build_pio_sketch(){ # build_pio_sketch - if [ "$#" -lt 3 ]; then - echo "ERROR: Illegal number of parameters" - echo "USAGE: build_pio_sketch " - return 1 - fi - - local board="$1" - local sketch="$2" - local buildFlags="$3" - local sketch_dir=$(dirname "$sketch") - echo "" - echo "Compiling '"$(basename "$sketch")"' ..." - python -m platformio ci -l '.' --board "$board" "$sketch_dir" --project-option="board_build.partitions = huge_app.csv" --project-option="build_flags=$buildFlags" -} - -function count_sketches() # count_sketches -{ - local examples="$1" - rm -rf sketches.txt - if [ ! -d "$examples" ]; then - touch sketches.txt - return 0 - fi - local sketches=$(find $examples -name *.ino) - local sketchnum=0 - for sketch in $sketches; do - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - continue - fi; - if [[ -f "$sketchdir/.test.skip" ]]; then - continue - fi - echo $sketch >> sketches.txt - sketchnum=$(($sketchnum + 1)) - done - return $sketchnum -} - -function build_pio_sketches() # build_pio_sketches -{ - if [ "$#" -lt 2 ]; then - echo "ERROR: Illegal number of parameters" - echo "USAGE: build_pio_sketches [ ]" - return 1 - fi - - local board=$1 - local examples=$2 - local chunk_idex=$3 - local chunks_num=$4 - - if [ "$#" -lt 4 ]; then - chunk_idex="0" - chunks_num="1" - fi - - if [ "$chunks_num" -le 0 ]; then - echo "ERROR: Chunks count must be positive number" - return 1 - fi - if [ "$chunk_idex" -ge "$chunks_num" ]; then - echo "ERROR: Chunk index must be less than chunks count" - return 1 - fi - - set +e - count_sketches "$examples" - local sketchcount=$? - set -e - local sketches=$(cat sketches.txt) - rm -rf sketches.txt - - local chunk_size=$(( $sketchcount / $chunks_num )) - local all_chunks=$(( $chunks_num * $chunk_size )) - if [ "$all_chunks" -lt "$sketchcount" ]; then - chunk_size=$(( $chunk_size + 1 )) - fi - - local start_index=$(( $chunk_idex * $chunk_size )) - if [ "$sketchcount" -le "$start_index" ]; then - echo "Skipping job" - return 0 - fi - - local end_index=$(( $(( $chunk_idex + 1 )) * $chunk_size )) - if [ "$end_index" -gt "$sketchcount" ]; then - end_index=$sketchcount - fi - - local start_num=$(( $start_index + 1 )) - echo "Found $sketchcount Sketches"; - echo "Chunk Count : $chunks_num" - echo "Chunk Size : $chunk_size" - echo "Start Sketch: $start_num" - echo "End Sketch : $end_index" - - local sketchnum=0 - for sketch in $sketches; do - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [ "${sketchdirname}.ino" != "$sketchname" ] \ - || [ -f "$sketchdir/.test.skip" ]; then - continue - fi - local sketchBuildFlags="" - if [ -f "$sketchdir/.test.build_flags" ]; then - while read line; do - sketchBuildFlags="$sketchBuildFlags $line" - done < "$sketchdir/.test.build_flags" - fi - sketchnum=$(($sketchnum + 1)) - if [ "$sketchnum" -le "$start_index" ] \ - || [ "$sketchnum" -gt "$end_index" ]; then - continue - fi - build_pio_sketch "$board" "$sketch" "$sketchBuildFlags" - local result=$? - if [ $result -ne 0 ]; then - return $result - fi - done - return 0 -} diff --git a/lib/ESPAsyncWebServer/.github/scripts/on-push.sh b/lib/ESPAsyncWebServer/.github/scripts/on-push.sh deleted file mode 100644 index 96fff573..00000000 --- a/lib/ESPAsyncWebServer/.github/scripts/on-push.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -set -e - -if [ ! -z "$TRAVIS_BUILD_DIR" ]; then - export GITHUB_WORKSPACE="$TRAVIS_BUILD_DIR" - export GITHUB_REPOSITORY="$TRAVIS_REPO_SLUG" -elif [ -z "$GITHUB_WORKSPACE" ]; then - export GITHUB_WORKSPACE="$PWD" - export GITHUB_REPOSITORY="me-no-dev/ESPAsyncWebServer" -fi - -TARGET_PLATFORM="$1" -CHUNK_INDEX=$2 -CHUNKS_CNT=$3 -BUILD_PIO=0 -if [ "$#" -lt 1 ]; then - TARGET_PLATFORM="esp32" -fi -if [ "$#" -lt 3 ] || [ "$CHUNKS_CNT" -le 0 ]; then - CHUNK_INDEX=0 - CHUNKS_CNT=1 -elif [ "$CHUNK_INDEX" -gt "$CHUNKS_CNT" ]; then - CHUNK_INDEX=$CHUNKS_CNT -elif [ "$CHUNK_INDEX" -eq "$CHUNKS_CNT" ]; then - BUILD_PIO=1 -fi - -if [ "$BUILD_PIO" -eq 0 ]; then - # ArduinoIDE Test - source ./.github/scripts/install-arduino-ide.sh - - echo "Installing ESPAsyncWebServer ..." - cp -rf "$GITHUB_WORKSPACE" "$ARDUINO_USR_PATH/libraries/ESPAsyncWebServer" - echo "Installing ArduinoJson ..." - git clone https://github.com/bblanchon/ArduinoJson "$ARDUINO_USR_PATH/libraries/ArduinoJson" > /dev/null 2>&1 - - if [[ "$TARGET_PLATFORM" == "esp32" ]]; then - echo "Installing AsyncTCP ..." - git clone https://github.com/me-no-dev/AsyncTCP "$ARDUINO_USR_PATH/libraries/AsyncTCP" > /dev/null 2>&1 - FQBN="espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app" - source ./.github/scripts/install-arduino-core-esp32.sh - echo "BUILDING ESP32 EXAMPLES" - else - echo "Installing ESPAsyncTCP ..." - git clone https://github.com/me-no-dev/ESPAsyncTCP "$ARDUINO_USR_PATH/libraries/ESPAsyncTCP" > /dev/null 2>&1 - FQBN="esp8266com:esp8266:generic:eesz=4M1M,ip=lm2f" - source ./.github/scripts/install-arduino-core-esp8266.sh - echo "BUILDING ESP8266 EXAMPLES" - fi - build_sketches "$FQBN" "$GITHUB_WORKSPACE/examples" "$CHUNK_INDEX" "$CHUNKS_CNT" -else - # PlatformIO Test - source ./.github/scripts/install-platformio.sh - - python -m platformio lib --storage-dir "$GITHUB_WORKSPACE" install - echo "Installing ArduinoJson ..." - python -m platformio lib -g install https://github.com/bblanchon/ArduinoJson.git > /dev/null 2>&1 - if [[ "$TARGET_PLATFORM" == "esp32" ]]; then - BOARD="esp32dev" - echo "Installing AsyncTCP ..." - python -m platformio lib -g install https://github.com/me-no-dev/AsyncTCP.git > /dev/null 2>&1 - echo "BUILDING ESP32 EXAMPLES" - else - BOARD="esp12e" - echo "Installing ESPAsyncTCP ..." - python -m platformio lib -g install https://github.com/me-no-dev/ESPAsyncTCP.git > /dev/null 2>&1 - echo "BUILDING ESP8266 EXAMPLES" - fi - build_pio_sketches "$BOARD" "$GITHUB_WORKSPACE/examples" -fi diff --git a/lib/ESPAsyncWebServer/.github/stale.yml b/lib/ESPAsyncWebServer/.github/stale.yml deleted file mode 100644 index ce7a8e3f..00000000 --- a/lib/ESPAsyncWebServer/.github/stale.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -daysUntilStale: 60 -daysUntilClose: 14 -limitPerRun: 30 -staleLabel: stale -exemptLabels: - - pinned - - security - - "to be implemented" - - "for reference" - - "move to PR" - - "enhancement" - -only: issues -onlyLabels: [] -exemptProjects: false -exemptMilestones: false -exemptAssignees: false - -markComment: > - [STALE_SET] This issue has been automatically marked as stale because it has not had - recent activity. It will be closed in 14 days if no further activity occurs. Thank you - for your contributions. - -unmarkComment: > - [STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future. - -closeComment: > - [STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions. - diff --git a/lib/ESPAsyncWebServer/.github/workflows/push.yml b/lib/ESPAsyncWebServer/.github/workflows/push.yml deleted file mode 100644 index abdea462..00000000 --- a/lib/ESPAsyncWebServer/.github/workflows/push.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: ESP Async Web Server CI - -on: - push: - branches: - - master - - release/* - pull_request: - -jobs: - - build-arduino: - name: Arduino for ${{ matrix.board }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - board: [esp32, esp8266] - steps: - - uses: actions/checkout@v1 - - name: Build Tests - run: bash ./.github/scripts/on-push.sh ${{ matrix.board }} 0 1 - - build-pio: - name: PlatformIO for ${{ matrix.board }} on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - board: [esp32, esp8266] - steps: - - uses: actions/checkout@v1 - - name: Build Tests - run: bash ./.github/scripts/on-push.sh ${{ matrix.board }} 1 1 diff --git a/lib/ESPAsyncWebServer/.gitignore b/lib/ESPAsyncWebServer/.gitignore deleted file mode 100644 index a0f0e538..00000000 --- a/lib/ESPAsyncWebServer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.DS_Store diff --git a/lib/ESPAsyncWebServer/.travis.yml b/lib/ESPAsyncWebServer/.travis.yml deleted file mode 100644 index e1b70352..00000000 --- a/lib/ESPAsyncWebServer/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -sudo: false - -language: python - -os: - - linux - -git: - depth: false - -stages: - - build - -jobs: - include: - - - name: "Build Arduino ESP32" - if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) - stage: build - script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 - - - name: "Build Arduino ESP8266" - if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) - stage: build - script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266 - - - name: "Build Platformio ESP32" - if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) - stage: build - script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 1 1 - - - name: "Build Platformio ESP8266" - if: tag IS blank AND (type = pull_request OR (type = push AND branch = master)) - stage: build - script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266 1 1 - -notifications: - email: - on_success: change - on_failure: change - webhooks: - urls: - - https://webhooks.gitter.im/e/60e65d0c78ea0a920347 - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: never # options: [always|never|change] default: always diff --git a/lib/ESPAsyncWebServer/CMakeLists.txt b/lib/ESPAsyncWebServer/CMakeLists.txt deleted file mode 100644 index 64292eca..00000000 --- a/lib/ESPAsyncWebServer/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(COMPONENT_SRCDIRS - "src" -) - -set(COMPONENT_ADD_INCLUDEDIRS - "src" -) - -set(COMPONENT_REQUIRES - "arduino-esp32" - "AsyncTCP" -) - -register_component() - -target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32) -target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti) diff --git a/lib/ESPAsyncWebServer/README.md b/lib/ESPAsyncWebServer/README.md deleted file mode 100644 index d6dd3206..00000000 --- a/lib/ESPAsyncWebServer/README.md +++ /dev/null @@ -1,1521 +0,0 @@ -# ESPAsyncWebServer -[![Build Status](https://travis-ci.org/me-no-dev/ESPAsyncWebServer.svg?branch=master)](https://travis-ci.org/me-no-dev/ESPAsyncWebServer) ![](https://github.com/me-no-dev/ESPAsyncWebServer/workflows/ESP%20Async%20Web%20Server%20CI/badge.svg) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/395dd42cfc674e6ca2e326af3af80ffc)](https://www.codacy.com/manual/me-no-dev/ESPAsyncWebServer?utm_source=github.com&utm_medium=referral&utm_content=me-no-dev/ESPAsyncWebServer&utm_campaign=Badge_Grade) - -For help and support [![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -Async HTTP and WebSocket Server for ESP8266 Arduino - -For ESP8266 it requires [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) -To use this library you might need to have the latest git versions of [ESP8266](https://github.com/esp8266/Arduino) Arduino Core - -For ESP32 it requires [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) to work -To use this library you might need to have the latest git versions of [ESP32](https://github.com/espressif/arduino-esp32) Arduino Core - -## Table of contents -- [ESPAsyncWebServer](#espasyncwebserver) - - [Table of contents](#table-of-contents) - - [Installation](#installation) - - [Using PlatformIO](#using-platformio) - - [Why should you care](#why-should-you-care) - - [Important things to remember](#important-things-to-remember) - - [Principles of operation](#principles-of-operation) - - [The Async Web server](#the-async-web-server) - - [Request Life Cycle](#request-life-cycle) - - [Rewrites and how do they work](#rewrites-and-how-do-they-work) - - [Handlers and how do they work](#handlers-and-how-do-they-work) - - [Responses and how do they work](#responses-and-how-do-they-work) - - [Template processing](#template-processing) - - [Libraries and projects that use AsyncWebServer](#libraries-and-projects-that-use-asyncwebserver) - - [Request Variables](#request-variables) - - [Common Variables](#common-variables) - - [Headers](#headers) - - [GET, POST and FILE parameters](#get-post-and-file-parameters) - - [FILE Upload handling](#file-upload-handling) - - [Body data handling](#body-data-handling) - - [JSON body handling with ArduinoJson](#json-body-handling-with-arduinojson) - - [Responses](#responses) - - [Redirect to another URL](#redirect-to-another-url) - - [Basic response with HTTP Code](#basic-response-with-http-code) - - [Basic response with HTTP Code and extra headers](#basic-response-with-http-code-and-extra-headers) - - [Basic response with string content](#basic-response-with-string-content) - - [Basic response with string content and extra headers](#basic-response-with-string-content-and-extra-headers) - - [Send large webpage from PROGMEM](#send-large-webpage-from-progmem) - - [Send large webpage from PROGMEM and extra headers](#send-large-webpage-from-progmem-and-extra-headers) - - [Send large webpage from PROGMEM containing templates](#send-large-webpage-from-progmem-containing-templates) - - [Send large webpage from PROGMEM containing templates and extra headers](#send-large-webpage-from-progmem-containing-templates-and-extra-headers) - - [Send binary content from PROGMEM](#send-binary-content-from-progmem) - - [Respond with content coming from a Stream](#respond-with-content-coming-from-a-stream) - - [Respond with content coming from a Stream and extra headers](#respond-with-content-coming-from-a-stream-and-extra-headers) - - [Respond with content coming from a Stream containing templates](#respond-with-content-coming-from-a-stream-containing-templates) - - [Respond with content coming from a Stream containing templates and extra headers](#respond-with-content-coming-from-a-stream-containing-templates-and-extra-headers) - - [Respond with content coming from a File](#respond-with-content-coming-from-a-file) - - [Respond with content coming from a File and extra headers](#respond-with-content-coming-from-a-file-and-extra-headers) - - [Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates) - - [Respond with content using a callback](#respond-with-content-using-a-callback) - - [Respond with content using a callback and extra headers](#respond-with-content-using-a-callback-and-extra-headers) - - [Respond with content using a callback containing templates](#respond-with-content-using-a-callback-containing-templates) - - [Respond with content using a callback containing templates and extra headers](#respond-with-content-using-a-callback-containing-templates-and-extra-headers) - - [Chunked Response](#chunked-response) - - [Chunked Response containing templates](#chunked-response-containing-templates) - - [Print to response](#print-to-response) - - [ArduinoJson Basic Response](#arduinojson-basic-response) - - [ArduinoJson Advanced Response](#arduinojson-advanced-response) - - [Serving static files](#serving-static-files) - - [Serving specific file by name](#serving-specific-file-by-name) - - [Serving files in directory](#serving-files-in-directory) - - [Serving static files with authentication](#serving-static-files-with-authentication) - - [Specifying Cache-Control header](#specifying-cache-control-header) - - [Specifying Date-Modified header](#specifying-date-modified-header) - - [Specifying Template Processor callback](#specifying-template-processor-callback) - - [Param Rewrite With Matching](#param-rewrite-with-matching) - - [Using filters](#using-filters) - - [Serve different site files in AP mode](#serve-different-site-files-in-ap-mode) - - [Rewrite to different index on AP](#rewrite-to-different-index-on-ap) - - [Serving different hosts](#serving-different-hosts) - - [Determine interface inside callbacks](#determine-interface-inside-callbacks) - - [Bad Responses](#bad-responses) - - [Respond with content using a callback without content length to HTTP/1.0 clients](#respond-with-content-using-a-callback-without-content-length-to-http10-clients) - - [Async WebSocket Plugin](#async-websocket-plugin) - - [Async WebSocket Event](#async-websocket-event) - - [Methods for sending data to a socket client](#methods-for-sending-data-to-a-socket-client) - - [Direct access to web socket message buffer](#direct-access-to-web-socket-message-buffer) - - [Limiting the number of web socket clients](#limiting-the-number-of-web-socket-clients) - - [Async Event Source Plugin](#async-event-source-plugin) - - [Setup Event Source on the server](#setup-event-source-on-the-server) - - [Setup Event Source in the browser](#setup-event-source-in-the-browser) - - [Scanning for available WiFi Networks](#scanning-for-available-wifi-networks) - - [Remove handlers and rewrites](#remove-handlers-and-rewrites) - - [Setting up the server](#setting-up-the-server) - - [Setup global and class functions as request handlers](#setup-global-and-class-functions-as-request-handlers) - - [Methods for controlling websocket connections](#methods-for-controlling-websocket-connections) - - [Adding Default Headers](#adding-default-headers) - - [Path variable](#path-variable) - -## Installation - -### Using PlatformIO - -[PlatformIO](http://platformio.org) is an open source ecosystem for IoT development with cross platform build system, library manager and full support for Espressif ESP8266/ESP32 development. It works on the popular host OS: Mac OS X, Windows, Linux 32/64, Linux ARM (like Raspberry Pi, BeagleBone, CubieBoard). - -1. Install [PlatformIO IDE](http://platformio.org/platformio-ide) -2. Create new project using "PlatformIO Home > New Project" -3. Update dev/platform to staging version: - - [Instruction for Espressif 8266](http://docs.platformio.org/en/latest/platforms/espressif8266.html#using-arduino-framework-with-staging-version) - - [Instruction for Espressif 32](http://docs.platformio.org/en/latest/platforms/espressif32.html#using-arduino-framework-with-staging-version) - 4. Add "ESP Async WebServer" to project using [Project Configuration File `platformio.ini`](http://docs.platformio.org/page/projectconf.html) and [lib_deps](http://docs.platformio.org/page/projectconf/section_env_library.html#lib-deps) option: - -```ini -[env:myboard] -platform = espressif... -board = ... -framework = arduino - -# using the latest stable version -lib_deps = ESP Async WebServer - -# or using GIT Url (the latest development version) -lib_deps = https://github.com/me-no-dev/ESPAsyncWebServer.git -``` - 5. Happy coding with PlatformIO! - -## Why should you care -- Using asynchronous network means that you can handle more than one connection at the same time -- You are called once the request is ready and parsed -- When you send the response, you are immediately ready to handle other connections - while the server is taking care of sending the response in the background -- Speed is OMG -- Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse -- Easily extendible to handle any type of content -- Supports Continue 100 -- Async WebSocket plugin offering different locations without extra servers or ports -- Async EventSource (Server-Sent Events) plugin to send events to the browser -- URL Rewrite plugin for conditional and permanent url rewrites -- ServeStatic plugin that supports cache, Last-Modified, default index and more -- Simple template processing engine to handle templates - -## Important things to remember -- This is fully asynchronous server and as such does not run on the loop thread. -- You can not use yield or delay or any function that uses them inside the callbacks -- The server is smart enough to know when to close the connection and free resources -- You can not send more than one response to a single request - -## Principles of operation - -### The Async Web server -- Listens for connections -- Wraps the new clients into ```Request``` -- Keeps track of clients and cleans memory -- Manages ```Rewrites``` and apply them on the request url -- Manages ```Handlers``` and attaches them to Requests - -### Request Life Cycle -- TCP connection is received by the server -- The connection is wrapped inside ```Request``` object -- When the request head is received (type, url, get params, http version and host), - the server goes through all ```Rewrites``` (in the order they were added) to rewrite the url and inject query parameters, - next, it goes through all attached ```Handlers```(in the order they were added) trying to find one - that ```canHandle``` the given request. If none are found, the default(catch-all) handler is attached. -- The rest of the request is received, calling the ```handleUpload``` or ```handleBody``` methods of the ```Handler``` if they are needed (POST+File/Body) -- When the whole request is parsed, the result is given to the ```handleRequest``` method of the ```Handler``` and is ready to be responded to -- In the ```handleRequest``` method, to the ```Request``` is attached a ```Response``` object (see below) that will serve the response data back to the client -- When the ```Response``` is sent, the client is closed and freed from the memory - -### Rewrites and how do they work -- The ```Rewrites``` are used to rewrite the request url and/or inject get parameters for a specific request url path. -- All ```Rewrites``` are evaluated on the request in the order they have been added to the server. -- The ```Rewrite``` will change the request url only if the request url (excluding get parameters) is fully match - the rewrite url, and when the optional ```Filter``` callback return true. -- Setting a ```Filter``` to the ```Rewrite``` enables to control when to apply the rewrite, decision can be based on - request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP. -- Two filter callbacks are provided: ```ON_AP_FILTER``` to execute the rewrite when request is made to the AP interface, - ```ON_STA_FILTER``` to execute the rewrite when request is made to the STA interface. -- The ```Rewrite``` can specify a target url with optional get parameters, e.g. ```/to-url?with=params``` - -### Handlers and how do they work -- The ```Handlers``` are used for executing specific actions to particular requests -- One ```Handler``` instance can be attached to any request and lives together with the server -- Setting a ```Filter``` to the ```Handler``` enables to control when to apply the handler, decision can be based on - request url, http version, request host/port/target host, get parameters or the request client's localIP or remoteIP. -- Two filter callbacks are provided: ```ON_AP_FILTER``` to execute the rewrite when request is made to the AP interface, - ```ON_STA_FILTER``` to execute the rewrite when request is made to the STA interface. -- The ```canHandle``` method is used for handler specific control on whether the requests can be handled - and for declaring any interesting headers that the ```Request``` should parse. Decision can be based on request - method, request url, http version, request host/port/target host and get parameters -- Once a ```Handler``` is attached to given ```Request``` (```canHandle``` returned true) - that ```Handler``` takes care to receive any file/data upload and attach a ```Response``` - once the ```Request``` has been fully parsed -- ```Handlers``` are evaluated in the order they are attached to the server. The ```canHandle``` is called only - if the ```Filter``` that was set to the ```Handler``` return true. -- The first ```Handler``` that can handle the request is selected, not further ```Filter``` and ```canHandle``` are called. - -### Responses and how do they work -- The ```Response``` objects are used to send the response data back to the client -- The ```Response``` object lives with the ```Request``` and is freed on end or disconnect -- Different techniques are used depending on the response type to send the data in packets - returning back almost immediately and sending the next packet when this one is received. - Any time in between is spent to run the user loop and handle other network packets -- Responding asynchronously is probably the most difficult thing for most to understand -- Many different options exist for the user to make responding a background task - -### Template processing -- ESPAsyncWebserver contains simple template processing engine. -- Template processing can be added to most response types. -- Currently it supports only replacing template placeholders with actual values. No conditional processing, cycles, etc. -- Placeholders are delimited with ```%``` symbols. Like this: ```%TEMPLATE_PLACEHOLDER%```. -- It works by extracting placeholder name from response text and passing it to user provided function which should return actual value to be used instead of placeholder. -- Since it's user provided function, it is possible for library users to implement conditional processing and cycles themselves. -- Since it's impossible to know the actual response size after template processing step in advance (and, therefore, to include it in response headers), the response becomes [chunked](#chunked-response). - -## Libraries and projects that use AsyncWebServer -- [WebSocketToSerial](https://github.com/hallard/WebSocketToSerial) - Debug serial devices through the web browser -- [Sattrack](https://github.com/Hopperpop/Sattrack) - Track the ISS with ESP8266 -- [ESP Radio](https://github.com/Edzelf/Esp-radio) - Icecast radio based on ESP8266 and VS1053 -- [VZero](https://github.com/andig/vzero) - the Wireless zero-config controller for volkszaehler.org -- [ESPurna](https://bitbucket.org/xoseperez/espurna) - ESPurna ("spark" in Catalan) is a custom C firmware for ESP8266 based smart switches. It was originally developed with the ITead Sonoff in mind. -- [fauxmoESP](https://bitbucket.org/xoseperez/fauxmoesp) - Belkin WeMo emulator library for ESP8266. -- [ESP-RFID](https://github.com/omersiar/esp-rfid) - MFRC522 RFID Access Control Management project for ESP8266. - -## Request Variables - -### Common Variables -```cpp -request->version(); // uint8_t: 0 = HTTP/1.0, 1 = HTTP/1.1 -request->method(); // enum: HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS -request->url(); // String: URL of the request (not including host, port or GET parameters) -request->host(); // String: The requested host (can be used for virtual hosting) -request->contentType(); // String: ContentType of the request (not avaiable in Handler::canHandle) -request->contentLength(); // size_t: ContentLength of the request (not avaiable in Handler::canHandle) -request->multipart(); // bool: True if the request has content type "multipart" -``` - -### Headers -```cpp -//List all collected headers -int headers = request->headers(); -int i; -for(i=0;igetHeader(i); - Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); -} - -//get specific header by name -if(request->hasHeader("MyHeader")){ - AsyncWebHeader* h = request->getHeader("MyHeader"); - Serial.printf("MyHeader: %s\n", h->value().c_str()); -} - -//List all collected headers (Compatibility) -int headers = request->headers(); -int i; -for(i=0;iheaderName(i).c_str(), request->header(i).c_str()); -} - -//get specific header by name (Compatibility) -if(request->hasHeader("MyHeader")){ - Serial.printf("MyHeader: %s\n", request->header("MyHeader").c_str()); -} -``` - -### GET, POST and FILE parameters -```cpp -//List all parameters -int params = request->params(); -for(int i=0;igetParam(i); - if(p->isFile()){ //p->isPost() is also true - Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); - } else if(p->isPost()){ - Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } else { - Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } -} - -//Check if GET parameter exists -if(request->hasParam("download")) - AsyncWebParameter* p = request->getParam("download"); - -//Check if POST (but not File) parameter exists -if(request->hasParam("download", true)) - AsyncWebParameter* p = request->getParam("download", true); - -//Check if FILE was uploaded -if(request->hasParam("download", true, true)) - AsyncWebParameter* p = request->getParam("download", true, true); - -//List all parameters (Compatibility) -int args = request->args(); -for(int i=0;iargName(i).c_str(), request->arg(i).c_str()); -} - -//Check if parameter exists (Compatibility) -if(request->hasArg("download")) - String arg = request->arg("download"); -``` - -### FILE Upload handling -```cpp -void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ - if(!index){ - Serial.printf("UploadStart: %s\n", filename.c_str()); - } - for(size_t i=0; i(); - // ... -}); -server.addHandler(handler); -``` - -## Responses -### Redirect to another URL -```cpp -//to local url -request->redirect("/login"); - -//to external url -request->redirect("http://esp8266.com"); -``` - -### Basic response with HTTP Code -```cpp -request->send(404); //Sends 404 File Not Found -``` - -### Basic response with HTTP Code and extra headers -```cpp -AsyncWebServerResponse *response = request->beginResponse(404); //Sends 404 File Not Found -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Basic response with string content -```cpp -request->send(200, "text/plain", "Hello World!"); -``` - -### Basic response with string content and extra headers -```cpp -AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!"); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Send large webpage from PROGMEM -```cpp -const char index_html[] PROGMEM = "..."; // large char array, tested with 14k -request->send_P(200, "text/html", index_html); -``` - -### Send large webpage from PROGMEM and extra headers -```cpp -const char index_html[] PROGMEM = "..."; // large char array, tested with 14k -AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Send large webpage from PROGMEM containing templates -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -const char index_html[] PROGMEM = "..."; // large char array, tested with 14k -request->send_P(200, "text/html", index_html, processor); -``` - -### Send large webpage from PROGMEM containing templates and extra headers -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -const char index_html[] PROGMEM = "..."; // large char array, tested with 14k -AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", index_html, processor); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Send binary content from PROGMEM -```cpp - -//File: favicon.ico.gz, Size: 726 -#define favicon_ico_gz_len 726 -const uint8_t favicon_ico_gz[] PROGMEM = { - 0x1F, 0x8B, 0x08, 0x08, 0x0B, 0x87, 0x90, 0x57, 0x00, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6F, - 0x6E, 0x2E, 0x69, 0x63, 0x6F, 0x00, 0xCD, 0x53, 0x5F, 0x48, 0x9A, 0x51, 0x14, 0xBF, 0x62, 0x6D, - 0x86, 0x96, 0xA9, 0x64, 0xD3, 0xFE, 0xA8, 0x99, 0x65, 0x1A, 0xB4, 0x8A, 0xA8, 0x51, 0x54, 0x23, - 0xA8, 0x11, 0x49, 0x51, 0x8A, 0x34, 0x62, 0x93, 0x85, 0x31, 0x58, 0x44, 0x12, 0x45, 0x2D, 0x58, - 0xF5, 0x52, 0x41, 0x10, 0x23, 0x82, 0xA0, 0x20, 0x98, 0x2F, 0xC1, 0x26, 0xED, 0xA1, 0x20, 0x89, - 0x04, 0xD7, 0x83, 0x58, 0x20, 0x28, 0x04, 0xAB, 0xD1, 0x9B, 0x8C, 0xE5, 0xC3, 0x60, 0x32, 0x64, - 0x0E, 0x56, 0xBF, 0x9D, 0xEF, 0xF6, 0x30, 0x82, 0xED, 0xAD, 0x87, 0xDD, 0x8F, 0xF3, 0xDD, 0x8F, - 0x73, 0xCF, 0xEF, 0x9C, 0xDF, 0x39, 0xBF, 0xFB, 0x31, 0x26, 0xA2, 0x27, 0x37, 0x97, 0xD1, 0x5B, - 0xCF, 0x9E, 0x67, 0x30, 0xA6, 0x66, 0x8C, 0x99, 0xC9, 0xC8, 0x45, 0x9E, 0x6B, 0x3F, 0x5F, 0x74, - 0xA6, 0x94, 0x5E, 0xDB, 0xFF, 0xB2, 0xE6, 0xE7, 0xE7, 0xF9, 0xDE, 0xD6, 0xD6, 0x96, 0xDB, 0xD8, - 0xD8, 0x78, 0xBF, 0xA1, 0xA1, 0xC1, 0xDA, 0xDC, 0xDC, 0x2C, 0xEB, 0xED, 0xED, 0x15, 0x9B, 0xCD, - 0xE6, 0x4A, 0x83, 0xC1, 0xE0, 0x2E, 0x29, 0x29, 0x99, 0xD6, 0x6A, 0xB5, 0x4F, 0x75, 0x3A, 0x9D, - 0x61, 0x75, 0x75, 0x95, 0xB5, 0xB7, 0xB7, 0xDF, 0xC8, 0xD1, 0xD4, 0xD4, 0xF4, 0xB0, 0xBA, 0xBA, - 0xFA, 0x83, 0xD5, 0x6A, 0xFD, 0x5A, 0x5E, 0x5E, 0x9E, 0x28, 0x2D, 0x2D, 0x0D, 0x10, 0xC6, 0x4B, - 0x98, 0x78, 0x5E, 0x5E, 0xDE, 0x95, 0x42, 0xA1, 0x40, 0x4E, 0x4E, 0xCE, 0x65, 0x76, 0x76, 0xF6, - 0x47, 0xB5, 0x5A, 0x6D, 0x4F, 0x26, 0x93, 0xA2, 0xD6, 0xD6, 0x56, 0x8E, 0x6D, 0x69, 0x69, 0xD1, - 0x11, 0x36, 0x62, 0xB1, 0x58, 0x60, 0x32, 0x99, 0xA0, 0xD7, 0xEB, 0x51, 0x58, 0x58, 0x88, 0xFC, - 0xFC, 0x7C, 0x10, 0x16, 0x02, 0x56, 0x2E, 0x97, 0x43, 0x2A, 0x95, 0x42, 0x2C, 0x16, 0x23, 0x33, - 0x33, 0x33, 0xAE, 0x52, 0xA9, 0x1E, 0x64, 0x65, 0x65, 0x71, 0x7C, 0x7D, 0x7D, 0xBD, 0x93, 0xEA, - 0xFE, 0x30, 0x1A, 0x8D, 0xE8, 0xEC, 0xEC, 0xC4, 0xE2, 0xE2, 0x22, 0x6A, 0x6A, 0x6A, 0x40, 0x39, - 0x41, 0xB5, 0x38, 0x4E, 0xC8, 0x33, 0x3C, 0x3C, 0x0C, 0x87, 0xC3, 0xC1, 0x6B, 0x54, 0x54, 0x54, - 0xBC, 0xE9, 0xEB, 0xEB, 0x93, 0x5F, 0x5C, 0x5C, 0x30, 0x8A, 0x9D, 0x2E, 0x2B, 0x2B, 0xBB, 0xA2, - 0x3E, 0x41, 0xBD, 0x21, 0x1E, 0x8F, 0x63, 0x6A, 0x6A, 0x0A, 0x81, 0x40, 0x00, 0x94, 0x1B, 0x3D, - 0x3D, 0x3D, 0x42, 0x3C, 0x96, 0x96, 0x96, 0x70, 0x7E, 0x7E, 0x8E, 0xE3, 0xE3, 0x63, 0xF8, 0xFD, - 0xFE, 0xB4, 0xD7, 0xEB, 0xF5, 0x8F, 0x8F, 0x8F, 0x5B, 0x68, 0x5E, 0x6F, 0x05, 0xCE, 0xB4, 0xE3, - 0xE8, 0xE8, 0x08, 0x27, 0x27, 0x27, 0xD8, 0xDF, 0xDF, 0xC7, 0xD9, 0xD9, 0x19, 0x6C, 0x36, 0x1B, - 0x36, 0x36, 0x36, 0x38, 0x9F, 0x85, 0x85, 0x05, 0xAC, 0xAF, 0xAF, 0x23, 0x1A, 0x8D, 0x22, 0x91, - 0x48, 0x20, 0x16, 0x8B, 0xFD, 0xDA, 0xDA, 0xDA, 0x7A, 0x41, 0x33, 0x7E, 0x57, 0x50, 0x50, 0x80, - 0x89, 0x89, 0x09, 0x84, 0xC3, 0x61, 0x6C, 0x6F, 0x6F, 0x23, 0x12, 0x89, 0xE0, 0xE0, 0xE0, 0x00, - 0x43, 0x43, 0x43, 0x58, 0x5E, 0x5E, 0xE6, 0x9C, 0x7D, 0x3E, 0x1F, 0x46, 0x47, 0x47, 0x79, 0xBE, - 0xBD, 0xBD, 0x3D, 0xE1, 0x3C, 0x1D, 0x0C, 0x06, 0x9F, 0x10, 0xB7, 0xC7, 0x84, 0x4F, 0xF6, 0xF7, - 0xF7, 0x63, 0x60, 0x60, 0x00, 0x83, 0x83, 0x83, 0x18, 0x19, 0x19, 0xC1, 0xDC, 0xDC, 0x1C, 0x8F, - 0x17, 0x7C, 0xA4, 0x27, 0xE7, 0x34, 0x39, 0x39, 0x89, 0x9D, 0x9D, 0x1D, 0x6E, 0x54, 0xE3, 0x13, - 0xE5, 0x34, 0x11, 0x37, 0x49, 0x51, 0x51, 0xD1, 0x4B, 0xA5, 0x52, 0xF9, 0x45, 0x26, 0x93, 0x5D, - 0x0A, 0xF3, 0x92, 0x48, 0x24, 0xA0, 0x6F, 0x14, 0x17, 0x17, 0xA3, 0xB6, 0xB6, 0x16, 0x5D, 0x5D, - 0x5D, 0x7C, 0x1E, 0xBB, 0xBB, 0xBB, 0x9C, 0xD7, 0xE1, 0xE1, 0x21, 0x42, 0xA1, 0xD0, 0x6B, 0xD2, - 0x45, 0x4C, 0x33, 0x12, 0x34, 0xCC, 0xA0, 0x19, 0x54, 0x92, 0x56, 0x0E, 0xD2, 0xD9, 0x43, 0xF8, - 0xCF, 0x82, 0x56, 0xC2, 0xDC, 0xEB, 0xEA, 0xEA, 0x38, 0x7E, 0x6C, 0x6C, 0x4C, 0xE0, 0xFE, 0x9D, - 0xB8, 0xBF, 0xA7, 0xFA, 0xAF, 0x56, 0x56, 0x56, 0xEE, 0x6D, 0x6E, 0x6E, 0xDE, 0xB8, 0x47, 0x55, - 0x55, 0x55, 0x6C, 0x66, 0x66, 0x46, 0x44, 0xDA, 0x3B, 0x34, 0x1A, 0x4D, 0x94, 0xB0, 0x3F, 0x09, - 0x7B, 0x45, 0xBD, 0xA5, 0x5D, 0x2E, 0x57, 0x8C, 0x7A, 0x73, 0xD9, 0xED, 0xF6, 0x3B, 0x84, 0xFF, - 0xE7, 0x7D, 0xA6, 0x3A, 0x2C, 0x95, 0x4A, 0xB1, 0x8E, 0x8E, 0x0E, 0x6D, 0x77, 0x77, 0xB7, 0xCD, - 0xE9, 0x74, 0x3E, 0x73, 0xBB, 0xDD, 0x8F, 0x3C, 0x1E, 0x8F, 0xE6, 0xF4, 0xF4, 0x94, 0xAD, 0xAD, - 0xAD, 0xDD, 0xDE, 0xCF, 0x73, 0x0B, 0x0B, 0xB8, 0xB6, 0xE0, 0x5D, 0xC6, 0x66, 0xC5, 0xE4, 0x10, - 0x4C, 0xF4, 0xF7, 0xD8, 0x59, 0xF2, 0x7F, 0xA3, 0xB8, 0xB4, 0xFC, 0x0F, 0xEE, 0x37, 0x70, 0xEC, - 0x16, 0x4A, 0x7E, 0x04, 0x00, 0x00 -}; - -AsyncWebServerResponse *response = request->beginResponse_P(200, "image/x-icon", favicon_ico_gz, favicon_ico_gz_len); -response->addHeader("Content-Encoding", "gzip"); -request->send(response); -``` - -### Respond with content coming from a Stream -```cpp -//read 12 bytes from Serial and send them as Content Type text/plain -request->send(Serial, "text/plain", 12); -``` - -### Respond with content coming from a Stream and extra headers -```cpp -//read 12 bytes from Serial and send them as Content Type text/plain -AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Respond with content coming from a Stream containing templates -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -//read 12 bytes from Serial and send them as Content Type text/plain -request->send(Serial, "text/plain", 12, processor); -``` - -### Respond with content coming from a Stream containing templates and extra headers -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -//read 12 bytes from Serial and send them as Content Type text/plain -AsyncWebServerResponse *response = request->beginResponse(Serial, "text/plain", 12, processor); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Respond with content coming from a File -```cpp -//Send index.htm with default content type -request->send(SPIFFS, "/index.htm"); - -//Send index.htm as text -request->send(SPIFFS, "/index.htm", "text/plain"); - -//Download index.htm -request->send(SPIFFS, "/index.htm", String(), true); -``` - -### Respond with content coming from a File and extra headers -```cpp -//Send index.htm with default content type -AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm"); - -//Send index.htm as text -AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", "text/plain"); - -//Download index.htm -AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/index.htm", String(), true); - -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Respond with content coming from a File containing templates -Internally uses [Chunked Response](#chunked-response). - -Index.htm contents: -``` -%HELLO_FROM_TEMPLATE% -``` - -Somewhere in source files: -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -//Send index.htm with template processor function -request->send(SPIFFS, "/index.htm", String(), false, processor); -``` - -### Respond with content using a callback -```cpp -//send 128 bytes as plain text -request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will not be asked for more bytes once the content length has been reached. - //Keep in mind that you can not delay or yield waiting for more data! - //Send what you currently have and you will be asked for more again - return mySource.read(buffer, maxLen); -}); -``` - -### Respond with content using a callback and extra headers -```cpp -//send 128 bytes as plain text -AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will not be asked for more bytes once the content length has been reached. - //Keep in mind that you can not delay or yield waiting for more data! - //Send what you currently have and you will be asked for more again - return mySource.read(buffer, maxLen); -}); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Respond with content using a callback containing templates -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -//send 128 bytes as plain text -request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will not be asked for more bytes once the content length has been reached. - //Keep in mind that you can not delay or yield waiting for more data! - //Send what you currently have and you will be asked for more again - return mySource.read(buffer, maxLen); -}, processor); -``` - -### Respond with content using a callback containing templates and extra headers -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -//send 128 bytes as plain text -AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will not be asked for more bytes once the content length has been reached. - //Keep in mind that you can not delay or yield waiting for more data! - //Send what you currently have and you will be asked for more again - return mySource.read(buffer, maxLen); -}, processor); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Chunked Response -Used when content length is unknown. Works best if the client supports HTTP/1.1 -```cpp -AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will be asked for more data until 0 is returned - //Keep in mind that you can not delay or yield waiting for more data! - return mySource.read(buffer, maxLen); -}); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Chunked Response containing templates -Used when content length is unknown. Works best if the client supports HTTP/1.1 -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //index equals the amount of bytes that have been already sent - //You will be asked for more data until 0 is returned - //Keep in mind that you can not delay or yield waiting for more data! - return mySource.read(buffer, maxLen); -}, processor); -response->addHeader("Server","ESP Async Web Server"); -request->send(response); -``` - -### Print to response -```cpp -AsyncResponseStream *response = request->beginResponseStream("text/html"); -response->addHeader("Server","ESP Async Web Server"); -response->printf("Webpage at %s", request->url().c_str()); - -response->print("

Hello "); -response->print(request->client()->remoteIP()); -response->print("

"); - -response->print("

General

"); -response->print("
    "); -response->printf("
  • Version: HTTP/1.%u
  • ", request->version()); -response->printf("
  • Method: %s
  • ", request->methodToString()); -response->printf("
  • URL: %s
  • ", request->url().c_str()); -response->printf("
  • Host: %s
  • ", request->host().c_str()); -response->printf("
  • ContentType: %s
  • ", request->contentType().c_str()); -response->printf("
  • ContentLength: %u
  • ", request->contentLength()); -response->printf("
  • Multipart: %s
  • ", request->multipart()?"true":"false"); -response->print("
"); - -response->print("

Headers

"); -response->print("
    "); -int headers = request->headers(); -for(int i=0;igetHeader(i); - response->printf("
  • %s: %s
  • ", h->name().c_str(), h->value().c_str()); -} -response->print("
"); - -response->print("

Parameters

"); -response->print("
    "); -int params = request->params(); -for(int i=0;igetParam(i); - if(p->isFile()){ - response->printf("
  • FILE[%s]: %s, size: %u
  • ", p->name().c_str(), p->value().c_str(), p->size()); - } else if(p->isPost()){ - response->printf("
  • POST[%s]: %s
  • ", p->name().c_str(), p->value().c_str()); - } else { - response->printf("
  • GET[%s]: %s
  • ", p->name().c_str(), p->value().c_str()); - } -} -response->print("
"); - -response->print(""); -//send the response last -request->send(response); -``` - -### ArduinoJson Basic Response -This way of sending Json is great for when the result is below 4KB -```cpp -#include "AsyncJson.h" -#include "ArduinoJson.h" - - -AsyncResponseStream *response = request->beginResponseStream("application/json"); -DynamicJsonBuffer jsonBuffer; -JsonObject &root = jsonBuffer.createObject(); -root["heap"] = ESP.getFreeHeap(); -root["ssid"] = WiFi.SSID(); -root.printTo(*response); -request->send(response); -``` - -### ArduinoJson Advanced Response -This response can handle really large Json objects (tested to 40KB) -There isn't any noticeable speed decrease for small results with the method above -Since ArduinoJson does not allow reading parts of the string, the whole Json has to -be passed every time a chunks needs to be sent, which shows speed decrease proportional -to the resulting json packets -```cpp -#include "AsyncJson.h" -#include "ArduinoJson.h" - - -AsyncJsonResponse * response = new AsyncJsonResponse(); -response->addHeader("Server","ESP Async Web Server"); -JsonObject& root = response->getRoot(); -root["heap"] = ESP.getFreeHeap(); -root["ssid"] = WiFi.SSID(); -response->setLength(); -request->send(response); -``` - -## Serving static files -In addition to serving files from SPIFFS as described above, the server provide a dedicated handler that optimize the -performance of serving files from SPIFFS - ```AsyncStaticWebHandler```. Use ```server.serveStatic()``` function to -initialize and add a new instance of ```AsyncStaticWebHandler``` to the server. -The Handler will not handle the request if the file does not exists, e.g. the server will continue to look for another -handler that can handle the request. -Notice that you can chain setter functions to setup the handler, or keep a pointer to change it at a later time. - -### Serving specific file by name -```cpp -// Serve the file "/www/page.htm" when request url is "/page.htm" -server.serveStatic("/page.htm", SPIFFS, "/www/page.htm"); -``` - -### Serving files in directory -To serve files in a directory, the path to the files should specify a directory in SPIFFS and ends with "/". -```cpp -// Serve files in directory "/www/" when request url starts with "/" -// Request to the root or none existing files will try to server the defualt -// file name "index.htm" if exists -server.serveStatic("/", SPIFFS, "/www/"); - -// Server with different default file -server.serveStatic("/", SPIFFS, "/www/").setDefaultFile("default.html"); -``` - -### Serving static files with authentication - -```cpp -server - .serveStatic("/", SPIFFS, "/www/") - .setDefaultFile("default.html") - .setAuthentication("user", "pass"); -``` - -### Specifying Cache-Control header -It is possible to specify Cache-Control header value to reduce the number of calls to the server once the client loaded -the files. For more information on Cache-Control values see [Cache-Control](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) -```cpp -// Cache responses for 10 minutes (600 seconds) -server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600"); - -//*** Change Cache-Control after server setup *** - -// During setup - keep a pointer to the handler -AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=600"); - -// At a later event - change Cache-Control -handler->setCacheControl("max-age=30"); -``` - -### Specifying Date-Modified header -It is possible to specify Date-Modified header to enable the server to return Not-Modified (304) response for requests -with "If-Modified-Since" header with the same value, instead of responding with the actual file content. -```cpp -// Update the date modified string every time files are updated -server.serveStatic("/", SPIFFS, "/www/").setLastModified("Mon, 20 Jun 2016 14:00:00 GMT"); - -//*** Chage last modified value at a later stage *** - -// During setup - read last modified value from config or EEPROM -String date_modified = loadDateModified(); -AsyncStaticWebHandler* handler = &server.serveStatic("/", SPIFFS, "/www/"); -handler->setLastModified(date_modified); - -// At a later event when files are updated -String date_modified = getNewDateModfied(); -saveDateModified(date_modified); // Save for next reset -handler->setLastModified(date_modified); -``` - -### Specifying Template Processor callback -It is possible to specify template processor for static files. For information on template processor see -[Respond with content coming from a File containing templates](#respond-with-content-coming-from-a-file-containing-templates). -```cpp -String processor(const String& var) -{ - if(var == "HELLO_FROM_TEMPLATE") - return F("Hello world!"); - return String(); -} - -// ... - -server.serveStatic("/", SPIFFS, "/www/").setTemplateProcessor(processor); -``` - -## Param Rewrite With Matching -It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter: -Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}" - -```cpp -class OneParamRewrite : public AsyncWebRewrite -{ - protected: - String _urlPrefix; - int _paramIndex; - String _paramsBackup; - - public: - OneParamRewrite(const char* from, const char* to) - : AsyncWebRewrite(from, to) { - - _paramIndex = _from.indexOf('{'); - - if( _paramIndex >=0 && _from.endsWith("}")) { - _urlPrefix = _from.substring(0, _paramIndex); - int index = _params.indexOf('{'); - if(index >= 0) { - _params = _params.substring(0, index); - } - } else { - _urlPrefix = _from; - } - _paramsBackup = _params; - } - - bool match(AsyncWebServerRequest *request) override { - if(request->url().startsWith(_urlPrefix)) { - if(_paramIndex >= 0) { - _params = _paramsBackup + request->url().substring(_paramIndex); - } else { - _params = _paramsBackup; - } - return true; - - } else { - return false; - } - } -}; -``` - -Usage: - -```cpp - server.addRewrite( new OneParamRewrite("/radio/{frequence}", "/radio?f={frequence}") ); -``` - -## Using filters -Filters can be set to `Rewrite` or `Handler` in order to control when to apply the rewrite and consider the handler. -A filter is a callback function that evaluates the request and return a boolean `true` to include the item -or `false` to exclude it. -Two filter callback are provided for convince: -* `ON_STA_FILTER` - return true when requests are made to the STA (station mode) interface. -* `ON_AP_FILTER` - return true when requests are made to the AP (access point) interface. - -### Serve different site files in AP mode -```cpp -server.serveStatic("/", SPIFFS, "/www/").setFilter(ON_STA_FILTER); -server.serveStatic("/", SPIFFS, "/ap/").setFilter(ON_AP_FILTER); -``` - -### Rewrite to different index on AP -```cpp -// Serve the file "/www/index-ap.htm" in AP, and the file "/www/index.htm" on STA -server.rewrite("/", "index.htm"); -server.rewrite("/index.htm", "index-ap.htm").setFilter(ON_AP_FILTER); -server.serveStatic("/", SPIFFS, "/www/"); -``` - -### Serving different hosts -```cpp -// Filter callback using request host -bool filterOnHost1(AsyncWebServerRequest *request) { return request->host() == "host1"; } - -// Server setup: server files in "/host1/" to requests for "host1", and files in "/www/" otherwise. -server.serveStatic("/", SPIFFS, "/host1/").setFilter(filterOnHost1); -server.serveStatic("/", SPIFFS, "/www/"); -``` - -### Determine interface inside callbacks -```cpp - String RedirectUrl = "http://"; - if (ON_STA_FILTER(request)) { - RedirectUrl += WiFi.localIP().toString(); - } else { - RedirectUrl += WiFi.softAPIP().toString(); - } - RedirectUrl += "/index.htm"; - request->redirect(RedirectUrl); -``` - -## Bad Responses -Some responses are implemented, but you should not use them, because they do not conform to HTTP. -The following example will lead to unclean close of the connection and more time wasted -than providing the length of the content - -### Respond with content using a callback without content length to HTTP/1.0 clients -```cpp -//This is used as fallback for chunked responses to HTTP/1.0 Clients -request->send("text/plain", 0, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { - //Write up to "maxLen" bytes into "buffer" and return the amount written. - //You will be asked for more data until 0 is returned - //Keep in mind that you can not delay or yield waiting for more data! - return mySource.read(buffer, maxLen); -}); -``` - -## Async WebSocket Plugin -The server includes a web socket plugin which lets you define different WebSocket locations to connect to -without starting another listening service or using different port - -### Async WebSocket Event -```cpp - -void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - if(type == WS_EVT_CONNECT){ - //client connected - os_printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf("Hello Client %u :)", client->id()); - client->ping(); - } else if(type == WS_EVT_DISCONNECT){ - //client disconnected - os_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id()); - } else if(type == WS_EVT_ERROR){ - //error was received from the other end - os_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } else if(type == WS_EVT_PONG){ - //pong message was received (in response to a ping request maybe) - os_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:""); - } else if(type == WS_EVT_DATA){ - //data packet - AwsFrameInfo * info = (AwsFrameInfo*)arg; - if(info->final && info->index == 0 && info->len == len){ - //the whole message is in a single frame and we got all of it's data - os_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); - if(info->opcode == WS_TEXT){ - data[len] = 0; - os_printf("%s\n", (char*)data); - } else { - for(size_t i=0; i < info->len; i++){ - os_printf("%02x ", data[i]); - } - os_printf("\n"); - } - if(info->opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } else { - //message is comprised of multiple frames or the frame is split into multiple packets - if(info->index == 0){ - if(info->num == 0) - os_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); - os_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - os_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); - if(info->message_opcode == WS_TEXT){ - data[len] = 0; - os_printf("%s\n", (char*)data); - } else { - for(size_t i=0; i < len; i++){ - os_printf("%02x ", data[i]); - } - os_printf("\n"); - } - - if((info->index + len) == info->len){ - os_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - if(info->final){ - os_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); - if(info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } -} -``` - -### Methods for sending data to a socket client -```cpp - - - -//Server methods -AsyncWebSocket ws("/ws"); -//printf to a client -ws.printf((uint32_t)client_id, arguments...); -//printf to all clients -ws.printfAll(arguments...); -//printf_P to a client -ws.printf_P((uint32_t)client_id, PSTR(format), arguments...); -//printfAll_P to all clients -ws.printfAll_P(PSTR(format), arguments...); -//send text to a client -ws.text((uint32_t)client_id, (char*)text); -ws.text((uint32_t)client_id, (uint8_t*)text, (size_t)len); -//send text from PROGMEM to a client -ws.text((uint32_t)client_id, PSTR("text")); -const char flash_text[] PROGMEM = "Text to send" -ws.text((uint32_t)client_id, FPSTR(flash_text)); -//send text to all clients -ws.textAll((char*)text); -ws.textAll((uint8_t*)text, (size_t)len); -//send binary to a client -ws.binary((uint32_t)client_id, (char*)binary); -ws.binary((uint32_t)client_id, (uint8_t*)binary, (size_t)len); -//send binary from PROGMEM to a client -const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 }; -ws.binary((uint32_t)client_id, flash_binary, 4); -//send binary to all clients -ws.binaryAll((char*)binary); -ws.binaryAll((uint8_t*)binary, (size_t)len); -//HTTP Authenticate before switch to Websocket protocol -ws.setAuthentication("user", "pass"); - -//client methods -AsyncWebSocketClient * client; -//printf -client->printf(arguments...); -//printf_P -client->printf_P(PSTR(format), arguments...); -//send text -client->text((char*)text); -client->text((uint8_t*)text, (size_t)len); -//send text from PROGMEM -client->text(PSTR("text")); -const char flash_text[] PROGMEM = "Text to send"; -client->text(FPSTR(flash_text)); -//send binary -client->binary((char*)binary); -client->binary((uint8_t*)binary, (size_t)len); -//send binary from PROGMEM -const uint8_t flash_binary[] PROGMEM = { 0x01, 0x02, 0x03, 0x04 }; -client->binary(flash_binary, 4); -``` - -### Direct access to web socket message buffer -When sending a web socket message using the above methods a buffer is created. Under certain circumstances you might want to manipulate or populate this buffer directly from your application, for example to prevent unnecessary duplications of the data. This example below shows how to create a buffer and print data to it from an ArduinoJson object then send it. - -```cpp -void sendDataWs(AsyncWebSocketClient * client) -{ - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - root["a"] = "abc"; - root["b"] = "abcd"; - root["c"] = "abcde"; - root["d"] = "abcdef"; - root["e"] = "abcdefg"; - size_t len = root.measureLength(); - AsyncWebSocketMessageBuffer * buffer = ws.makeBuffer(len); // creates a buffer (len + 1) for you. - if (buffer) { - root.printTo((char *)buffer->get(), len + 1); - if (client) { - client->text(buffer); - } else { - ws.textAll(buffer); - } - } -} -``` - -### Limiting the number of web socket clients -Browsers sometimes do not correctly close the websocket connection, even when the close() function is called in javascript. This will eventually exhaust the web server's resources and will cause the server to crash. Periodically calling the cleanClients() function from the main loop() function limits the number of clients by closing the oldest client when the maximum number of clients has been exceeded. This can called be every cycle, however, if you wish to use less power, then calling as infrequently as once per second is sufficient. - -```cpp -void loop(){ - ws.cleanupClients(); -} -``` - - -## Async Event Source Plugin -The server includes EventSource (Server-Sent Events) plugin which can be used to send short text events to the browser. -Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol. - -### Setup Event Source on the server -```cpp -AsyncWebServer server(80); -AsyncEventSource events("/events"); - -void setup(){ - // setup ...... - events.onConnect([](AsyncEventSourceClient *client){ - if(client->lastId()){ - Serial.printf("Client reconnected! Last message ID that it gat is: %u\n", client->lastId()); - } - //send event with message "hello!", id current millis - // and set reconnect delay to 1 second - client->send("hello!",NULL,millis(),1000); - }); - //HTTP Basic authentication - events.setAuthentication("user", "pass"); - server.addHandler(&events); - // setup ...... -} - -void loop(){ - if(eventTriggered){ // your logic here - //send event "myevent" - events.send("my event content","myevent",millis()); - } -} -``` - -### Setup Event Source in the browser -```javascript -if (!!window.EventSource) { - var source = new EventSource('/events'); - - source.addEventListener('open', function(e) { - console.log("Events Connected"); - }, false); - - source.addEventListener('error', function(e) { - if (e.target.readyState != EventSource.OPEN) { - console.log("Events Disconnected"); - } - }, false); - - source.addEventListener('message', function(e) { - console.log("message", e.data); - }, false); - - source.addEventListener('myevent', function(e) { - console.log("myevent", e.data); - }, false); -} -``` - -## Scanning for available WiFi Networks -```cpp -//First request will return 0 results unless you start scan from somewhere else (loop/setup) -//Do not request more often than 3-5 seconds -server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){ - String json = "["; - int n = WiFi.scanComplete(); - if(n == -2){ - WiFi.scanNetworks(true); - } else if(n){ - for (int i = 0; i < n; ++i){ - if(i) json += ","; - json += "{"; - json += "\"rssi\":"+String(WiFi.RSSI(i)); - json += ",\"ssid\":\""+WiFi.SSID(i)+"\""; - json += ",\"bssid\":\""+WiFi.BSSIDstr(i)+"\""; - json += ",\"channel\":"+String(WiFi.channel(i)); - json += ",\"secure\":"+String(WiFi.encryptionType(i)); - json += ",\"hidden\":"+String(WiFi.isHidden(i)?"true":"false"); - json += "}"; - } - WiFi.scanDelete(); - if(WiFi.scanComplete() == -2){ - WiFi.scanNetworks(true); - } - } - json += "]"; - request->send(200, "application/json", json); - json = String(); -}); -``` - -## Remove handlers and rewrites - -Server goes through handlers in same order as they were added. You can't simple add handler with same path to override them. -To remove handler: -```arduino -// save callback for particular URL path -auto handler = server.on("/some/path", [](AsyncWebServerRequest *request){ - //do something useful -}); -// when you don't need handler anymore remove it -server.removeHandler(&handler); - -// same with rewrites -server.removeRewrite(&someRewrite); - -server.onNotFound([](AsyncWebServerRequest *request){ - request->send(404); -}); - -// remove server.onNotFound handler -server.onNotFound(NULL); - -// remove all rewrites, handlers and onNotFound/onFileUpload/onRequestBody callbacks -server.reset(); -``` - -## Setting up the server -```cpp -#include "ESPAsyncTCP.h" -#include "ESPAsyncWebServer.h" - -AsyncWebServer server(80); -AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws -AsyncEventSource events("/events"); // event source (Server-Sent events) - -const char* ssid = "your-ssid"; -const char* password = "your-pass"; -const char* http_username = "admin"; -const char* http_password = "admin"; - -//flag to use from web update to reboot the ESP -bool shouldReboot = false; - -void onRequest(AsyncWebServerRequest *request){ - //Handle Unknown Request - request->send(404); -} - -void onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ - //Handle body -} - -void onUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ - //Handle upload -} - -void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - //Handle WebSocket event -} - -void setup(){ - Serial.begin(115200); - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - if (WiFi.waitForConnectResult() != WL_CONNECTED) { - Serial.printf("WiFi Failed!\n"); - return; - } - - // attach AsyncWebSocket - ws.onEvent(onEvent); - server.addHandler(&ws); - - // attach AsyncEventSource - server.addHandler(&events); - - // respond to GET requests on URL /heap - server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/plain", String(ESP.getFreeHeap())); - }); - - // upload a file to /upload - server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){ - request->send(200); - }, onUpload); - - // send a file when /index is requested - server.on("/index", HTTP_ANY, [](AsyncWebServerRequest *request){ - request->send(SPIFFS, "/index.htm"); - }); - - // HTTP basic authentication - server.on("/login", HTTP_GET, [](AsyncWebServerRequest *request){ - if(!request->authenticate(http_username, http_password)) - return request->requestAuthentication(); - request->send(200, "text/plain", "Login Success!"); - }); - - // Simple Firmware Update Form - server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/html", "
"); - }); - server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ - shouldReboot = !Update.hasError(); - AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", shouldReboot?"OK":"FAIL"); - response->addHeader("Connection", "close"); - request->send(response); - },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ - if(!index){ - Serial.printf("Update Start: %s\n", filename.c_str()); - Update.runAsync(true); - if(!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)){ - Update.printError(Serial); - } - } - if(!Update.hasError()){ - if(Update.write(data, len) != len){ - Update.printError(Serial); - } - } - if(final){ - if(Update.end(true)){ - Serial.printf("Update Success: %uB\n", index+len); - } else { - Update.printError(Serial); - } - } - }); - - // attach filesystem root at URL /fs - server.serveStatic("/fs", SPIFFS, "/"); - - // Catch-All Handlers - // Any request that can not find a Handler that canHandle it - // ends in the callbacks below. - server.onNotFound(onRequest); - server.onFileUpload(onUpload); - server.onRequestBody(onBody); - - server.begin(); -} - -void loop(){ - if(shouldReboot){ - Serial.println("Rebooting..."); - delay(100); - ESP.restart(); - } - static char temp[128]; - sprintf(temp, "Seconds since boot: %u", millis()/1000); - events.send(temp, "time"); //send event "time" -} -``` - -### Setup global and class functions as request handlers - -```cpp -#include -#include -#include -#include - -void handleRequest(AsyncWebServerRequest *request){} - -class WebClass { -public : - AsyncWebServer classWebServer = AsyncWebServer(81); - - WebClass(){}; - - void classRequest (AsyncWebServerRequest *request){} - - void begin(){ - // attach global request handler - classWebServer.on("/example", HTTP_ANY, handleRequest); - - // attach class request handler - classWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, this, std::placeholders::_1)); - } -}; - -AsyncWebServer globalWebServer(80); -WebClass webClassInstance; - -void setup() { - // attach global request handler - globalWebServer.on("/example", HTTP_ANY, handleRequest); - - // attach class request handler - globalWebServer.on("/example", HTTP_ANY, std::bind(&WebClass::classRequest, webClassInstance, std::placeholders::_1)); -} - -void loop() { - -} -``` - -### Methods for controlling websocket connections - -```cpp - // Disable client connections if it was activated - if ( ws.enabled() ) - ws.enable(false); - - // enable client connections if it was disabled - if ( !ws.enabled() ) - ws.enable(true); -``` - -Example of OTA code - -```cpp - // OTA callbacks - ArduinoOTA.onStart([]() { - // Clean SPIFFS - SPIFFS.end(); - - // Disable client connections - ws.enable(false); - - // Advertise connected clients what's going on - ws.textAll("OTA Update Started"); - - // Close them - ws.closeAll(); - - }); - -``` - -### Adding Default Headers - -In some cases, such as when working with CORS, or with some sort of custom authentication system, -you might need to define a header that should get added to all responses (including static, websocket and EventSource). -The DefaultHeaders singleton allows you to do this. - -Example: - -```cpp -DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); -webServer.begin(); -``` - -*NOTE*: You will still need to respond to the OPTIONS method for CORS pre-flight in most cases. (unless you are only using GET) - -This is one option: - -```cpp -webServer.onNotFound([](AsyncWebServerRequest *request) { - if (request->method() == HTTP_OPTIONS) { - request->send(200); - } else { - request->send(404); - } -}); -``` - -### Path variable - -With path variable you can create a custom regex rule for a specific parameter in a route. -For example we want a `sensorId` parameter in a route rule to match only a integer. - -```cpp - server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) { - String sensorId = request->pathArg(0); - }); -``` -*NOTE*: All regex patterns starts with `^` and ends with `$` - -To enable the `Path variable` support, you have to define the buildflag `-DASYNCWEBSERVER_REGEX`. - - -For Arduino IDE create/update `platform.local.txt`: - -`Windows`: C:\Users\(username)\AppData\Local\Arduino15\packages\\`{espxxxx}`\hardware\\`espxxxx`\\`{version}`\platform.local.txt - -`Linux`: ~/.arduino15/packages/`{espxxxx}`/hardware/`{espxxxx}`/`{version}`/platform.local.txt - -Add/Update the following line: -``` - compiler.cpp.extra_flags=-DDASYNCWEBSERVER_REGEX -``` - -For platformio modify `platformio.ini`: -```ini -[env:myboard] -build_flags = - -DASYNCWEBSERVER_REGEX -``` -*NOTE*: By enabling `ASYNCWEBSERVER_REGEX`, `` will be included. This will add an 100k to your binary. diff --git a/lib/ESPAsyncWebServer/_config.yml b/lib/ESPAsyncWebServer/_config.yml deleted file mode 100644 index c4192631..00000000 --- a/lib/ESPAsyncWebServer/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-cayman \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/component.mk b/lib/ESPAsyncWebServer/component.mk deleted file mode 100644 index bb5bb161..00000000 --- a/lib/ESPAsyncWebServer/component.mk +++ /dev/null @@ -1,3 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS := src -COMPONENT_SRCDIRS := src -CXXFLAGS += -fno-rtti diff --git a/lib/ESPAsyncWebServer/examples/CaptivePortal/CaptivePortal.ino b/lib/ESPAsyncWebServer/examples/CaptivePortal/CaptivePortal.ino deleted file mode 100644 index f97f142a..00000000 --- a/lib/ESPAsyncWebServer/examples/CaptivePortal/CaptivePortal.ino +++ /dev/null @@ -1,47 +0,0 @@ -#include -#ifdef ESP32 -#include -#include -#elif defined(ESP8266) -#include -#include -#endif -#include "ESPAsyncWebServer.h" - -DNSServer dnsServer; -AsyncWebServer server(80); - -class CaptiveRequestHandler : public AsyncWebHandler { -public: - CaptiveRequestHandler() {} - virtual ~CaptiveRequestHandler() {} - - bool canHandle(AsyncWebServerRequest *request){ - //request->addInterestingHeader("ANY"); - return true; - } - - void handleRequest(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - response->print("Captive Portal"); - response->print("

This is out captive portal front page.

"); - response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); - response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); - response->print(""); - request->send(response); - } -}; - - -void setup(){ - //your other setup stuff... - WiFi.softAP("esp-captive"); - dnsServer.start(53, "*", WiFi.softAPIP()); - server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP - //more handlers... - server.begin(); -} - -void loop(){ - dnsServer.processNextRequest(); -} diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino deleted file mode 100644 index bf42d003..00000000 --- a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino +++ /dev/null @@ -1,221 +0,0 @@ -#include -#ifdef ESP32 -#include -#include -#include -#include -#include -#elif defined(ESP8266) -#include -#include -#include -#endif -#include -#include - -// SKETCH BEGIN -AsyncWebServer server(80); -AsyncWebSocket ws("/ws"); -AsyncEventSource events("/events"); - -void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - if(type == WS_EVT_CONNECT){ - Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf("Hello Client %u :)", client->id()); - client->ping(); - } else if(type == WS_EVT_DISCONNECT){ - Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); - } else if(type == WS_EVT_ERROR){ - Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); - } else if(type == WS_EVT_PONG){ - Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:""); - } else if(type == WS_EVT_DATA){ - AwsFrameInfo * info = (AwsFrameInfo*)arg; - String msg = ""; - if(info->final && info->index == 0 && info->len == len){ - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); - - if(info->opcode == WS_TEXT){ - for(size_t i=0; i < info->len; i++) { - msg += (char) data[i]; - } - } else { - char buff[3]; - for(size_t i=0; i < info->len; i++) { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - Serial.printf("%s\n",msg.c_str()); - - if(info->opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } else { - //message is comprised of multiple frames or the frame is split into multiple packets - if(info->index == 0){ - if(info->num == 0) - Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); - Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); - - if(info->opcode == WS_TEXT){ - for(size_t i=0; i < len; i++) { - msg += (char) data[i]; - } - } else { - char buff[3]; - for(size_t i=0; i < len; i++) { - sprintf(buff, "%02x ", (uint8_t) data[i]); - msg += buff ; - } - } - Serial.printf("%s\n",msg.c_str()); - - if((info->index + len) == info->len){ - Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - if(info->final){ - Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); - if(info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } -} - - -const char* ssid = "*******"; -const char* password = "*******"; -const char * hostName = "esp-async"; -const char* http_username = "admin"; -const char* http_password = "admin"; - -void setup(){ - Serial.begin(115200); - Serial.setDebugOutput(true); - WiFi.mode(WIFI_AP_STA); - WiFi.softAP(hostName); - WiFi.begin(ssid, password); - if (WiFi.waitForConnectResult() != WL_CONNECTED) { - Serial.printf("STA: Failed!\n"); - WiFi.disconnect(false); - delay(1000); - WiFi.begin(ssid, password); - } - - //Send OTA events to the browser - ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); }); - ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); }); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - char p[32]; - sprintf(p, "Progress: %u%%\n", (progress/(total/100))); - events.send(p, "ota"); - }); - ArduinoOTA.onError([](ota_error_t error) { - if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota"); - else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota"); - else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota"); - else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota"); - else if(error == OTA_END_ERROR) events.send("End Failed", "ota"); - }); - ArduinoOTA.setHostname(hostName); - ArduinoOTA.begin(); - - MDNS.addService("http","tcp",80); - - SPIFFS.begin(); - - ws.onEvent(onWsEvent); - server.addHandler(&ws); - - events.onConnect([](AsyncEventSourceClient *client){ - client->send("hello!",NULL,millis(),1000); - }); - server.addHandler(&events); - -#ifdef ESP32 - server.addHandler(new SPIFFSEditor(SPIFFS, http_username,http_password)); -#elif defined(ESP8266) - server.addHandler(new SPIFFSEditor(http_username,http_password)); -#endif - - server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/plain", String(ESP.getFreeHeap())); - }); - - server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm"); - - server.onNotFound([](AsyncWebServerRequest *request){ - Serial.printf("NOT_FOUND: "); - if(request->method() == HTTP_GET) - Serial.printf("GET"); - else if(request->method() == HTTP_POST) - Serial.printf("POST"); - else if(request->method() == HTTP_DELETE) - Serial.printf("DELETE"); - else if(request->method() == HTTP_PUT) - Serial.printf("PUT"); - else if(request->method() == HTTP_PATCH) - Serial.printf("PATCH"); - else if(request->method() == HTTP_HEAD) - Serial.printf("HEAD"); - else if(request->method() == HTTP_OPTIONS) - Serial.printf("OPTIONS"); - else - Serial.printf("UNKNOWN"); - Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); - - if(request->contentLength()){ - Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); - Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); - } - - int headers = request->headers(); - int i; - for(i=0;igetHeader(i); - Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); - } - - int params = request->params(); - for(i=0;igetParam(i); - if(p->isFile()){ - Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); - } else if(p->isPost()){ - Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } else { - Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); - } - } - - request->send(404); - }); - server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ - if(!index) - Serial.printf("UploadStart: %s\n", filename.c_str()); - Serial.printf("%s", (const char*)data); - if(final) - Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len); - }); - server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ - if(!index) - Serial.printf("BodyStart: %u\n", total); - Serial.printf("%s", (const char*)data); - if(index + len == total) - Serial.printf("BodyEnd: %u\n", total); - }); - server.begin(); -} - -void loop(){ - ArduinoOTA.handle(); - ws.cleanupClients(); -} diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files deleted file mode 100644 index 955397fa..00000000 --- a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/.exclude.files +++ /dev/null @@ -1,2 +0,0 @@ -/*.js.gz -/.exclude.files diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz deleted file mode 100644 index 7b175c1c..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ace.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz deleted file mode 100644 index cf5b49f6..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/ext-searchbox.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico deleted file mode 100644 index 71b25fe6..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/favicon.ico and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm deleted file mode 100644 index 28f47e99..00000000 --- a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/index.htm +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - WebSocketTester - - - - -

-    
- $ -
- - diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz deleted file mode 100644 index ebd6fe94..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-css.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz deleted file mode 100644 index 26b53532..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-html.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz deleted file mode 100644 index c0451c1c..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/mode-javascript.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz b/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz deleted file mode 100644 index ec8aa87a..00000000 Binary files a/lib/ESPAsyncWebServer/examples/ESP_AsyncFSBrowser/data/worker-html.js.gz and /dev/null differ diff --git a/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags b/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags deleted file mode 100644 index 9ea3bb74..00000000 --- a/lib/ESPAsyncWebServer/examples/regex_patterns/.test.build_flags +++ /dev/null @@ -1 +0,0 @@ --DASYNCWEBSERVER_REGEX=1 diff --git a/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino b/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino deleted file mode 100644 index fb013063..00000000 --- a/lib/ESPAsyncWebServer/examples/regex_patterns/regex_patterns.ino +++ /dev/null @@ -1,77 +0,0 @@ -// -// A simple server implementation with regex routes: -// * serve static messages -// * read GET and POST parameters -// * handle missing pages / 404s -// - -// Add buildflag ASYNCWEBSERVER_REGEX to enable the regex support - -// For platformio: platformio.ini: -// build_flags = -// -DASYNCWEBSERVER_REGEX - -// For arduino IDE: create/update platform.local.txt -// Windows: C:\Users\(username)\AppData\Local\Arduino15\packages\espxxxx\hardware\espxxxx\{version}\platform.local.txt -// Linux: ~/.arduino15/packages/espxxxx/hardware/espxxxx/{version}/platform.local.txt -// -// compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX=1 - -#include -#ifdef ESP32 -#include -#include -#elif defined(ESP8266) -#include -#include -#endif -#include - -AsyncWebServer server(80); - -const char* ssid = "YOUR_SSID"; -const char* password = "YOUR_PASSWORD"; - -const char* PARAM_MESSAGE = "message"; - -void notFound(AsyncWebServerRequest *request) { - request->send(404, "text/plain", "Not found"); -} - -void setup() { - - Serial.begin(115200); - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - if (WiFi.waitForConnectResult() != WL_CONNECTED) { - Serial.printf("WiFi Failed!\n"); - return; - } - - Serial.print("IP Address: "); - Serial.println(WiFi.localIP()); - - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/plain", "Hello, world"); - }); - - // Send a GET request to /sensor/ - server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) { - String sensorNumber = request->pathArg(0); - request->send(200, "text/plain", "Hello, sensor: " + sensorNumber); - }); - - // Send a GET request to /sensor//action/ - server.on("^\\/sensor\\/([0-9]+)\\/action\\/([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) { - String sensorNumber = request->pathArg(0); - String action = request->pathArg(1); - request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action); - }); - - server.onNotFound(notFound); - - server.begin(); -} - -void loop() { -} diff --git a/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino b/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino deleted file mode 100644 index bdbcf60d..00000000 --- a/lib/ESPAsyncWebServer/examples/simple_server/simple_server.ino +++ /dev/null @@ -1,74 +0,0 @@ -// -// A simple server implementation showing how to: -// * serve static messages -// * read GET and POST parameters -// * handle missing pages / 404s -// - -#include -#ifdef ESP32 -#include -#include -#elif defined(ESP8266) -#include -#include -#endif -#include - -AsyncWebServer server(80); - -const char* ssid = "YOUR_SSID"; -const char* password = "YOUR_PASSWORD"; - -const char* PARAM_MESSAGE = "message"; - -void notFound(AsyncWebServerRequest *request) { - request->send(404, "text/plain", "Not found"); -} - -void setup() { - - Serial.begin(115200); - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - if (WiFi.waitForConnectResult() != WL_CONNECTED) { - Serial.printf("WiFi Failed!\n"); - return; - } - - Serial.print("IP Address: "); - Serial.println(WiFi.localIP()); - - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/plain", "Hello, world"); - }); - - // Send a GET request to /get?message= - server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { - String message; - if (request->hasParam(PARAM_MESSAGE)) { - message = request->getParam(PARAM_MESSAGE)->value(); - } else { - message = "No message sent"; - } - request->send(200, "text/plain", "Hello, GET: " + message); - }); - - // Send a POST request to /post with a form field message set to - server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){ - String message; - if (request->hasParam(PARAM_MESSAGE, true)) { - message = request->getParam(PARAM_MESSAGE, true)->value(); - } else { - message = "No message sent"; - } - request->send(200, "text/plain", "Hello, POST: " + message); - }); - - server.onNotFound(notFound); - - server.begin(); -} - -void loop() { -} \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/keywords.txt b/lib/ESPAsyncWebServer/keywords.txt deleted file mode 100644 index c391e6c4..00000000 --- a/lib/ESPAsyncWebServer/keywords.txt +++ /dev/null @@ -1,3 +0,0 @@ -JsonArray KEYWORD1 -add KEYWORD2 -createArray KEYWORD3 diff --git a/lib/ESPAsyncWebServer/library.json b/lib/ESPAsyncWebServer/library.json deleted file mode 100644 index 55659279..00000000 --- a/lib/ESPAsyncWebServer/library.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name":"ESP Async WebServer", - "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32", - "keywords":"http,async,websocket,webserver", - "authors": - { - "name": "Hristo Gochkov", - "maintainer": true - }, - "repository": - { - "type": "git", - "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" - }, - "version": "1.2.3", - "license": "LGPL-3.0", - "frameworks": "arduino", - "platforms": ["espressif8266", "espressif32"], - "dependencies": [ - { - "name": "ESPAsyncTCP", - "platforms": "espressif8266" - }, - { - "name": "AsyncTCP", - "platforms": "espressif32" - }, - { - "name": "Hash", - "platforms": "espressif8266" - } - ] -} diff --git a/lib/ESPAsyncWebServer/library.properties b/lib/ESPAsyncWebServer/library.properties deleted file mode 100644 index 401b0417..00000000 --- a/lib/ESPAsyncWebServer/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=ESP Async WebServer -version=1.2.3 -author=Me-No-Dev -maintainer=Me-No-Dev -sentence=Async Web Server for ESP8266 and ESP31B -paragraph=Async Web Server for ESP8266 and ESP31B -category=Other -url=https://github.com/me-no-dev/ESPAsyncWebServer -architectures=* diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp b/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp deleted file mode 100644 index f2914df5..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncEventSource.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "Arduino.h" -#include "AsyncEventSource.h" - -static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - String ev = ""; - - if(reconnect){ - ev += "retry: "; - ev += String(reconnect); - ev += "\r\n"; - } - - if(id){ - ev += "id: "; - ev += String(id); - ev += "\r\n"; - } - - if(event != NULL){ - ev += "event: "; - ev += String(event); - ev += "\r\n"; - } - - if(message != NULL){ - size_t messageLen = strlen(message); - char * lineStart = (char *)message; - char * lineEnd; - do { - char * nextN = strchr(lineStart, '\n'); - char * nextR = strchr(lineStart, '\r'); - if(nextN == NULL && nextR == NULL){ - size_t llen = ((char *)message + messageLen) - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ - memcpy(ldata, lineStart, llen); - ldata[llen] = 0; - ev += "data: "; - ev += ldata; - ev += "\r\n\r\n"; - free(ldata); - } - lineStart = (char *)message + messageLen; - } else { - char * nextLine = NULL; - if(nextN != NULL && nextR != NULL){ - if(nextR < nextN){ - lineEnd = nextR; - if(nextN == (nextR + 1)) - nextLine = nextN + 1; - else - nextLine = nextR + 1; - } else { - lineEnd = nextN; - if(nextR == (nextN + 1)) - nextLine = nextR + 1; - else - nextLine = nextN + 1; - } - } else if(nextN != NULL){ - lineEnd = nextN; - nextLine = nextN + 1; - } else { - lineEnd = nextR; - nextLine = nextR + 1; - } - - size_t llen = lineEnd - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ - memcpy(ldata, lineStart, llen); - ldata[llen] = 0; - ev += "data: "; - ev += ldata; - ev += "\r\n"; - free(ldata); - } - lineStart = nextLine; - if(lineStart == ((char *)message + messageLen)) - ev += "\r\n"; - } - } while(lineStart < ((char *)message + messageLen)); - } - - return ev; -} - -// Message - -AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) -: _data(nullptr), _len(len), _sent(0), _acked(0) -{ - _data = (uint8_t*)malloc(_len+1); - if(_data == nullptr){ - _len = 0; - } else { - memcpy(_data, data, len); - _data[_len] = 0; - } -} - -AsyncEventSourceMessage::~AsyncEventSourceMessage() { - if(_data != NULL) - free(_data); -} - -size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) { - (void)time; - // If the whole message is now acked... - if(_acked + len > _len){ - // Return the number of extra bytes acked (they will be carried on to the next message) - const size_t extra = _acked + len - _len; - _acked = _len; - return extra; - } - // Return that no extra bytes left. - _acked += len; - return 0; -} - -size_t AsyncEventSourceMessage::send(AsyncClient *client) { - const size_t len = _len - _sent; - if(client->space() < len){ - return 0; - } - size_t sent = client->add((const char *)_data, len); - if(client->canSend()) - client->send(); - _sent += sent; - return sent; -} - -// Client - -AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) -: _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; })) -{ - _client = request->client(); - _server = server; - _lastId = 0; - if(request->hasHeader("Last-Event-ID")) - _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str()); - - _client->setRxTimeout(0); - _client->onError(NULL, NULL); - _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); - _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); - _client->onData(NULL, NULL); - _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this); - _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this); - - _server->_addClient(this); - delete request; -} - -AsyncEventSourceClient::~AsyncEventSourceClient(){ - _messageQueue.free(); - close(); -} - -void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){ - if(dataMessage == NULL) - return; - if(!connected()){ - delete dataMessage; - return; - } - if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ - ets_printf("ERROR: Too many messages queued\n"); - delete dataMessage; - } else { - _messageQueue.add(dataMessage); - } - if(_client->canSend()) - _runQueue(); -} - -void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){ - while(len && !_messageQueue.isEmpty()){ - len = _messageQueue.front()->ack(len, time); - if(_messageQueue.front()->finished()) - _messageQueue.remove(_messageQueue.front()); - } - - _runQueue(); -} - -void AsyncEventSourceClient::_onPoll(){ - if(!_messageQueue.isEmpty()){ - _runQueue(); - } -} - - -void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){ - _client->close(true); -} - -void AsyncEventSourceClient::_onDisconnect(){ - _client = NULL; - _server->_handleDisconnect(this); -} - -void AsyncEventSourceClient::close(){ - if(_client != NULL) - _client->close(); -} - -void AsyncEventSourceClient::write(const char * message, size_t len){ - _queueMessage(new AsyncEventSourceMessage(message, len)); -} - -void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - String ev = generateEventMessage(message, event, id, reconnect); - _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); -} - -void AsyncEventSourceClient::_runQueue(){ - while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){ - _messageQueue.remove(_messageQueue.front()); - } - - for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) - { - if(!(*i)->sent()) - (*i)->send(_client); - } -} - - -// Handler - -AsyncEventSource::AsyncEventSource(const String& url) - : _url(url) - , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; })) - , _connectcb(NULL) -{} - -AsyncEventSource::~AsyncEventSource(){ - close(); -} - -void AsyncEventSource::onConnect(ArEventHandlerFunction cb){ - _connectcb = cb; -} - -void AsyncEventSource::_addClient(AsyncEventSourceClient * client){ - /*char * temp = (char *)malloc(2054); - if(temp != NULL){ - memset(temp+1,' ',2048); - temp[0] = ':'; - temp[2049] = '\r'; - temp[2050] = '\n'; - temp[2051] = '\r'; - temp[2052] = '\n'; - temp[2053] = 0; - client->write((const char *)temp, 2053); - free(temp); - }*/ - - _clients.add(client); - if(_connectcb) - _connectcb(client); -} - -void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){ - _clients.remove(client); -} - -void AsyncEventSource::close(){ - for(const auto &c: _clients){ - if(c->connected()) - c->close(); - } -} - -// pmb fix -size_t AsyncEventSource::avgPacketsWaiting() const { - if(_clients.isEmpty()) - return 0; - - size_t aql=0; - uint32_t nConnectedClients=0; - - for(const auto &c: _clients){ - if(c->connected()) { - aql+=c->packetsWaiting(); - ++nConnectedClients; - } - } -// return aql / nConnectedClients; - return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up -} - -void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ - - - String ev = generateEventMessage(message, event, id, reconnect); - for(const auto &c: _clients){ - if(c->connected()) { - c->write(ev.c_str(), ev.length()); - } - } -} - -size_t AsyncEventSource::count() const { - return _clients.count_if([](AsyncEventSourceClient *c){ - return c->connected(); - }); -} - -bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET || !request->url().equals(_url)) { - return false; - } - request->addInterestingHeader("Last-Event-ID"); - return true; -} - -void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - request->send(new AsyncEventSourceResponse(this)); -} - -// Response - -AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ - _server = server; - _code = 200; - _contentType = "text/event-stream"; - _sendContentLength = false; - addHeader("Cache-Control", "no-cache"); - addHeader("Connection","keep-alive"); -} - -void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){ - String out = _assembleHead(request->version()); - request->client()->write(out.c_str(), _headLength); - _state = RESPONSE_WAIT_ACK; -} - -size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){ - if(len){ - new AsyncEventSourceClient(request, _server); - } - return 0; -} - diff --git a/lib/ESPAsyncWebServer/src/AsyncEventSource.h b/lib/ESPAsyncWebServer/src/AsyncEventSource.h deleted file mode 100644 index b097fa62..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncEventSource.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef ASYNCEVENTSOURCE_H_ -#define ASYNCEVENTSOURCE_H_ - -#include -#ifdef ESP32 -#include -#define SSE_MAX_QUEUED_MESSAGES 32 -#else -#include -#define SSE_MAX_QUEUED_MESSAGES 8 -#endif -#include - -#include "AsyncWebSynchronization.h" - -#ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif -#endif - -#ifdef ESP32 -#define DEFAULT_MAX_SSE_CLIENTS 8 -#else -#define DEFAULT_MAX_SSE_CLIENTS 4 -#endif - -class AsyncEventSource; -class AsyncEventSourceResponse; -class AsyncEventSourceClient; -typedef std::function ArEventHandlerFunction; - -class AsyncEventSourceMessage { - private: - uint8_t * _data; - size_t _len; - size_t _sent; - //size_t _ack; - size_t _acked; - public: - AsyncEventSourceMessage(const char * data, size_t len); - ~AsyncEventSourceMessage(); - size_t ack(size_t len, uint32_t time __attribute__((unused))); - size_t send(AsyncClient *client); - bool finished(){ return _acked == _len; } - bool sent() { return _sent == _len; } -}; - -class AsyncEventSourceClient { - private: - AsyncClient *_client; - AsyncEventSource *_server; - uint32_t _lastId; - LinkedList _messageQueue; - void _queueMessage(AsyncEventSourceMessage *dataMessage); - void _runQueue(); - - public: - - AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); - ~AsyncEventSourceClient(); - - AsyncClient* client(){ return _client; } - void close(); - void write(const char * message, size_t len); - void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); - bool connected() const { return (_client != NULL) && _client->connected(); } - uint32_t lastId() const { return _lastId; } - size_t packetsWaiting() const { return _messageQueue.length(); } - - //system callbacks (do not call) - void _onAck(size_t len, uint32_t time); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); -}; - -class AsyncEventSource: public AsyncWebHandler { - private: - String _url; - LinkedList _clients; - ArEventHandlerFunction _connectcb; - public: - AsyncEventSource(const String& url); - ~AsyncEventSource(); - - const char * url() const { return _url.c_str(); } - void close(); - void onConnect(ArEventHandlerFunction cb); - void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); - size_t count() const; //number clinets connected - size_t avgPacketsWaiting() const; - - //system callbacks (do not call) - void _addClient(AsyncEventSourceClient * client); - void _handleDisconnect(AsyncEventSourceClient * client); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; -}; - -class AsyncEventSourceResponse: public AsyncWebServerResponse { - private: - String _content; - AsyncEventSource *_server; - public: - AsyncEventSourceResponse(AsyncEventSource *server); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } -}; - - -#endif /* ASYNCEVENTSOURCE_H_ */ diff --git a/lib/ESPAsyncWebServer/src/AsyncJson.h b/lib/ESPAsyncWebServer/src/AsyncJson.h deleted file mode 100644 index 27b4a26f..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncJson.h +++ /dev/null @@ -1,252 +0,0 @@ -// AsyncJson.h -/* - Async Response to use with ArduinoJson and AsyncWebServer - Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon. - - Example of callback in use - - server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) { - - AsyncJsonResponse * response = new AsyncJsonResponse(); - JsonObject& root = response->getRoot(); - root["key1"] = "key number one"; - JsonObject& nested = root.createNestedObject("nested"); - nested["key1"] = "key number one"; - - response->setLength(); - request->send(response); - }); - - -------------------- - - Async Request to use with ArduinoJson and AsyncWebServer - Written by Arsène von Wyss (avonwyss) - - Example - - AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint"); - handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) { - JsonObject& jsonObj = json.as(); - // ... - }); - server.addHandler(handler); - -*/ -#ifndef ASYNC_JSON_H_ -#define ASYNC_JSON_H_ -#include -#include -#include - -#if ARDUINOJSON_VERSION_MAJOR == 5 - #define ARDUINOJSON_5_COMPATIBILITY -#else - #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 -#endif - -constexpr const char* JSON_MIMETYPE = "application/json"; - -/* - * Json Response - * */ - -class ChunkPrint : public Print { - private: - uint8_t* _destination; - size_t _to_skip; - size_t _to_write; - size_t _pos; - public: - ChunkPrint(uint8_t* destination, size_t from, size_t len) - : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {} - virtual ~ChunkPrint(){} - size_t write(uint8_t c){ - if (_to_skip > 0) { - _to_skip--; - return 1; - } else if (_to_write > 0) { - _to_write--; - _destination[_pos++] = c; - return 1; - } - return 0; - } - size_t write(const uint8_t *buffer, size_t size) - { - return this->Print::write(buffer, size); - } -}; - -class AsyncJsonResponse: public AsyncAbstractResponse { - protected: - -#ifdef ARDUINOJSON_5_COMPATIBILITY - DynamicJsonBuffer _jsonBuffer; -#else - DynamicJsonDocument _jsonBuffer; -#endif - - JsonVariant _root; - bool _isValid; - - public: - -#ifdef ARDUINOJSON_5_COMPATIBILITY - AsyncJsonResponse(bool isArray=false): _isValid{false} { - _code = 200; - _contentType = JSON_MIMETYPE; - if(isArray) - _root = _jsonBuffer.createArray(); - else - _root = _jsonBuffer.createObject(); - } -#else - AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} { - _code = 200; - _contentType = JSON_MIMETYPE; - if(isArray) - _root = _jsonBuffer.createNestedArray(); - else - _root = _jsonBuffer.createNestedObject(); - } -#endif - - ~AsyncJsonResponse() {} - JsonVariant & getRoot() { return _root; } - bool _sourceValid() const { return _isValid; } - size_t setLength() { - -#ifdef ARDUINOJSON_5_COMPATIBILITY - _contentLength = _root.measureLength(); -#else - _contentLength = measureJson(_root); -#endif - - if (_contentLength) { _isValid = true; } - return _contentLength; - } - - size_t getSize() { return _jsonBuffer.size(); } - - size_t _fillBuffer(uint8_t *data, size_t len){ - ChunkPrint dest(data, _sentLength, len); - -#ifdef ARDUINOJSON_5_COMPATIBILITY - _root.printTo( dest ) ; -#else - serializeJson(_root, dest); -#endif - return len; - } -}; - -class PrettyAsyncJsonResponse: public AsyncJsonResponse { -public: -#ifdef ARDUINOJSON_5_COMPATIBILITY - PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {} -#else - PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {} -#endif - size_t setLength () { -#ifdef ARDUINOJSON_5_COMPATIBILITY - _contentLength = _root.measurePrettyLength (); -#else - _contentLength = measureJsonPretty(_root); -#endif - if (_contentLength) {_isValid = true;} - return _contentLength; - } - size_t _fillBuffer (uint8_t *data, size_t len) { - ChunkPrint dest (data, _sentLength, len); -#ifdef ARDUINOJSON_5_COMPATIBILITY - _root.prettyPrintTo (dest); -#else - serializeJsonPretty(_root, dest); -#endif - return len; - } -}; - -typedef std::function ArJsonRequestHandlerFunction; - -class AsyncCallbackJsonWebHandler: public AsyncWebHandler { -private: -protected: - const String _uri; - WebRequestMethodComposite _method; - ArJsonRequestHandlerFunction _onRequest; - size_t _contentLength; -#ifndef ARDUINOJSON_5_COMPATIBILITY - const size_t maxJsonBufferSize; -#endif - size_t _maxContentLength; -public: -#ifdef ARDUINOJSON_5_COMPATIBILITY - AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) - : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} -#else - AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE) - : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {} -#endif - - void setMethod(WebRequestMethodComposite method){ _method = method; } - void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; } - void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; } - - virtual bool canHandle(AsyncWebServerRequest *request) override final{ - if(!_onRequest) - return false; - - if(!(_method & request->method())) - return false; - - if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) - return false; - - if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) ) - return false; - - request->addInterestingHeader("ANY"); - return true; - } - - virtual void handleRequest(AsyncWebServerRequest *request) override final { - if(_onRequest) { - if (request->_tempObject != NULL) { - -#ifdef ARDUINOJSON_5_COMPATIBILITY - DynamicJsonBuffer jsonBuffer; - JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject)); - if (json.success()) { -#else - DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); - DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); - if(!error) { - JsonVariant json = jsonBuffer.as(); -#endif - - _onRequest(request, json); - return; - } - } - request->send(_contentLength > _maxContentLength ? 413 : 400); - } else { - request->send(500); - } - } - virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { - } - virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { - if (_onRequest) { - _contentLength = total; - if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { - request->_tempObject = malloc(total); - } - if (request->_tempObject != NULL) { - memcpy((uint8_t*)(request->_tempObject) + index, data, len); - } - } - } - virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} -}; -#endif diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp b/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp deleted file mode 100644 index 52dcd75f..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.cpp +++ /dev/null @@ -1,1303 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "Arduino.h" -#include "AsyncWebSocket.h" - -#include - -#ifndef ESP8266 -extern "C" { -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); -void SHA1Init(SHA1_CTX* context); -void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); -void SHA1Final(unsigned char digest[20], SHA1_CTX* context); -} -#else -#include -#endif - -#define MAX_PRINTF_LEN 64 - -size_t webSocketSendFrameWindow(AsyncClient *client){ - if(!client->canSend()) - return 0; - size_t space = client->space(); - if(space < 9) - return 0; - return space - 8; -} - -size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len){ - if(!client->canSend()) - return 0; - size_t space = client->space(); - if(space < 2) - return 0; - uint8_t mbuf[4] = {0,0,0,0}; - uint8_t headLen = 2; - if(len && mask){ - headLen += 4; - mbuf[0] = rand() % 0xFF; - mbuf[1] = rand() % 0xFF; - mbuf[2] = rand() % 0xFF; - mbuf[3] = rand() % 0xFF; - } - if(len > 125) - headLen += 2; - if(space < headLen) - return 0; - space -= headLen; - - if(len > space) len = space; - - uint8_t *buf = (uint8_t*)malloc(headLen); - if(buf == NULL){ - //os_printf("could not malloc %u bytes for frame header\n", headLen); - return 0; - } - - buf[0] = opcode & 0x0F; - if(final) - buf[0] |= 0x80; - if(len < 126) - buf[1] = len & 0x7F; - else { - buf[1] = 126; - buf[2] = (uint8_t)((len >> 8) & 0xFF); - buf[3] = (uint8_t)(len & 0xFF); - } - if(len && mask){ - buf[1] |= 0x80; - memcpy(buf + (headLen - 4), mbuf, 4); - } - if(client->add((const char *)buf, headLen) != headLen){ - //os_printf("error adding %lu header bytes\n", headLen); - free(buf); - return 0; - } - free(buf); - - if(len){ - if(len && mask){ - size_t i; - for(i=0;iadd((const char *)data, len) != len){ - //os_printf("error adding %lu data bytes\n", len); - return 0; - } - } - if(!client->send()){ - //os_printf("error sending frame: %lu\n", headLen+len); - return 0; - } - return len; -} - - -/* - * AsyncWebSocketMessageBuffer - */ - - - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer() - :_data(nullptr) - ,_len(0) - ,_lock(false) - ,_count(0) -{ - -} - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) - :_data(nullptr) - ,_len(size) - ,_lock(false) - ,_count(0) -{ - - if (!data) { - return; - } - - _data = new uint8_t[_len + 1]; - - if (_data) { - memcpy(_data, data, _len); - _data[_len] = 0; - } -} - - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) - :_data(nullptr) - ,_len(size) - ,_lock(false) - ,_count(0) -{ - _data = new uint8_t[_len + 1]; - - if (_data) { - _data[_len] = 0; - } - -} - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) - :_data(nullptr) - ,_len(0) - ,_lock(false) - ,_count(0) -{ - _len = copy._len; - _lock = copy._lock; - _count = 0; - - if (_len) { - _data = new uint8_t[_len + 1]; - _data[_len] = 0; - } - - if (_data) { - memcpy(_data, copy._data, _len); - _data[_len] = 0; - } - -} - -AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer && copy) - :_data(nullptr) - ,_len(0) - ,_lock(false) - ,_count(0) -{ - _len = copy._len; - _lock = copy._lock; - _count = 0; - - if (copy._data) { - _data = copy._data; - copy._data = nullptr; - } - -} - -AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() -{ - if (_data) { - delete[] _data; - } -} - -bool AsyncWebSocketMessageBuffer::reserve(size_t size) -{ - _len = size; - - if (_data) { - delete[] _data; - _data = nullptr; - } - - _data = new uint8_t[_len + 1]; - - if (_data) { - _data[_len] = 0; - return true; - } else { - return false; - } - -} - - - -/* - * Control Frame - */ - -class AsyncWebSocketControl { - private: - uint8_t _opcode; - uint8_t *_data; - size_t _len; - bool _mask; - bool _finished; - public: - AsyncWebSocketControl(uint8_t opcode, uint8_t *data=NULL, size_t len=0, bool mask=false) - :_opcode(opcode) - ,_len(len) - ,_mask(len && mask) - ,_finished(false) - { - if(data == NULL) - _len = 0; - if(_len){ - if(_len > 125) - _len = 125; - _data = (uint8_t*)malloc(_len); - if(_data == NULL) - _len = 0; - else memcpy(_data, data, len); - } else _data = NULL; - } - virtual ~AsyncWebSocketControl(){ - if(_data != NULL) - free(_data); - } - virtual bool finished() const { return _finished; } - uint8_t opcode(){ return _opcode; } - uint8_t len(){ return _len + 2; } - size_t send(AsyncClient *client){ - _finished = true; - return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); - } -}; - -/* - * Basic Buffered Message - */ - - -AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode, bool mask) - :_len(len) - ,_sent(0) - ,_ack(0) - ,_acked(0) -{ - _opcode = opcode & 0x07; - _mask = mask; - _data = (uint8_t*)malloc(_len+1); - if(_data == NULL){ - _len = 0; - _status = WS_MSG_ERROR; - } else { - _status = WS_MSG_SENDING; - memcpy(_data, data, _len); - _data[_len] = 0; - } -} -AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask) - :_len(0) - ,_sent(0) - ,_ack(0) - ,_acked(0) - ,_data(NULL) -{ - _opcode = opcode & 0x07; - _mask = mask; - -} - - -AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() { - if(_data != NULL) - free(_data); -} - - void AsyncWebSocketBasicMessage::ack(size_t len, uint32_t time) { - (void)time; - _acked += len; - if(_sent == _len && _acked == _ack){ - _status = WS_MSG_SENT; - } -} - size_t AsyncWebSocketBasicMessage::send(AsyncClient *client) { - if(_status != WS_MSG_SENDING) - return 0; - if(_acked < _ack){ - return 0; - } - if(_sent == _len){ - if(_acked == _ack) - _status = WS_MSG_SENT; - return 0; - } - if(_sent > _len){ - _status = WS_MSG_ERROR; - return 0; - } - - size_t toSend = _len - _sent; - size_t window = webSocketSendFrameWindow(client); - - if(window < toSend) { - toSend = window; - } - - _sent += toSend; - _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); - - bool final = (_sent == _len); - uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); - uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION; - - size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); - _status = WS_MSG_SENDING; - if(toSend && sent != toSend){ - _sent -= (toSend - sent); - _ack -= (toSend - sent); - } - return sent; -} - -// bool AsyncWebSocketBasicMessage::reserve(size_t size) { -// if (size) { -// _data = (uint8_t*)malloc(size +1); -// if (_data) { -// memset(_data, 0, size); -// _len = size; -// _status = WS_MSG_SENDING; -// return true; -// } -// } -// return false; -// } - - -/* - * AsyncWebSocketMultiMessage Message - */ - - -AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode, bool mask) - :_len(0) - ,_sent(0) - ,_ack(0) - ,_acked(0) - ,_WSbuffer(nullptr) -{ - - _opcode = opcode & 0x07; - _mask = mask; - - if (buffer) { - _WSbuffer = buffer; - (*_WSbuffer)++; - _data = buffer->get(); - _len = buffer->length(); - _status = WS_MSG_SENDING; - //ets_printf("M: %u\n", _len); - } else { - _status = WS_MSG_ERROR; - } - -} - - -AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { - if (_WSbuffer) { - (*_WSbuffer)--; // decreases the counter. - } -} - - void AsyncWebSocketMultiMessage::ack(size_t len, uint32_t time) { - (void)time; - _acked += len; - if(_sent >= _len && _acked >= _ack){ - _status = WS_MSG_SENT; - } - //ets_printf("A: %u\n", len); -} - size_t AsyncWebSocketMultiMessage::send(AsyncClient *client) { - if(_status != WS_MSG_SENDING) - return 0; - if(_acked < _ack){ - return 0; - } - if(_sent == _len){ - _status = WS_MSG_SENT; - return 0; - } - if(_sent > _len){ - _status = WS_MSG_ERROR; - //ets_printf("E: %u > %u\n", _sent, _len); - return 0; - } - - size_t toSend = _len - _sent; - size_t window = webSocketSendFrameWindow(client); - - if(window < toSend) { - toSend = window; - } - - _sent += toSend; - _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); - - //ets_printf("W: %u %u\n", _sent - toSend, toSend); - - bool final = (_sent == _len); - uint8_t* dPtr = (uint8_t*)(_data + (_sent - toSend)); - uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION; - - size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); - _status = WS_MSG_SENDING; - if(toSend && sent != toSend){ - //ets_printf("E: %u != %u\n", toSend, sent); - _sent -= (toSend - sent); - _ack -= (toSend - sent); - } - //ets_printf("S: %u %u\n", _sent, sent); - return sent; -} - - -/* - * Async WebSocket Client - */ - const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; - const size_t AWSC_PING_PAYLOAD_LEN = 22; - -AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) - : _controlQueue(LinkedList([](AsyncWebSocketControl *c){ delete c; })) - , _messageQueue(LinkedList([](AsyncWebSocketMessage *m){ delete m; })) - , _tempObject(NULL) -{ - _client = request->client(); - _server = server; - _clientId = _server->_getNextId(); - _status = WS_CONNECTED; - _pstate = 0; - _lastMessageTime = millis(); - _keepAlivePeriod = 0; - _client->setRxTimeout(0); - _client->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this); - _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); - _client->onDisconnect([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this); - _client->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); - _client->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); - _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); - _server->_addClient(this); - _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0); - delete request; -} - -AsyncWebSocketClient::~AsyncWebSocketClient(){ - _messageQueue.free(); - _controlQueue.free(); - _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); -} - -void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){ - _lastMessageTime = millis(); - if(!_controlQueue.isEmpty()){ - auto head = _controlQueue.front(); - if(head->finished()){ - len -= head->len(); - if(_status == WS_DISCONNECTING && head->opcode() == WS_DISCONNECT){ - _controlQueue.remove(head); - _status = WS_DISCONNECTED; - _client->close(true); - return; - } - _controlQueue.remove(head); - } - } - if(len && !_messageQueue.isEmpty()){ - _messageQueue.front()->ack(len, time); - } - _server->_cleanBuffers(); - _runQueue(); -} - -void AsyncWebSocketClient::_onPoll(){ - if(_client->canSend() && (!_controlQueue.isEmpty() || !_messageQueue.isEmpty())){ - _runQueue(); - } else if(_keepAlivePeriod > 0 && _controlQueue.isEmpty() && _messageQueue.isEmpty() && (millis() - _lastMessageTime) >= _keepAlivePeriod){ - ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); - } -} - -void AsyncWebSocketClient::_runQueue(){ - while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){ - _messageQueue.remove(_messageQueue.front()); - } - - if(!_controlQueue.isEmpty() && (_messageQueue.isEmpty() || _messageQueue.front()->betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front()->len() - 1)){ - _controlQueue.front()->send(_client); - } else if(!_messageQueue.isEmpty() && _messageQueue.front()->betweenFrames() && webSocketSendFrameWindow(_client)){ - _messageQueue.front()->send(_client); - } -} - -bool AsyncWebSocketClient::queueIsFull(){ - if((_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED) ) return true; - return false; -} - -void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){ - if(dataMessage == NULL) - return; - if(_status != WS_CONNECTED){ - delete dataMessage; - return; - } - if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){ - ets_printf("ERROR: Too many messages queued\n"); - delete dataMessage; - } else { - _messageQueue.add(dataMessage); - } - if(_client->canSend()) - _runQueue(); -} - -void AsyncWebSocketClient::_queueControl(AsyncWebSocketControl *controlMessage){ - if(controlMessage == NULL) - return; - _controlQueue.add(controlMessage); - if(_client->canSend()) - _runQueue(); -} - -void AsyncWebSocketClient::close(uint16_t code, const char * message){ - if(_status != WS_CONNECTED) - return; - if(code){ - uint8_t packetLen = 2; - if(message != NULL){ - size_t mlen = strlen(message); - if(mlen > 123) mlen = 123; - packetLen += mlen; - } - char * buf = (char*)malloc(packetLen); - if(buf != NULL){ - buf[0] = (uint8_t)(code >> 8); - buf[1] = (uint8_t)(code & 0xFF); - if(message != NULL){ - memcpy(buf+2, message, packetLen -2); - } - _queueControl(new AsyncWebSocketControl(WS_DISCONNECT,(uint8_t*)buf,packetLen)); - free(buf); - return; - } - } - _queueControl(new AsyncWebSocketControl(WS_DISCONNECT)); -} - -void AsyncWebSocketClient::ping(uint8_t *data, size_t len){ - if(_status == WS_CONNECTED) - _queueControl(new AsyncWebSocketControl(WS_PING, data, len)); -} - -void AsyncWebSocketClient::_onError(int8_t){} - -void AsyncWebSocketClient::_onTimeout(uint32_t time){ - (void)time; - _client->close(true); -} - -void AsyncWebSocketClient::_onDisconnect(){ - _client = NULL; - _server->_handleDisconnect(this); -} - -void AsyncWebSocketClient::_onData(void *pbuf, size_t plen){ - _lastMessageTime = millis(); - uint8_t *data = (uint8_t*)pbuf; - while(plen > 0){ - if(!_pstate){ - const uint8_t *fdata = data; - _pinfo.index = 0; - _pinfo.final = (fdata[0] & 0x80) != 0; - _pinfo.opcode = fdata[0] & 0x0F; - _pinfo.masked = (fdata[1] & 0x80) != 0; - _pinfo.len = fdata[1] & 0x7F; - data += 2; - plen -= 2; - if(_pinfo.len == 126){ - _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; - data += 2; - plen -= 2; - } else if(_pinfo.len == 127){ - _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; - data += 8; - plen -= 8; - } - - if(_pinfo.masked){ - memcpy(_pinfo.mask, data, 4); - data += 4; - plen -= 4; - } - } - - const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen); - const auto datalast = data[datalen]; - - if(_pinfo.masked){ - for(size_t i=0;i_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen); - - _pinfo.index += datalen; - } else if((datalen + _pinfo.index) == _pinfo.len){ - _pstate = 0; - if(_pinfo.opcode == WS_DISCONNECT){ - if(datalen){ - uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; - char * reasonString = (char*)(data+2); - if(reasonCode > 1001){ - _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); - } - } - if(_status == WS_DISCONNECTING){ - _status = WS_DISCONNECTED; - _client->close(true); - } else { - _status = WS_DISCONNECTING; - _client->ackLater(); - _queueControl(new AsyncWebSocketControl(WS_DISCONNECT, data, datalen)); - } - } else if(_pinfo.opcode == WS_PING){ - _queueControl(new AsyncWebSocketControl(WS_PONG, data, datalen)); - } else if(_pinfo.opcode == WS_PONG){ - if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) - _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); - } else if(_pinfo.opcode < 8){//continuation or text/binary frame - _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); - } - } else { - //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); - //what should we do? - break; - } - - // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; - if (datalen > 0) - data[datalen] = datalast; - - data += datalen; - plen -= datalen; - } -} - -size_t AsyncWebSocketClient::printf(const char *format, ...) { - va_list arg; - va_start(arg, format); - char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ - va_end(arg); - return 0; - } - char* buffer = temp; - size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); - va_end(arg); - - if (len > (MAX_PRINTF_LEN - 1)) { - buffer = new char[len + 1]; - if (!buffer) { - delete[] temp; - return 0; - } - va_start(arg, format); - vsnprintf(buffer, len + 1, format, arg); - va_end(arg); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; -} - -#ifndef ESP32 -size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { - va_list arg; - va_start(arg, formatP); - char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ - va_end(arg); - return 0; - } - char* buffer = temp; - size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); - va_end(arg); - - if (len > (MAX_PRINTF_LEN - 1)) { - buffer = new char[len + 1]; - if (!buffer) { - delete[] temp; - return 0; - } - va_start(arg, formatP); - vsnprintf_P(buffer, len + 1, formatP, arg); - va_end(arg); - } - text(buffer, len); - if (buffer != temp) { - delete[] buffer; - } - delete[] temp; - return len; -} -#endif - -void AsyncWebSocketClient::text(const char * message, size_t len){ - _queueMessage(new AsyncWebSocketBasicMessage(message, len)); -} -void AsyncWebSocketClient::text(const char * message){ - text(message, strlen(message)); -} -void AsyncWebSocketClient::text(uint8_t * message, size_t len){ - text((const char *)message, len); -} -void AsyncWebSocketClient::text(char * message){ - text(message, strlen(message)); -} -void AsyncWebSocketClient::text(const String &message){ - text(message.c_str(), message.length()); -} -void AsyncWebSocketClient::text(const __FlashStringHelper *data){ - PGM_P p = reinterpret_cast(data); - size_t n = 0; - while (1) { - if (pgm_read_byte(p+n) == 0) break; - n += 1; - } - char * message = (char*) malloc(n+1); - if(message){ - for(size_t b=0; b(data); - char * message = (char*) malloc(len); - if(message){ - for(size_t b=0; bremoteIP(); -} - -uint16_t AsyncWebSocketClient::remotePort() { - if(!_client) { - return 0; - } - return _client->remotePort(); -} - - - -/* - * Async Web Socket - Each separate socket location - */ - -AsyncWebSocket::AsyncWebSocket(const String& url) - :_url(url) - ,_clients(LinkedList([](AsyncWebSocketClient *c){ delete c; })) - ,_cNextId(1) - ,_enabled(true) - ,_buffers(LinkedList([](AsyncWebSocketMessageBuffer *b){ delete b; })) -{ - _eventHandler = NULL; -} - -AsyncWebSocket::~AsyncWebSocket(){} - -void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - if(_eventHandler != NULL){ - _eventHandler(this, client, type, arg, data, len); - } -} - -void AsyncWebSocket::_addClient(AsyncWebSocketClient * client){ - _clients.add(client); -} - -void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){ - - _clients.remove_first([=](AsyncWebSocketClient * c){ - return c->id() == client->id(); - }); -} - -bool AsyncWebSocket::availableForWriteAll(){ - for(const auto& c: _clients){ - if(c->queueIsFull()) return false; - } - return true; -} - -bool AsyncWebSocket::availableForWrite(uint32_t id){ - for(const auto& c: _clients){ - if(c->queueIsFull() && (c->id() == id )) return false; - } - return true; -} - -size_t AsyncWebSocket::count() const { - return _clients.count_if([](AsyncWebSocketClient * c){ - return c->status() == WS_CONNECTED; - }); -} - -AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id){ - for(const auto &c: _clients){ - if(c->id() == id && c->status() == WS_CONNECTED){ - return c; - } - } - return nullptr; -} - - -void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message){ - AsyncWebSocketClient * c = client(id); - if(c) - c->close(code, message); -} - -void AsyncWebSocket::closeAll(uint16_t code, const char * message){ - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->close(code, message); - } -} - -void AsyncWebSocket::cleanupClients(uint16_t maxClients) -{ - if (count() > maxClients){ - _clients.front()->close(); - } -} - -void AsyncWebSocket::ping(uint32_t id, uint8_t *data, size_t len){ - AsyncWebSocketClient * c = client(id); - if(c) - c->ping(data, len); -} - -void AsyncWebSocket::pingAll(uint8_t *data, size_t len){ - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->ping(data, len); - } -} - -void AsyncWebSocket::text(uint32_t id, const char * message, size_t len){ - AsyncWebSocketClient * c = client(id); - if(c) - c->text(message, len); -} - -void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){ - if (!buffer) return; - buffer->lock(); - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED){ - c->text(buffer); - } - } - buffer->unlock(); - _cleanBuffers(); -} - - -void AsyncWebSocket::textAll(const char * message, size_t len){ - AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len); - textAll(WSBuffer); -} - -void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){ - AsyncWebSocketClient * c = client(id); - if(c) - c->binary(message, len); -} - -void AsyncWebSocket::binaryAll(const char * message, size_t len){ - AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len); - binaryAll(buffer); -} - -void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) -{ - if (!buffer) return; - buffer->lock(); - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->binary(buffer); - } - buffer->unlock(); - _cleanBuffers(); -} - -void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){ - AsyncWebSocketClient * c = client(id); - if(c) - c->message(message); -} - -void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message){ - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->message(message); - } - _cleanBuffers(); -} - -size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){ - AsyncWebSocketClient * c = client(id); - if(c){ - va_list arg; - va_start(arg, format); - size_t len = c->printf(format, arg); - va_end(arg); - return len; - } - return 0; -} - -size_t AsyncWebSocket::printfAll(const char *format, ...) { - va_list arg; - char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ - return 0; - } - va_start(arg, format); - size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); - va_end(arg); - delete[] temp; - - AsyncWebSocketMessageBuffer * buffer = makeBuffer(len); - if (!buffer) { - return 0; - } - - va_start(arg, format); - vsnprintf( (char *)buffer->get(), len + 1, format, arg); - va_end(arg); - - textAll(buffer); - return len; -} - -#ifndef ESP32 -size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){ - AsyncWebSocketClient * c = client(id); - if(c != NULL){ - va_list arg; - va_start(arg, formatP); - size_t len = c->printf_P(formatP, arg); - va_end(arg); - return len; - } - return 0; -} -#endif - -size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { - va_list arg; - char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ - return 0; - } - va_start(arg, formatP); - size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); - va_end(arg); - delete[] temp; - - AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); - if (!buffer) { - return 0; - } - - va_start(arg, formatP); - vsnprintf_P((char *)buffer->get(), len + 1, formatP, arg); - va_end(arg); - - textAll(buffer); - return len; -} - -void AsyncWebSocket::text(uint32_t id, const char * message){ - text(id, message, strlen(message)); -} -void AsyncWebSocket::text(uint32_t id, uint8_t * message, size_t len){ - text(id, (const char *)message, len); -} -void AsyncWebSocket::text(uint32_t id, char * message){ - text(id, message, strlen(message)); -} -void AsyncWebSocket::text(uint32_t id, const String &message){ - text(id, message.c_str(), message.length()); -} -void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *message){ - AsyncWebSocketClient * c = client(id); - if(c != NULL) - c->text(message); -} -void AsyncWebSocket::textAll(const char * message){ - textAll(message, strlen(message)); -} -void AsyncWebSocket::textAll(uint8_t * message, size_t len){ - textAll((const char *)message, len); -} -void AsyncWebSocket::textAll(char * message){ - textAll(message, strlen(message)); -} -void AsyncWebSocket::textAll(const String &message){ - textAll(message.c_str(), message.length()); -} -void AsyncWebSocket::textAll(const __FlashStringHelper *message){ - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c->text(message); - } -} -void AsyncWebSocket::binary(uint32_t id, const char * message){ - binary(id, message, strlen(message)); -} -void AsyncWebSocket::binary(uint32_t id, uint8_t * message, size_t len){ - binary(id, (const char *)message, len); -} -void AsyncWebSocket::binary(uint32_t id, char * message){ - binary(id, message, strlen(message)); -} -void AsyncWebSocket::binary(uint32_t id, const String &message){ - binary(id, message.c_str(), message.length()); -} -void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *message, size_t len){ - AsyncWebSocketClient * c = client(id); - if(c != NULL) - c-> binary(message, len); -} -void AsyncWebSocket::binaryAll(const char * message){ - binaryAll(message, strlen(message)); -} -void AsyncWebSocket::binaryAll(uint8_t * message, size_t len){ - binaryAll((const char *)message, len); -} -void AsyncWebSocket::binaryAll(char * message){ - binaryAll(message, strlen(message)); -} -void AsyncWebSocket::binaryAll(const String &message){ - binaryAll(message.c_str(), message.length()); -} -void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){ - for(const auto& c: _clients){ - if(c->status() == WS_CONNECTED) - c-> binary(message, len); - } - } - -const char * WS_STR_CONNECTION = "Connection"; -const char * WS_STR_UPGRADE = "Upgrade"; -const char * WS_STR_ORIGIN = "Origin"; -const char * WS_STR_VERSION = "Sec-WebSocket-Version"; -const char * WS_STR_KEY = "Sec-WebSocket-Key"; -const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol"; -const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept"; -const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ - if(!_enabled) - return false; - - if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) - return false; - - request->addInterestingHeader(WS_STR_CONNECTION); - request->addInterestingHeader(WS_STR_UPGRADE); - request->addInterestingHeader(WS_STR_ORIGIN); - request->addInterestingHeader(WS_STR_VERSION); - request->addInterestingHeader(WS_STR_KEY); - request->addInterestingHeader(WS_STR_PROTOCOL); - return true; -} - -void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){ - if(!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)){ - request->send(400); - return; - } - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())){ - return request->requestAuthentication(); - } - AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); - if(version->value().toInt() != 13){ - AsyncWebServerResponse *response = request->beginResponse(400); - response->addHeader(WS_STR_VERSION,"13"); - request->send(response); - return; - } - AsyncWebHeader* key = request->getHeader(WS_STR_KEY); - AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this); - if(request->hasHeader(WS_STR_PROTOCOL)){ - AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); - //ToDo: check protocol - response->addHeader(WS_STR_PROTOCOL, protocol->value()); - } - request->send(response); -} - -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) -{ - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); - if (buffer) { - AsyncWebLockGuard l(_lock); - _buffers.add(buffer); - } - return buffer; -} - -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) -{ - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); - - if (buffer) { - AsyncWebLockGuard l(_lock); - _buffers.add(buffer); - } - - return buffer; -} - -void AsyncWebSocket::_cleanBuffers() -{ - AsyncWebLockGuard l(_lock); - - for(AsyncWebSocketMessageBuffer * c: _buffers){ - if(c && c->canDelete()){ - _buffers.remove(c); - } - } -} - -AsyncWebSocket::AsyncWebSocketClientLinkedList AsyncWebSocket::getClients() const { - return _clients; -} - -/* - * Response to Web Socket request - sends the authorization and detaches the TCP Client from the web server - * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 - */ - -AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server){ - _server = server; - _code = 101; - _sendContentLength = false; - - uint8_t * hash = (uint8_t*)malloc(20); - if(hash == NULL){ - _state = RESPONSE_FAILED; - return; - } - char * buffer = (char *) malloc(33); - if(buffer == NULL){ - free(hash); - _state = RESPONSE_FAILED; - return; - } -#ifdef ESP8266 - sha1(key + WS_STR_UUID, hash); -#else - (String&)key += WS_STR_UUID; - SHA1_CTX ctx; - SHA1Init(&ctx); - SHA1Update(&ctx, (const unsigned char*)key.c_str(), key.length()); - SHA1Final(hash, &ctx); -#endif - base64_encodestate _state; - base64_init_encodestate(&_state); - int len = base64_encode_block((const char *) hash, 20, buffer, &_state); - len = base64_encode_blockend((buffer + len), &_state); - addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); - addHeader(WS_STR_UPGRADE, "websocket"); - addHeader(WS_STR_ACCEPT,buffer); - free(buffer); - free(hash); -} - -void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request){ - if(_state == RESPONSE_FAILED){ - request->client()->close(true); - return; - } - String out = _assembleHead(request->version()); - request->client()->write(out.c_str(), _headLength); - _state = RESPONSE_WAIT_ACK; -} - -size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ - (void)time; - if(len){ - new AsyncWebSocketClient(request, _server); - } - return 0; -} diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h b/lib/ESPAsyncWebServer/src/AsyncWebSocket.h deleted file mode 100644 index 5b03aceb..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSocket.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef ASYNCWEBSOCKET_H_ -#define ASYNCWEBSOCKET_H_ - -#include -#ifdef ESP32 -#include -#define WS_MAX_QUEUED_MESSAGES 32 -#else -#include -#define WS_MAX_QUEUED_MESSAGES 8 -#endif -#include - -#include "AsyncWebSynchronization.h" - -#ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif -#endif - -#ifdef ESP32 -#define DEFAULT_MAX_WS_CLIENTS 8 -#else -#define DEFAULT_MAX_WS_CLIENTS 4 -#endif - -class AsyncWebSocket; -class AsyncWebSocketResponse; -class AsyncWebSocketClient; -class AsyncWebSocketControl; - -typedef struct { - /** Message type as defined by enum AwsFrameType. - * Note: Applications will only see WS_TEXT and WS_BINARY. - * All other types are handled by the library. */ - uint8_t message_opcode; - /** Frame number of a fragmented message. */ - uint32_t num; - /** Is this the last frame in a fragmented message ?*/ - uint8_t final; - /** Is this frame masked? */ - uint8_t masked; - /** Message type as defined by enum AwsFrameType. - * This value is the same as message_opcode for non-fragmented - * messages, but may also be WS_CONTINUATION in a fragmented message. */ - uint8_t opcode; - /** Length of the current frame. - * This equals the total length of the message if num == 0 && final == true */ - uint64_t len; - /** Mask key */ - uint8_t mask[4]; - /** Offset of the data inside the current frame. */ - uint64_t index; -} AwsFrameInfo; - -typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus; -typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType; -typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; -typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; - -class AsyncWebSocketMessageBuffer { - private: - uint8_t * _data; - size_t _len; - bool _lock; - uint32_t _count; - - public: - AsyncWebSocketMessageBuffer(); - AsyncWebSocketMessageBuffer(size_t size); - AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); - AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); - AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); - ~AsyncWebSocketMessageBuffer(); - void operator ++(int i) { (void)i; _count++; } - void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; } - bool reserve(size_t size); - void lock() { _lock = true; } - void unlock() { _lock = false; } - uint8_t * get() { return _data; } - size_t length() { return _len; } - uint32_t count() { return _count; } - bool canDelete() { return (!_count && !_lock); } - - friend AsyncWebSocket; - -}; - -class AsyncWebSocketMessage { - protected: - uint8_t _opcode; - bool _mask; - AwsMessageStatus _status; - public: - AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){} - virtual ~AsyncWebSocketMessage(){} - virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){} - virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; } - virtual bool finished(){ return _status != WS_MSG_SENDING; } - virtual bool betweenFrames() const { return false; } -}; - -class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage { - private: - size_t _len; - size_t _sent; - size_t _ack; - size_t _acked; - uint8_t * _data; -public: - AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false); - AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false); - virtual ~AsyncWebSocketBasicMessage() override; - virtual bool betweenFrames() const override { return _acked == _ack; } - virtual void ack(size_t len, uint32_t time) override ; - virtual size_t send(AsyncClient *client) override ; -}; - -class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { - private: - uint8_t * _data; - size_t _len; - size_t _sent; - size_t _ack; - size_t _acked; - AsyncWebSocketMessageBuffer * _WSbuffer; -public: - AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false); - virtual ~AsyncWebSocketMultiMessage() override; - virtual bool betweenFrames() const override { return _acked == _ack; } - virtual void ack(size_t len, uint32_t time) override ; - virtual size_t send(AsyncClient *client) override ; -}; - -class AsyncWebSocketClient { - private: - AsyncClient *_client; - AsyncWebSocket *_server; - uint32_t _clientId; - AwsClientStatus _status; - - LinkedList _controlQueue; - LinkedList _messageQueue; - - uint8_t _pstate; - AwsFrameInfo _pinfo; - - uint32_t _lastMessageTime; - uint32_t _keepAlivePeriod; - - void _queueMessage(AsyncWebSocketMessage *dataMessage); - void _queueControl(AsyncWebSocketControl *controlMessage); - void _runQueue(); - - public: - void *_tempObject; - - AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); - ~AsyncWebSocketClient(); - - //client id increments for the given server - uint32_t id(){ return _clientId; } - AwsClientStatus status(){ return _status; } - AsyncClient* client(){ return _client; } - AsyncWebSocket *server(){ return _server; } - AwsFrameInfo const &pinfo() const { return _pinfo; } - - IPAddress remoteIP(); - uint16_t remotePort(); - - //control frames - void close(uint16_t code=0, const char * message=NULL); - void ping(uint8_t *data=NULL, size_t len=0); - - //set auto-ping period in seconds. disabled if zero (default) - void keepAlivePeriod(uint16_t seconds){ - _keepAlivePeriod = seconds * 1000; - } - uint16_t keepAlivePeriod(){ - return (uint16_t)(_keepAlivePeriod / 1000); - } - - //data packets - void message(AsyncWebSocketMessage *message){ _queueMessage(message); } - bool queueIsFull(); - - size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3))); -#ifndef ESP32 - size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); -#endif - void text(const char * message, size_t len); - void text(const char * message); - void text(uint8_t * message, size_t len); - void text(char * message); - void text(const String &message); - void text(const __FlashStringHelper *data); - void text(AsyncWebSocketMessageBuffer *buffer); - - void binary(const char * message, size_t len); - void binary(const char * message); - void binary(uint8_t * message, size_t len); - void binary(char * message); - void binary(const String &message); - void binary(const __FlashStringHelper *data, size_t len); - void binary(AsyncWebSocketMessageBuffer *buffer); - - bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; } - - //system callbacks (do not call) - void _onAck(size_t len, uint32_t time); - void _onError(int8_t); - void _onPoll(); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void *pbuf, size_t plen); -}; - -typedef std::function AwsEventHandler; - -//WebServer Handler implementation that plays the role of a socket server -class AsyncWebSocket: public AsyncWebHandler { - public: - typedef LinkedList AsyncWebSocketClientLinkedList; - private: - String _url; - AsyncWebSocketClientLinkedList _clients; - uint32_t _cNextId; - AwsEventHandler _eventHandler; - bool _enabled; - AsyncWebLock _lock; - - public: - AsyncWebSocket(const String& url); - ~AsyncWebSocket(); - const char * url() const { return _url.c_str(); } - void enable(bool e){ _enabled = e; } - bool enabled() const { return _enabled; } - bool availableForWriteAll(); - bool availableForWrite(uint32_t id); - - size_t count() const; - AsyncWebSocketClient * client(uint32_t id); - bool hasClient(uint32_t id){ return client(id) != NULL; } - - void close(uint32_t id, uint16_t code=0, const char * message=NULL); - void closeAll(uint16_t code=0, const char * message=NULL); - void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); - - void ping(uint32_t id, uint8_t *data=NULL, size_t len=0); - void pingAll(uint8_t *data=NULL, size_t len=0); // done - - void text(uint32_t id, const char * message, size_t len); - void text(uint32_t id, const char * message); - void text(uint32_t id, uint8_t * message, size_t len); - void text(uint32_t id, char * message); - void text(uint32_t id, const String &message); - void text(uint32_t id, const __FlashStringHelper *message); - - void textAll(const char * message, size_t len); - void textAll(const char * message); - void textAll(uint8_t * message, size_t len); - void textAll(char * message); - void textAll(const String &message); - void textAll(const __FlashStringHelper *message); // need to convert - void textAll(AsyncWebSocketMessageBuffer * buffer); - - void binary(uint32_t id, const char * message, size_t len); - void binary(uint32_t id, const char * message); - void binary(uint32_t id, uint8_t * message, size_t len); - void binary(uint32_t id, char * message); - void binary(uint32_t id, const String &message); - void binary(uint32_t id, const __FlashStringHelper *message, size_t len); - - void binaryAll(const char * message, size_t len); - void binaryAll(const char * message); - void binaryAll(uint8_t * message, size_t len); - void binaryAll(char * message); - void binaryAll(const String &message); - void binaryAll(const __FlashStringHelper *message, size_t len); - void binaryAll(AsyncWebSocketMessageBuffer * buffer); - - void message(uint32_t id, AsyncWebSocketMessage *message); - void messageAll(AsyncWebSocketMultiMessage *message); - - size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4))); - size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3))); -#ifndef ESP32 - size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4))); -#endif - size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); - - //event listener - void onEvent(AwsEventHandler handler){ - _eventHandler = handler; - } - - //system callbacks (do not call) - uint32_t _getNextId(){ return _cNextId++; } - void _addClient(AsyncWebSocketClient * client); - void _handleDisconnect(AsyncWebSocketClient * client); - void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; - - - // messagebuffer functions/objects. - AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); - AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); - LinkedList _buffers; - void _cleanBuffers(); - - AsyncWebSocketClientLinkedList getClients() const; -}; - -//WebServer response to authenticate the socket and detach the tcp client from the web server request -class AsyncWebSocketResponse: public AsyncWebServerResponse { - private: - String _content; - AsyncWebSocket *_server; - public: - AsyncWebSocketResponse(const String& key, AsyncWebSocket *server); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } -}; - - -#endif /* ASYNCWEBSOCKET_H_ */ diff --git a/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h b/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h deleted file mode 100644 index f36c52dc..00000000 --- a/lib/ESPAsyncWebServer/src/AsyncWebSynchronization.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef ASYNCWEBSYNCHRONIZATION_H_ -#define ASYNCWEBSYNCHRONIZATION_H_ - -// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default - -#include - -#ifdef ESP32 - -// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore -class AsyncWebLock -{ -private: - SemaphoreHandle_t _lock; - mutable void *_lockedBy; - -public: - AsyncWebLock() { - _lock = xSemaphoreCreateBinary(); - _lockedBy = NULL; - xSemaphoreGive(_lock); - } - - ~AsyncWebLock() { - vSemaphoreDelete(_lock); - } - - bool lock() const { - extern void *pxCurrentTCB; - if (_lockedBy != pxCurrentTCB) { - xSemaphoreTake(_lock, portMAX_DELAY); - _lockedBy = pxCurrentTCB; - return true; - } - return false; - } - - void unlock() const { - _lockedBy = NULL; - xSemaphoreGive(_lock); - } -}; - -#else - -// This is the 8266 version of the Sync Lock which is currently unimplemented -class AsyncWebLock -{ - -public: - AsyncWebLock() { - } - - ~AsyncWebLock() { - } - - bool lock() const { - return false; - } - - void unlock() const { - } -}; -#endif - -class AsyncWebLockGuard -{ -private: - const AsyncWebLock *_lock; - -public: - AsyncWebLockGuard(const AsyncWebLock &l) { - if (l.lock()) { - _lock = &l; - } else { - _lock = NULL; - } - } - - ~AsyncWebLockGuard() { - if (_lock) { - _lock->unlock(); - } - } -}; - -#endif // ASYNCWEBSYNCHRONIZATION_H_ \ No newline at end of file diff --git a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h deleted file mode 100644 index 3b2fbd96..00000000 --- a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h +++ /dev/null @@ -1,501 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef _ESPAsyncWebServer_H_ -#define _ESPAsyncWebServer_H_ - -#include - -#include "Arduino.h" -#include "FS.h" -#include "StringArray.h" - -#ifdef ESP32 -#include -#include -#elif defined(ESP8266) -#include -#include -#else -#error Platform not supported -#endif - -#ifdef ASYNCWEBSERVER_REGEX -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE -#else -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) -#endif - -#define DEBUGF(...) //Serial.printf(__VA_ARGS__) - -class AsyncWebServer; -class AsyncWebServerRequest; -class AsyncWebServerResponse; -class AsyncWebHeader; -class AsyncWebParameter; -class AsyncWebRewrite; -class AsyncWebHandler; -class AsyncStaticWebHandler; -class AsyncCallbackWebHandler; -class AsyncResponseStream; - -#ifdef ESP32 -#ifdef WEBSERVER_H -#define HTTP_ENUM -#endif -#elif defined(ESP8266) -#ifdef ESP8266WEBSERVER_H -#define HTTP_ENUM -#endif -#endif - -#ifndef HTTP_ENUM -typedef enum { HTTP_GET = 0b00000001, - HTTP_POST = 0b00000010, - HTTP_DELETE = 0b00000100, - HTTP_PUT = 0b00001000, - HTTP_PATCH = 0b00010000, - HTTP_HEAD = 0b00100000, - HTTP_OPTIONS = 0b01000000, - HTTP_ANY = 0b01111111, -} WebRequestMethod; -#endif - -//if this value is returned when asked for data, packet will not be sent and you will be asked for data again -#define RESPONSE_TRY_AGAIN 0xFFFFFFFF - -typedef uint8_t WebRequestMethodComposite; -typedef std::function ArDisconnectHandler; - -/* - * PARAMETER :: Chainable object to hold GET/POST and FILE parameters - * */ - -class AsyncWebParameter { - private: - String _name; - String _value; - size_t _size; - bool _isForm; - bool _isFile; - - public: - AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} - const String& name() const { return _name; } - const String& value() const { return _value; } - size_t size() const { return _size; } - bool isPost() const { return _isForm; } - bool isFile() const { return _isFile; } -}; - -/* - * HEADER :: Chainable object to hold the headers - * */ - -class AsyncWebHeader { - private: - String _name; - String _value; - - public: - AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {} - AsyncWebHeader(const String& data) : _name(), _value() { - if (!data) return; - int index = data.indexOf(':'); - if (index < 0) return; - _name = data.substring(0, index); - _value = data.substring(index + 2); - } - ~AsyncWebHeader() {} - const String& name() const { return _name; } - const String& value() const { return _value; } - String toString() const { return String(_name + ": " + _value + "\r\n"); } -}; - -/* - * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect - * */ - -typedef enum { RCT_NOT_USED = -1, - RCT_DEFAULT = 0, - RCT_HTTP, - RCT_WS, - RCT_EVENT, - RCT_MAX } RequestedConnectionType; - -typedef std::function AwsResponseFiller; -typedef std::function AwsTemplateProcessor; - -class AsyncWebServerRequest { - using File = fs::File; - using FS = fs::FS; - friend class AsyncWebServer; - friend class AsyncCallbackWebHandler; - - private: - AsyncClient* _client; - AsyncWebServer* _server; - AsyncWebHandler* _handler; - AsyncWebServerResponse* _response; - StringArray _interestingHeaders; - ArDisconnectHandler _onDisconnectfn; - - String _temp; - uint8_t _parseState; - - uint8_t _version; - WebRequestMethodComposite _method; - String _url; - String _host; - String _contentType; - String _boundary; - String _authorization; - RequestedConnectionType _reqconntype; - void _removeNotInterestingHeaders(); - bool _isDigest; - bool _isMultipart; - bool _isPlainPost; - bool _expectingContinue; - size_t _contentLength; - size_t _parsedLength; - - LinkedList _headers; - LinkedList _params; - LinkedList _pathParams; - - uint8_t _multiParseState; - uint8_t _boundaryPosition; - size_t _itemStartIndex; - size_t _itemSize; - String _itemName; - String _itemFilename; - String _itemType; - String _itemValue; - uint8_t* _itemBuffer; - size_t _itemBufferIndex; - bool _itemIsFile; - - void _onPoll(); - void _onAck(size_t len, uint32_t time); - void _onError(int8_t error); - void _onTimeout(uint32_t time); - void _onDisconnect(); - void _onData(void* buf, size_t len); - - void _addParam(AsyncWebParameter*); - void _addPathParam(const char* param); - - bool _parseReqHead(); - bool _parseReqHeader(); - void _parseLine(); - void _parsePlainPostChar(uint8_t data); - void _parseMultipartPostByte(uint8_t data, bool last); - void _addGetParams(const String& params); - - void _handleUploadStart(); - void _handleUploadByte(uint8_t data, bool last); - void _handleUploadEnd(); - - public: - File _tempFile; - void* _tempObject; - - AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); - ~AsyncWebServerRequest(); - - AsyncClient* client() { return _client; } - uint8_t version() const { return _version; } - WebRequestMethodComposite method() const { return _method; } - const String& url() const { return _url; } - const String& host() const { return _host; } - const String& contentType() const { return _contentType; } - size_t contentLength() const { return _contentLength; } - bool multipart() const { return _isMultipart; } - const char* methodToString() const; - const char* requestedConnTypeToString() const; - RequestedConnectionType requestedConnType() const { return _reqconntype; } - bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); - void onDisconnect(ArDisconnectHandler fn); - - //hash is the string representation of: - // base64(user:pass) for basic or - // user:realm:md5(user:realm:pass) for digest - bool authenticate(const char* hash); - bool authenticate(const char* username, const char* password, const char* realm = NULL, bool passwordIsHash = false); - void requestAuthentication(const char* realm = NULL, bool isDigest = true); - - void setHandler(AsyncWebHandler* handler) { _handler = handler; } - void addInterestingHeader(const String& name); - - void redirect(const String& url); - - void send(AsyncWebServerResponse* response); - void send(int code, const String& contentType = String(), const String& content = String()); - void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); - void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); - - AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String()); - AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = 1460); - AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); - AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); - - size_t headers() const; // get header count - bool hasHeader(const String& name) const; // check if header exists - bool hasHeader(const __FlashStringHelper* data) const; // check if header exists - - AsyncWebHeader* getHeader(const String& name) const; - AsyncWebHeader* getHeader(const __FlashStringHelper* data) const; - AsyncWebHeader* getHeader(size_t num) const; - - size_t params() const; // get arguments count - bool hasParam(const String& name, bool post = false, bool file = false) const; - bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const; - - AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const; - AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const; - AsyncWebParameter* getParam(size_t num) const; - - size_t args() const { return params(); } // get arguments count - const String& arg(const String& name) const; // get request argument value by name - const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name) - const String& arg(size_t i) const; // get request argument value by number - const String& argName(size_t i) const; // get request argument name by number - bool hasArg(const char* name) const; // check if argument exists - bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists - - const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; - - const String& header(const char* name) const; // get request header value by name - const String& header(const __FlashStringHelper* data) const; // get request header value by F(name) - const String& header(size_t i) const; // get request header value by number - const String& headerName(size_t i) const; // get request header name by number - String urlDecode(const String& text) const; -}; - -/* - * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) - * */ - -typedef std::function ArRequestFilterFunction; - -bool ON_STA_FILTER(AsyncWebServerRequest* request); - -bool ON_AP_FILTER(AsyncWebServerRequest* request); - -/* - * REWRITE :: One instance can be handle any Request (done by the Server) - * */ - -class AsyncWebRewrite { - protected: - String _from; - String _toUrl; - String _params; - ArRequestFilterFunction _filter; - - public: - AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to), _params(String()), _filter(NULL) { - int index = _toUrl.indexOf('?'); - if (index > 0) { - _params = _toUrl.substring(index + 1); - _toUrl = _toUrl.substring(0, index); - } - } - virtual ~AsyncWebRewrite() {} - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { - _filter = fn; - return *this; - } - bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); } - const String& from(void) const { return _from; } - const String& toUrl(void) const { return _toUrl; } - const String& params(void) const { return _params; } - virtual bool match(AsyncWebServerRequest* request) { return from() == request->url() && filter(request); } -}; - -/* - * HANDLER :: One instance can be attached to any Request (done by the Server) - * */ - -class AsyncWebHandler { - protected: - ArRequestFilterFunction _filter; - String _username; - String _password; - - public: - AsyncWebHandler() : _username(""), _password("") {} - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { - _filter = fn; - return *this; - } - AsyncWebHandler& setAuthentication(const char* username, const char* password) { - _username = String(username); - _password = String(password); - return *this; - }; - bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } - virtual ~AsyncWebHandler() {} - virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) { - return false; - } - virtual void handleRequest(AsyncWebServerRequest* request __attribute__((unused))) {} - virtual void handleUpload(AsyncWebServerRequest* request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {} - virtual void handleBody(AsyncWebServerRequest* request __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} - virtual bool isRequestHandlerTrivial() { return true; } -}; - -/* - * RESPONSE :: One instance is created for each Request (attached by the Handler) - * */ - -typedef enum { - RESPONSE_SETUP, - RESPONSE_HEADERS, - RESPONSE_CONTENT, - RESPONSE_WAIT_ACK, - RESPONSE_END, - RESPONSE_FAILED -} WebResponseState; - -class AsyncWebServerResponse { - protected: - int _code; - LinkedList _headers; - String _contentType; - size_t _contentLength; - bool _sendContentLength; - bool _chunked; - size_t _headLength; - size_t _sentLength; - size_t _ackedLength; - size_t _writtenLength; - WebResponseState _state; - const char* _responseCodeToString(int code); - - public: - AsyncWebServerResponse(); - virtual ~AsyncWebServerResponse(); - virtual void setCode(int code); - virtual void setContentLength(size_t len); - virtual void setContentType(const String& type); - virtual void addHeader(const String& name, const String& value); - virtual String _assembleHead(uint8_t version); - virtual bool _started() const; - virtual bool _finished() const; - virtual bool _failed() const; - virtual bool _sourceValid() const; - virtual void _respond(AsyncWebServerRequest* request); - virtual size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); -}; - -/* - * SERVER :: One instance - * */ - -typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; - -class AsyncWebServer { - protected: - AsyncServer _server; - LinkedList _rewrites; - LinkedList _handlers; - AsyncCallbackWebHandler* _catchAllHandler; - - public: - AsyncWebServer(uint16_t port); - ~AsyncWebServer(); - - void begin(); - void end(); - -#if ASYNC_TCP_SSL_ENABLED - void onSslFileRequest(AcSSlFileHandler cb, void* arg); - void beginSecure(const char* cert, const char* private_key_file, const char* password); -#endif - - AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); - bool removeRewrite(AsyncWebRewrite* rewrite); - AsyncWebRewrite& rewrite(const char* from, const char* to); - - AsyncWebHandler& addHandler(AsyncWebHandler* handler); - bool removeHandler(AsyncWebHandler* handler); - - AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); - - AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); - - void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned - void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) - - void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody - - void _handleDisconnect(AsyncWebServerRequest* request); - void _attachHandler(AsyncWebServerRequest* request); - void _rewriteRequest(AsyncWebServerRequest* request); -}; - -class DefaultHeaders { - using headers_t = LinkedList; - headers_t _headers; - - DefaultHeaders() - : _headers(headers_t([](AsyncWebHeader* h) { delete h; })) {} - - public: - using ConstIterator = headers_t::ConstIterator; - - void addHeader(const String& name, const String& value) { - _headers.add(new AsyncWebHeader(name, value)); - } - - ConstIterator begin() const { return _headers.begin(); } - ConstIterator end() const { return _headers.end(); } - - DefaultHeaders(DefaultHeaders const&) = delete; - DefaultHeaders& operator=(DefaultHeaders const&) = delete; - static DefaultHeaders& Instance() { - static DefaultHeaders instance; - return instance; - } -}; - -#include "AsyncEventSource.h" -#include "AsyncWebSocket.h" -#include "WebHandlerImpl.h" -#include "WebResponseImpl.h" - -#endif /* _AsyncWebServer_H_ */ diff --git a/lib/ESPAsyncWebServer/src/SPIFFSEditor.cpp b/lib/ESPAsyncWebServer/src/SPIFFSEditor.cpp deleted file mode 100644 index a84fa87d..00000000 --- a/lib/ESPAsyncWebServer/src/SPIFFSEditor.cpp +++ /dev/null @@ -1,544 +0,0 @@ -#include "SPIFFSEditor.h" -#include - -//File: edit.htm.gz, Size: 4151 -#define edit_htm_gz_len 4151 -const uint8_t edit_htm_gz[] PROGMEM = { - 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68, - 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED, - 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6, - 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB, - 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A, - 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61, - 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7, - 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02, - 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C, - 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A, - 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89, - 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76, - 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D, - 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9, - 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B, - 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91, - 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78, - 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78, - 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98, - 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E, - 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41, - 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21, - 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F, - 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74, - 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C, - 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0, - 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C, - 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30, - 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9, - 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61, - 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B, - 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9, - 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B, - 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD, - 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3, - 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77, - 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83, - 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF, - 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3, - 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55, - 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3, - 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF, - 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF, - 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60, - 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1, - 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE, - 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F, - 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0, - 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9, - 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5, - 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15, - 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74, - 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D, - 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD, - 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A, - 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6, - 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2, - 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF, - 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53, - 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2, - 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A, - 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83, - 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26, - 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0, - 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0, - 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84, - 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99, - 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5, - 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9, - 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87, - 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F, - 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6, - 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B, - 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D, - 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25, - 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3, - 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F, - 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35, - 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A, - 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6, - 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7, - 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A, - 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9, - 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97, - 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36, - 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C, - 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A, - 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C, - 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F, - 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11, - 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16, - 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA, - 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB, - 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A, - 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6, - 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28, - 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1, - 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E, - 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E, - 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92, - 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05, - 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8, - 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0, - 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85, - 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40, - 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56, - 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47, - 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA, - 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7, - 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD, - 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61, - 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58, - 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D, - 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8, - 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C, - 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA, - 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49, - 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51, - 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00, - 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A, - 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A, - 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35, - 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F, - 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E, - 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C, - 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64, - 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C, - 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1, - 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B, - 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC, - 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42, - 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B, - 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71, - 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F, - 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28, - 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9, - 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD, - 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6, - 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F, - 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5, - 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8, - 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF, - 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62, - 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C, - 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7, - 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89, - 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29, - 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95, - 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7, - 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB, - 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09, - 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F, - 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60, - 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35, - 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6, - 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B, - 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66, - 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25, - 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E, - 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97, - 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC, - 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE, - 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7, - 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13, - 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0, - 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A, - 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93, - 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E, - 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9, - 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78, - 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5, - 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12, - 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E, - 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35, - 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98, - 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58, - 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3, - 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64, - 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39, - 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D, - 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62, - 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48, - 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D, - 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8, - 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9, - 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30, - 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6, - 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1, - 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56, - 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84, - 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0, - 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC, - 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E, - 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39, - 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B, - 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA, - 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1, - 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1, - 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88, - 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4, - 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC, - 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98, - 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97, - 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8, - 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30, - 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA, - 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B, - 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC, - 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45, - 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD, - 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76, - 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD, - 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76, - 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4, - 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF, - 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4, - 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42, - 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43, - 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B, - 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97, - 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73, - 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B, - 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50, - 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51, - 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25, - 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26, - 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80, - 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9, - 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0, - 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74, - 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA, - 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D, - 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F, - 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2, - 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1, - 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99, - 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D, - 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B, - 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD, - 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F, - 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB, - 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47, - 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59, - 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D, - 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD, - 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94, - 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35, - 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81, - 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D, - 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20, - 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB, - 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B, - 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6, - 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17, - 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8, - 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26, - 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57, - 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36, - 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53, - 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00 -}; - -#define SPIFFS_MAXLENGTH_FILEPATH 32 -const char *excludeListFile = "/.exclude.files"; - -typedef struct ExcludeListS { - char *item; - ExcludeListS *next; -} ExcludeList; - -static ExcludeList *excludes = NULL; - -static bool matchWild(const char *pattern, const char *testee) { - const char *nxPat = NULL, *nxTst = NULL; - - while (*testee) { - if (( *pattern == '?' ) || (*pattern == *testee)){ - pattern++;testee++; - continue; - } - if (*pattern=='*'){ - nxPat=pattern++; nxTst=testee; - continue; - } - if (nxPat){ - pattern = nxPat+1; testee=++nxTst; - continue; - } - return false; - } - while (*pattern=='*'){pattern++;} - return (*pattern == 0); -} - -static bool addExclude(const char *item){ - size_t len = strlen(item); - if(!len){ - return false; - } - ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList)); - if(!e){ - return false; - } - e->item = (char *)malloc(len+1); - if(!e->item){ - free(e); - return false; - } - memcpy(e->item, item, len+1); - e->next = excludes; - excludes = e; - return true; -} - -static void loadExcludeList(fs::FS &_fs, const char *filename){ - static char linebuf[SPIFFS_MAXLENGTH_FILEPATH]; - fs::File excludeFile=_fs.open(filename, "r"); - if(!excludeFile){ - //addExclude("/*.js.gz"); - return; - } -#ifdef ESP32 - if(excludeFile.isDirectory()){ - excludeFile.close(); - return; - } -#endif - if (excludeFile.size() > 0){ - uint8_t idx; - bool isOverflowed = false; - while (excludeFile.available()){ - linebuf[0] = '\0'; - idx = 0; - int lastChar; - do { - lastChar = excludeFile.read(); - if(lastChar != '\r'){ - linebuf[idx++] = (char) lastChar; - } - } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH)); - - if(isOverflowed){ - isOverflowed = (lastChar != '\n'); - continue; - } - isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH); - linebuf[idx-1] = '\0'; - if(!addExclude(linebuf)){ - excludeFile.close(); - return; - } - } - } - excludeFile.close(); -} - -static bool isExcluded(fs::FS &_fs, const char *filename) { - if(excludes == NULL){ - loadExcludeList(_fs, excludeListFile); - } - ExcludeList *e = excludes; - while(e){ - if (matchWild(e->item, filename)){ - return true; - } - e = e->next; - } - return false; -} - -// WEB HANDLER IMPLEMENTATION - -#ifdef ESP32 -SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password) -#else -SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs) -#endif -:_fs(fs) -,_username(username) -,_password(password) -,_authenticated(false) -,_startTime(0) -{} - -bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){ - if(request->url().equalsIgnoreCase("/edit")){ - if(request->method() == HTTP_GET){ - if(request->hasParam("list")) - return true; - if(request->hasParam("edit")){ - request->_tempFile = _fs.open(request->arg("edit"), "r"); - if(!request->_tempFile){ - return false; - } -#ifdef ESP32 - if(request->_tempFile.isDirectory()){ - request->_tempFile.close(); - return false; - } -#endif - } - if(request->hasParam("download")){ - request->_tempFile = _fs.open(request->arg("download"), "r"); - if(!request->_tempFile){ - return false; - } -#ifdef ESP32 - if(request->_tempFile.isDirectory()){ - request->_tempFile.close(); - return false; - } -#endif - } - request->addInterestingHeader("If-Modified-Since"); - return true; - } - else if(request->method() == HTTP_POST) - return true; - else if(request->method() == HTTP_DELETE) - return true; - else if(request->method() == HTTP_PUT) - return true; - - } - return false; -} - - -void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){ - if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - - if(request->method() == HTTP_GET){ - if(request->hasParam("list")){ - String path = request->getParam("list")->value(); -#ifdef ESP32 - File dir = _fs.open(path); -#else - Dir dir = _fs.openDir(path); -#endif - path = String(); - String output = "["; -#ifdef ESP32 - File entry = dir.openNextFile(); - while(entry){ -#else - while(dir.next()){ - fs::File entry = dir.openFile("r"); -#endif - if (isExcluded(_fs, entry.name())) { -#ifdef ESP32 - entry = dir.openNextFile(); -#endif - continue; - } - if (output != "[") output += ','; - output += "{\"type\":\""; - output += "file"; - output += "\",\"name\":\""; - output += String(entry.name()); - output += "\",\"size\":"; - output += String(entry.size()); - output += "}"; -#ifdef ESP32 - entry = dir.openNextFile(); -#else - entry.close(); -#endif - } -#ifdef ESP32 - dir.close(); -#endif - output += "]"; - request->send(200, "application/json", output); - output = String(); - } - else if(request->hasParam("edit") || request->hasParam("download")){ - request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download")); - } - else { - const char * buildTime = __DATE__ " " __TIME__ " GMT"; - if (request->header("If-Modified-Since").equals(buildTime)) { - request->send(304); - } else { - AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len); - response->addHeader("Content-Encoding", "gzip"); - response->addHeader("Last-Modified", buildTime); - request->send(response); - } - } - } else if(request->method() == HTTP_DELETE){ - if(request->hasParam("path", true)){ - _fs.remove(request->getParam("path", true)->value()); - request->send(200, "", "DELETE: "+request->getParam("path", true)->value()); - } else - request->send(404); - } else if(request->method() == HTTP_POST){ - if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value())) - request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value()); - else - request->send(500); - } else if(request->method() == HTTP_PUT){ - if(request->hasParam("path", true)){ - String filename = request->getParam("path", true)->value(); - if(_fs.exists(filename)){ - request->send(200); - } else { - fs::File f = _fs.open(filename, "w"); - if(f){ - f.write((uint8_t)0x00); - f.close(); - request->send(200, "", "CREATE: "+filename); - } else { - request->send(500); - } - } - } else - request->send(400); - } -} - -void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ - if(!index){ - if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){ - _authenticated = true; - request->_tempFile = _fs.open(filename, "w"); - _startTime = millis(); - } - } - if(_authenticated && request->_tempFile){ - if(len){ - request->_tempFile.write(data,len); - } - if(final){ - request->_tempFile.close(); - } - } -} diff --git a/lib/ESPAsyncWebServer/src/SPIFFSEditor.h b/lib/ESPAsyncWebServer/src/SPIFFSEditor.h deleted file mode 100644 index 00e87293..00000000 --- a/lib/ESPAsyncWebServer/src/SPIFFSEditor.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SPIFFSEditor_H_ -#define SPIFFSEditor_H_ -#include - -#ifdef ESP8266 -#include -#endif - -class SPIFFSEditor : public AsyncWebHandler { - private: - fs::FS _fs; - String _username; - String _password; - bool _authenticated; - uint32_t _startTime; - - public: -#ifdef ESP32 - SPIFFSEditor(const fs::FS& fs, const String& username = String(), const String& password = String()); -#else - SPIFFSEditor(const String& username = String(), const String& password = String(), const fs::FS& fs = LittleFS); -#endif - virtual bool canHandle(AsyncWebServerRequest* request) override final; - virtual void handleRequest(AsyncWebServerRequest* request) override final; - virtual void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final; - virtual bool isRequestHandlerTrivial() override final { return false; } -}; - -#endif diff --git a/lib/ESPAsyncWebServer/src/StringArray.h b/lib/ESPAsyncWebServer/src/StringArray.h deleted file mode 100644 index 4c0aa701..00000000 --- a/lib/ESPAsyncWebServer/src/StringArray.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef STRINGARRAY_H_ -#define STRINGARRAY_H_ - -#include "stddef.h" -#include "WString.h" - -template -class LinkedListNode { - T _value; - public: - LinkedListNode* next; - LinkedListNode(const T val): _value(val), next(nullptr) {} - ~LinkedListNode(){} - const T& value() const { return _value; }; - T& value(){ return _value; } -}; - -template class Item = LinkedListNode> -class LinkedList { - public: - typedef Item ItemType; - typedef std::function OnRemove; - typedef std::function Predicate; - private: - ItemType* _root; - OnRemove _onRemove; - - class Iterator { - ItemType* _node; - public: - Iterator(ItemType* current = nullptr) : _node(current) {} - Iterator(const Iterator& i) : _node(i._node) {} - Iterator& operator ++() { _node = _node->next; return *this; } - bool operator != (const Iterator& i) const { return _node != i._node; } - const T& operator * () const { return _node->value(); } - const T* operator -> () const { return &_node->value(); } - }; - - public: - typedef const Iterator ConstIterator; - ConstIterator begin() const { return ConstIterator(_root); } - ConstIterator end() const { return ConstIterator(nullptr); } - - LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} - ~LinkedList(){} - void add(const T& t){ - auto it = new ItemType(t); - if(!_root){ - _root = it; - } else { - auto i = _root; - while(i->next) i = i->next; - i->next = it; - } - } - T& front() const { - return _root->value(); - } - - bool isEmpty() const { - return _root == nullptr; - } - size_t length() const { - size_t i = 0; - auto it = _root; - while(it){ - i++; - it = it->next; - } - return i; - } - size_t count_if(Predicate predicate) const { - size_t i = 0; - auto it = _root; - while(it){ - if (!predicate){ - i++; - } - else if (predicate(it->value())) { - i++; - } - it = it->next; - } - return i; - } - const T* nth(size_t N) const { - size_t i = 0; - auto it = _root; - while(it){ - if(i++ == N) - return &(it->value()); - it = it->next; - } - return nullptr; - } - bool remove(const T& t){ - auto it = _root; - auto pit = _root; - while(it){ - if(it->value() == t){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - - if (_onRemove) { - _onRemove(it->value()); - } - - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - bool remove_first(Predicate predicate){ - auto it = _root; - auto pit = _root; - while(it){ - if(predicate(it->value())){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - - void free(){ - while(_root != nullptr){ - auto it = _root; - _root = _root->next; - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - } - _root = nullptr; - } -}; - - -class StringArray : public LinkedList { -public: - - StringArray() : LinkedList(nullptr) {} - - bool containsIgnoreCase(const String& str){ - for (const auto& s : *this) { - if (str.equalsIgnoreCase(s)) { - return true; - } - } - return false; - } -}; - - - - -#endif /* STRINGARRAY_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp b/lib/ESPAsyncWebServer/src/WebAuthentication.cpp deleted file mode 100644 index 2feca542..00000000 --- a/lib/ESPAsyncWebServer/src/WebAuthentication.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "WebAuthentication.h" -#include -#ifdef ESP32 -#include "mbedtls/md5.h" -#else -#include "md5.h" -#endif - - -// Basic Auth hash = base64("username:password") - -bool checkBasicAuthentication(const char * hash, const char * username, const char * password){ - if(username == NULL || password == NULL || hash == NULL) - return false; - - size_t toencodeLen = strlen(username)+strlen(password)+1; - size_t encodedLen = base64_encode_expected_len(toencodeLen); - if(strlen(hash) != encodedLen) - return false; - - char *toencode = new char[toencodeLen+1]; - if(toencode == NULL){ - return false; - } - char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; - if(encoded == NULL){ - delete[] toencode; - return false; - } - sprintf(toencode, "%s:%s", username, password); - if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){ - delete[] toencode; - delete[] encoded; - return true; - } - delete[] toencode; - delete[] encoded; - return false; -} - -static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more -#ifdef ESP32 - mbedtls_md5_context _ctx; -#else - md5_context_t _ctx; -#endif - uint8_t i; - uint8_t * _buf = (uint8_t*)malloc(16); - if(_buf == NULL) - return false; - memset(_buf, 0x00, 16); -#ifdef ESP32 - mbedtls_md5_init(&_ctx); - mbedtls_md5_starts(&_ctx); - mbedtls_md5_update(&_ctx, data, len); - mbedtls_md5_finish(&_ctx, _buf); -#else - MD5Init(&_ctx); - MD5Update(&_ctx, data, len); - MD5Final(_buf, &_ctx); -#endif - for(i = 0; i < 16; i++) { - sprintf(output + (i * 2), "%02x", _buf[i]); - } - free(_buf); - return true; -} - -static String genRandomMD5(){ -#ifdef ESP8266 - uint32_t r = RANDOM_REG32; -#else - uint32_t r = rand(); -#endif - char * out = (char*)malloc(33); - if(out == NULL || !getMD5((uint8_t*)(&r), 4, out)) - return ""; - String res = String(out); - free(out); - return res; -} - -static String stringMD5(const String& in){ - char * out = (char*)malloc(33); - if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) - return ""; - String res = String(out); - free(out); - return res; -} - -String generateDigestHash(const char * username, const char * password, const char * realm){ - if(username == NULL || password == NULL || realm == NULL){ - return ""; - } - char * out = (char*)malloc(33); - String res = String(username); - res.concat(":"); - res.concat(realm); - res.concat(":"); - String in = res; - in.concat(password); - if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) - return ""; - res.concat(out); - free(out); - return res; -} - -String requestDigestAuthentication(const char * realm){ - String header = "realm=\""; - if(realm == NULL) - header.concat("asyncesp"); - else - header.concat(realm); - header.concat( "\", qop=\"auth\", nonce=\""); - header.concat(genRandomMD5()); - header.concat("\", opaque=\""); - header.concat(genRandomMD5()); - header.concat("\""); - return header; -} - -bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){ - if(username == NULL || password == NULL || header == NULL || method == NULL){ - //os_printf("AUTH FAIL: missing requred fields\n"); - return false; - } - - String myHeader = String(header); - int nextBreak = myHeader.indexOf(","); - if(nextBreak < 0){ - //os_printf("AUTH FAIL: no variables\n"); - return false; - } - - String myUsername = String(); - String myRealm = String(); - String myNonce = String(); - String myUri = String(); - String myResponse = String(); - String myQop = String(); - String myNc = String(); - String myCnonce = String(); - - myHeader += ", "; - do { - String avLine = myHeader.substring(0, nextBreak); - avLine.trim(); - myHeader = myHeader.substring(nextBreak+1); - nextBreak = myHeader.indexOf(","); - - int eqSign = avLine.indexOf("="); - if(eqSign < 0){ - //os_printf("AUTH FAIL: no = sign\n"); - return false; - } - String varName = avLine.substring(0, eqSign); - avLine = avLine.substring(eqSign + 1); - if(avLine.startsWith("\"")){ - avLine = avLine.substring(1, avLine.length() - 1); - } - - if(varName.equals("username")){ - if(!avLine.equals(username)){ - //os_printf("AUTH FAIL: username\n"); - return false; - } - myUsername = avLine; - } else if(varName.equals("realm")){ - if(realm != NULL && !avLine.equals(realm)){ - //os_printf("AUTH FAIL: realm\n"); - return false; - } - myRealm = avLine; - } else if(varName.equals("nonce")){ - if(nonce != NULL && !avLine.equals(nonce)){ - //os_printf("AUTH FAIL: nonce\n"); - return false; - } - myNonce = avLine; - } else if(varName.equals("opaque")){ - if(opaque != NULL && !avLine.equals(opaque)){ - //os_printf("AUTH FAIL: opaque\n"); - return false; - } - } else if(varName.equals("uri")){ - if(uri != NULL && !avLine.equals(uri)){ - //os_printf("AUTH FAIL: uri\n"); - return false; - } - myUri = avLine; - } else if(varName.equals("response")){ - myResponse = avLine; - } else if(varName.equals("qop")){ - myQop = avLine; - } else if(varName.equals("nc")){ - myNc = avLine; - } else if(varName.equals("cnonce")){ - myCnonce = avLine; - } - } while(nextBreak > 0); - - String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password)); - String ha2 = String(method) + ":" + myUri; - String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2); - - if(myResponse.equals(stringMD5(response))){ - //os_printf("AUTH SUCCESS\n"); - return true; - } - - //os_printf("AUTH FAIL: password\n"); - return false; -} diff --git a/lib/ESPAsyncWebServer/src/WebAuthentication.h b/lib/ESPAsyncWebServer/src/WebAuthentication.h deleted file mode 100644 index ff68265d..00000000 --- a/lib/ESPAsyncWebServer/src/WebAuthentication.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef WEB_AUTHENTICATION_H_ -#define WEB_AUTHENTICATION_H_ - -#include "Arduino.h" - -bool checkBasicAuthentication(const char * header, const char * username, const char * password); -String requestDigestAuthentication(const char * realm); -bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); - -//for storing hashed versions on the device that can be authenticated against -String generateDigestHash(const char * username, const char * password, const char * realm); - -#endif diff --git a/lib/ESPAsyncWebServer/src/WebHandlerImpl.h b/lib/ESPAsyncWebServer/src/WebHandlerImpl.h deleted file mode 100644 index d121fa7a..00000000 --- a/lib/ESPAsyncWebServer/src/WebHandlerImpl.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef ASYNCWEBSERVERHANDLERIMPL_H_ -#define ASYNCWEBSERVERHANDLERIMPL_H_ - -#include -#ifdef ASYNCWEBSERVER_REGEX -#include -#endif - -#include "stddef.h" -#include - -class AsyncStaticWebHandler: public AsyncWebHandler { - using File = fs::File; - using FS = fs::FS; - private: - bool _getFile(AsyncWebServerRequest *request); - bool _fileExists(AsyncWebServerRequest *request, const String& path); - uint8_t _countBits(const uint8_t value) const; - protected: - FS _fs; - String _uri; - String _path; - String _default_file; - String _cache_control; - String _last_modified; - AwsTemplateProcessor _callback; - bool _isDir; - bool _gzipFirst; - uint8_t _gzipStats; - public: - AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; - AsyncStaticWebHandler& setIsDir(bool isDir); - AsyncStaticWebHandler& setDefaultFile(const char* filename); - AsyncStaticWebHandler& setCacheControl(const char* cache_control); - AsyncStaticWebHandler& setLastModified(const char* last_modified); - AsyncStaticWebHandler& setLastModified(struct tm* last_modified); - #ifdef ESP8266 - AsyncStaticWebHandler& setLastModified(time_t last_modified); - AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated - #endif - AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;} -}; - -class AsyncCallbackWebHandler: public AsyncWebHandler { - private: - protected: - String _uri; - WebRequestMethodComposite _method; - ArRequestHandlerFunction _onRequest; - ArUploadHandlerFunction _onUpload; - ArBodyHandlerFunction _onBody; - bool _isRegex; - public: - AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} - void setUri(const String& uri){ - _uri = uri; - _isRegex = uri.startsWith("^") && uri.endsWith("$"); - } - void setMethod(WebRequestMethodComposite method){ _method = method; } - void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; } - void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; } - void onBody(ArBodyHandlerFunction fn){ _onBody = fn; } - - virtual bool canHandle(AsyncWebServerRequest *request) override final{ - - if(!_onRequest) - return false; - - if(!(_method & request->method())) - return false; - -#ifdef ASYNCWEBSERVER_REGEX - if (_isRegex) { - std::regex pattern(_uri.c_str()); - std::smatch matches; - std::string s(request->url().c_str()); - if(std::regex_search(s, matches, pattern)) { - for (size_t i = 1; i < matches.size(); ++i) { // start from 1 - request->_addPathParam(matches[i].str().c_str()); - } - } else { - return false; - } - } else -#endif - if (_uri.length() && _uri.endsWith("*")) { - String uriTemplate = String(_uri); - uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); - if (!request->url().startsWith(uriTemplate)) - return false; - } - else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) - return false; - - request->addInterestingHeader("ANY"); - return true; - } - - virtual void handleRequest(AsyncWebServerRequest *request) override final { - if(_onRequest) - _onRequest(request); - else - request->send(500); - } - virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { - if(_onUpload) - _onUpload(request, filename, index, data, len, final); - } - virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { - if(_onBody) - _onBody(request, data, len, index, total); - } - virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} -}; - -#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebHandlers.cpp b/lib/ESPAsyncWebServer/src/WebHandlers.cpp deleted file mode 100644 index 1f435e68..00000000 --- a/lib/ESPAsyncWebServer/src/WebHandlers.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "ESPAsyncWebServer.h" -#include "WebHandlerImpl.h" - -AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) - : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr) -{ - // Ensure leading '/' - if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri; - if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path; - - // If path ends with '/' we assume a hint that this is a directory to improve performance. - // However - if it does not end with '/' we, can't assume a file, path can still be a directory. - _isDir = _path[_path.length()-1] == '/'; - - // Remove the trailing '/' so we can handle default file - // Notice that root will be "" not "/" - if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1); - if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1); - - // Reset stats - _gzipFirst = false; - _gzipStats = 0xF8; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){ - _isDir = isDir; - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){ - _default_file = String(filename); - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){ - _cache_control = String(cache_control); - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){ - _last_modified = String(last_modified); - return *this; -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){ - char result[30]; - strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified); - return setLastModified((const char *)result); -} - -#ifdef ESP8266 -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){ - return setLastModified((struct tm *)gmtime(&last_modified)); -} - -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){ - time_t last_modified; - if(time(&last_modified) == 0) //time is not yet set - return *this; - return setLastModified(last_modified); -} -#endif -bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET - || !request->url().startsWith(_uri) - || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) - ){ - return false; - } - if (_getFile(request)) { - // We interested in "If-Modified-Since" header to check if file was modified - if (_last_modified.length()) - request->addInterestingHeader("If-Modified-Since"); - - if(_cache_control.length()) - request->addInterestingHeader("If-None-Match"); - - DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n"); - return true; - } - - return false; -} - -bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) -{ - // Remove the found uri - String path = request->url().substring(_uri.length()); - - // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/' - bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/'); - - path = _path + path; - - // Do we have a file or .gz file - if (!canSkipFileCheck && _fileExists(request, path)) - return true; - - // Can't handle if not default file - if (_default_file.length() == 0) - return false; - - // Try to add default file, ensure there is a trailing '/' ot the path. - if (path.length() == 0 || path[path.length()-1] != '/') - path += "/"; - path += _default_file; - - return _fileExists(request, path); -} - -#ifdef ESP32 -#define FILE_IS_REAL(f) (f == true && !f.isDirectory()) -#else -#define FILE_IS_REAL(f) (f == true) -#endif - -bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path) -{ - bool fileFound = false; - bool gzipFound = false; - - String gzip = path + ".gz"; - - if (_gzipFirst) { - request->_tempFile = _fs.open(gzip, "r"); - gzipFound = FILE_IS_REAL(request->_tempFile); - if (!gzipFound){ - request->_tempFile = _fs.open(path, "r"); - fileFound = FILE_IS_REAL(request->_tempFile); - } - } else { - request->_tempFile = _fs.open(path, "r"); - fileFound = FILE_IS_REAL(request->_tempFile); - if (!fileFound){ - request->_tempFile = _fs.open(gzip, "r"); - gzipFound = FILE_IS_REAL(request->_tempFile); - } - } - - bool found = fileFound || gzipFound; - - if (found) { - // Extract the file name from the path and keep it in _tempObject - size_t pathLen = path.length(); - char * _tempPath = (char*)malloc(pathLen+1); - snprintf(_tempPath, pathLen+1, "%s", path.c_str()); - request->_tempObject = (void*)_tempPath; - - // Calculate gzip statistic - _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0); - if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip - else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip - else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first - } - - return found; -} - -uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const -{ - uint8_t w = value; - uint8_t n; - for (n=0; w!=0; n++) w&=w-1; - return n; -} - -void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) -{ - // Get the filename from request->_tempObject and free it - String filename = String((char*)request->_tempObject); - free(request->_tempObject); - request->_tempObject = NULL; - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - - if (request->_tempFile == true) { - String etag = String(request->_tempFile.size()); - if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) { - request->_tempFile.close(); - request->send(304); // Not modified - } else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) { - request->_tempFile.close(); - AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified - response->addHeader("Cache-Control", _cache_control); - response->addHeader("ETag", etag); - request->send(response); - } else { - AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); - if (_last_modified.length()) - response->addHeader("Last-Modified", _last_modified); - if (_cache_control.length()){ - response->addHeader("Cache-Control", _cache_control); - response->addHeader("ETag", etag); - } - request->send(response); - } - } else { - request->send(404); - } -} diff --git a/lib/ESPAsyncWebServer/src/WebRequest.cpp b/lib/ESPAsyncWebServer/src/WebRequest.cpp deleted file mode 100644 index bbce5ca4..00000000 --- a/lib/ESPAsyncWebServer/src/WebRequest.cpp +++ /dev/null @@ -1,1008 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "ESPAsyncWebServer.h" -#include "WebResponseImpl.h" -#include "WebAuthentication.h" - -#ifndef ESP8266 -#define os_strlen strlen -#endif - -static const String SharedEmptyString = String(); - -#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) - -enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; - -AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) - : _client(c) - , _server(s) - , _handler(NULL) - , _response(NULL) - , _temp() - , _parseState(0) - , _version(0) - , _method(HTTP_ANY) - , _url() - , _host() - , _contentType() - , _boundary() - , _authorization() - , _reqconntype(RCT_HTTP) - , _isDigest(false) - , _isMultipart(false) - , _isPlainPost(false) - , _expectingContinue(false) - , _contentLength(0) - , _parsedLength(0) - , _headers(LinkedList([](AsyncWebHeader *h){ delete h; })) - , _params(LinkedList([](AsyncWebParameter *p){ delete p; })) - , _pathParams(LinkedList([](String *p){ delete p; })) - , _multiParseState(0) - , _boundaryPosition(0) - , _itemStartIndex(0) - , _itemSize(0) - , _itemName() - , _itemFilename() - , _itemType() - , _itemValue() - , _itemBuffer(0) - , _itemBufferIndex(0) - , _itemIsFile(false) - , _tempObject(NULL) -{ - c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); - c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); - c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); - c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); - c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); - c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); -} - -AsyncWebServerRequest::~AsyncWebServerRequest(){ - _headers.free(); - - _params.free(); - _pathParams.free(); - - _interestingHeaders.free(); - - if(_response != NULL){ - delete _response; - } - - if(_tempObject != NULL){ - free(_tempObject); - } - - if(_tempFile){ - _tempFile.close(); - } -} - -void AsyncWebServerRequest::_onData(void *buf, size_t len){ - size_t i = 0; - while (true) { - - if(_parseState < PARSE_REQ_BODY){ - // Find new line in buf - char *str = (char*)buf; - for (i = 0; i < len; i++) { - if (str[i] == '\n') { - break; - } - } - if (i == len) { // No new line, just add the buffer in _temp - char ch = str[len-1]; - str[len-1] = 0; - _temp.reserve(_temp.length()+len); - _temp.concat(str); - _temp.concat(ch); - } else { // Found new line - extract it and parse - str[i] = 0; // Terminate the string at the end of the line. - _temp.concat(str); - _temp.trim(); - _parseLine(); - if (++i < len) { - // Still have more buffer to process - buf = str+i; - len-= i; - continue; - } - } - } else if(_parseState == PARSE_REQ_BODY){ - // A handler should be already attached at this point in _parseLine function. - // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. - const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); - if(_isMultipart){ - if(needParse){ - size_t i; - for(i=0; i end) equal = end; - String name = params.substring(start, equal); - String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); - start = end + 1; - } -} - -bool AsyncWebServerRequest::_parseReqHead(){ - // Split the head into method, url and version - int index = _temp.indexOf(' '); - String m = _temp.substring(0, index); - index = _temp.indexOf(' ', index+1); - String u = _temp.substring(m.length()+1, index); - _temp = _temp.substring(index+1); - - if(m == "GET"){ - _method = HTTP_GET; - } else if(m == "POST"){ - _method = HTTP_POST; - } else if(m == "DELETE"){ - _method = HTTP_DELETE; - } else if(m == "PUT"){ - _method = HTTP_PUT; - } else if(m == "PATCH"){ - _method = HTTP_PATCH; - } else if(m == "HEAD"){ - _method = HTTP_HEAD; - } else if(m == "OPTIONS"){ - _method = HTTP_OPTIONS; - } - - String g = String(); - index = u.indexOf('?'); - if(index > 0){ - g = u.substring(index +1); - u = u.substring(0, index); - } - _url = urlDecode(u); - _addGetParams(g); - - if(!_temp.startsWith("HTTP/1.0")) - _version = 1; - - _temp = String(); - return true; -} - -bool strContains(String src, String find, bool mindcase = true) { - int pos=0, i=0; - const int slen = src.length(); - const int flen = find.length(); - - if (slen < flen) return false; - while (pos <= (slen - flen)) { - for (i=0; i < flen; i++) { - if (mindcase) { - if (src[pos+i] != find[i]) i = flen + 1; // no match - } else if (tolower(src[pos+i]) != tolower(find[i])) i = flen + 1; // no match - } - if (i == flen) return true; - pos++; - } - return false; -} - -bool AsyncWebServerRequest::_parseReqHeader(){ - int index = _temp.indexOf(':'); - if(index){ - String name = _temp.substring(0, index); - String value = _temp.substring(index + 2); - if(name.equalsIgnoreCase("Host")){ - _host = value; - } else if(name.equalsIgnoreCase("Content-Type")){ - _contentType = value.substring(0, value.indexOf(';')); - if (value.startsWith("multipart/")){ - _boundary = value.substring(value.indexOf('=')+1); - _boundary.replace("\"",""); - _isMultipart = true; - } - } else if(name.equalsIgnoreCase("Content-Length")){ - _contentLength = atoi(value.c_str()); - } else if(name.equalsIgnoreCase("Expect") && value == "100-continue"){ - _expectingContinue = true; - } else if(name.equalsIgnoreCase("Authorization")){ - if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase("Basic")){ - _authorization = value.substring(6); - } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase("Digest")){ - _isDigest = true; - _authorization = value.substring(7); - } - } else { - if(name.equalsIgnoreCase("Upgrade") && value.equalsIgnoreCase("websocket")){ - // WebSocket request can be uniquely identified by header: [Upgrade: websocket] - _reqconntype = RCT_WS; - } else { - if(name.equalsIgnoreCase("Accept") && strContains(value, "text/event-stream", false)){ - // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] - _reqconntype = RCT_EVENT; - } - } - } - _headers.add(new AsyncWebHeader(name, value)); - } - _temp = String(); - return true; -} - -void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ - if(data && (char)data != '&') - _temp += (char)data; - if(!data || (char)data == '&' || _parsedLength == _contentLength){ - String name = "body"; - String value = _temp; - if(!_temp.startsWith("{") && !_temp.startsWith("[") && _temp.indexOf('=') > 0){ - name = _temp.substring(0, _temp.indexOf('=')); - value = _temp.substring(_temp.indexOf('=') + 1); - } - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); - _temp = String(); - } -} - -void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ - _itemBuffer[_itemBufferIndex++] = data; - - if(last || _itemBufferIndex == 1460){ - //check if authenticated before calling the upload - if(_handler) - _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); - _itemBufferIndex = 0; - } -} - -enum { - EXPECT_BOUNDARY, - PARSE_HEADERS, - WAIT_FOR_RETURN1, - EXPECT_FEED1, - EXPECT_DASH1, - EXPECT_DASH2, - BOUNDARY_OR_DATA, - DASH3_OR_RETURN2, - EXPECT_FEED2, - PARSING_FINISHED, - PARSE_ERROR -}; - -void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ -#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) - - if(!_parsedLength){ - _multiParseState = EXPECT_BOUNDARY; - _temp = String(); - _itemName = String(); - _itemFilename = String(); - _itemType = String(); - } - - if(_multiParseState == WAIT_FOR_RETURN1){ - if(data != '\r'){ - itemWriteByte(data); - } else { - _multiParseState = EXPECT_FEED1; - } - } else if(_multiParseState == EXPECT_BOUNDARY){ - if(_parsedLength < 2 && data != '-'){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 2 == _boundary.length() && data != '\r'){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 3 == _boundary.length()){ - if(data != '\n'){ - _multiParseState = PARSE_ERROR; - return; - } - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } - } else if(_multiParseState == PARSE_HEADERS){ - if((char)data != '\r' && (char)data != '\n') - _temp += (char)data; - if((char)data == '\n'){ - if(_temp.length()){ - if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){ - _itemType = _temp.substring(14); - _itemIsFile = true; - } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ - _temp = _temp.substring(_temp.indexOf(';') + 2); - while(_temp.indexOf(';') > 0){ - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); - if(name == "name"){ - _itemName = nameVal; - } else if(name == "filename"){ - _itemFilename = nameVal; - _itemIsFile = true; - } - _temp = _temp.substring(_temp.indexOf(';') + 2); - } - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); - if(name == "name"){ - _itemName = nameVal; - } else if(name == "filename"){ - _itemFilename = nameVal; - _itemIsFile = true; - } - } - _temp = String(); - } else { - _multiParseState = WAIT_FOR_RETURN1; - //value starts from here - _itemSize = 0; - _itemStartIndex = _parsedLength; - _itemValue = String(); - if(_itemIsFile){ - if(_itemBuffer) - free(_itemBuffer); - _itemBuffer = (uint8_t*)malloc(1460); - if(_itemBuffer == NULL){ - _multiParseState = PARSE_ERROR; - return; - } - _itemBufferIndex = 0; - } - } - } - } else if(_multiParseState == EXPECT_FEED1){ - if(data != '\n'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH1; - } - } else if(_multiParseState == EXPECT_DASH1){ - if(data != '-'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH2; - } - } else if(_multiParseState == EXPECT_DASH2){ - if(data != '-'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = BOUNDARY_OR_DATA; - _boundaryPosition = 0; - } - } else if(_multiParseState == BOUNDARY_OR_DATA){ - if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; - for(i=0; i<_boundaryPosition; i++) - itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } else if(_boundaryPosition == _boundary.length() - 1){ - _multiParseState = DASH3_OR_RETURN2; - if(!_itemIsFile){ - _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); - } else { - if(_itemSize){ - //check if authenticated before calling the upload - if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); - _itemBufferIndex = 0; - _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); - } - free(_itemBuffer); - _itemBuffer = NULL; - } - - } else { - _boundaryPosition++; - } - } else if(_multiParseState == DASH3_OR_RETURN2){ - if(data == '-' && (_contentLength - _parsedLength - 4) != 0){ - //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); - _contentLength = _parsedLength + 4;//lets close the request gracefully - } - if(data == '\r'){ - _multiParseState = EXPECT_FEED2; - } else if(data == '-' && _contentLength == (_parsedLength + 4)){ - _multiParseState = PARSING_FINISHED; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } - } else if(_multiParseState == EXPECT_FEED2){ - if(data == '\n'){ - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); - itemWriteByte('\r'); _parseMultipartPostByte(data, last); - } - } -} - -void AsyncWebServerRequest::_parseLine(){ - if(_parseState == PARSE_REQ_START){ - if(!_temp.length()){ - _parseState = PARSE_REQ_FAIL; - _client->close(); - } else { - _parseReqHead(); - _parseState = PARSE_REQ_HEADERS; - } - return; - } - - if(_parseState == PARSE_REQ_HEADERS){ - if(!_temp.length()){ - //end of headers - _server->_rewriteRequest(this); - _server->_attachHandler(this); - _removeNotInterestingHeaders(); - if(_expectingContinue){ - const char * response = "HTTP/1.1 100 Continue\r\n\r\n"; - _client->write(response, os_strlen(response)); - } - //check handler for authentication - if(_contentLength){ - _parseState = PARSE_REQ_BODY; - } else { - _parseState = PARSE_REQ_END; - if(_handler) _handler->handleRequest(this); - else send(501); - } - } else _parseReqHeader(); - } -} - -size_t AsyncWebServerRequest::headers() const{ - return _headers.length(); -} - -bool AsyncWebServerRequest::hasHeader(const String& name) const { - for(const auto& h: _headers){ - if(h->name().equalsIgnoreCase(name)){ - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = 0; - while (1) { - if (pgm_read_byte(p+n) == 0) break; - n += 1; - } - char * name = (char*) malloc(n+1); - name[n] = 0; - if (name) { - for(size_t b=0; bname().equalsIgnoreCase(name)){ - return h; - } - } - return nullptr; -} - -AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - AsyncWebHeader* result = getHeader( String(name)); - free(name); - return result; - } else { - return nullptr; - } -} - -AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { - auto header = _headers.nth(num); - return header ? *header : nullptr; -} - -size_t AsyncWebServerRequest::params() const { - return _params.length(); -} - -bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - - char * name = (char*) malloc(n+1); - name[n] = 0; - if (name) { - strcpy_P(name,p); - bool result = hasParam( name, post, file); - free(name); - return result; - } else { - return false; - } -} - -AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ - return p; - } - } - return nullptr; -} - -AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - AsyncWebParameter* result = getParam(name, post, file); - free(name); - return result; - } else { - return nullptr; - } -} - -AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { - auto param = _params.nth(num); - return param ? *param : nullptr; -} - -void AsyncWebServerRequest::addInterestingHeader(const String& name){ - if(!_interestingHeaders.containsIgnoreCase(name)) - _interestingHeaders.add(name); -} - -void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ - _response = response; - if(_response == NULL){ - _client->close(true); - _onDisconnect(); - return; - } - if(!_response->_sourceValid()){ - delete response; - _response = NULL; - send(500); - } - else { - _client->setRxTimeout(0); - _response->_respond(this); - } -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){ - return new AsyncBasicResponse(code, contentType, content); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+".gz"))) - return new AsyncFileResponse(fs, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true) - return new AsyncFileResponse(content, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ - return new AsyncStreamResponse(stream, contentType, len, callback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - return new AsyncCallbackResponse(contentType, len, callback, templateCallback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - if(_version) - return new AsyncChunkedResponse(contentType, callback, templateCallback); - return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); -} - -AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){ - return new AsyncResponseStream(contentType, bufferSize); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - return new AsyncProgmemResponse(code, contentType, content, len, callback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); -} - -void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){ - send(beginResponse(code, contentType, content)); -} - -void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+".gz"))){ - send(beginResponse(fs, path, contentType, download, callback)); - } else send(404); -} - -void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true){ - send(beginResponse(content, path, contentType, download, callback)); - } else send(404); -} - -void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ - send(beginResponse(stream, contentType, len, callback)); -} - -void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - send(beginResponse(contentType, len, callback, templateCallback)); -} - -void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - send(beginChunkedResponse(contentType, callback, templateCallback)); -} - -void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, len, callback)); -} - -void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, callback)); -} - -void AsyncWebServerRequest::redirect(const String& url){ - AsyncWebServerResponse * response = beginResponse(302); - response->addHeader("Location",url); - send(response); -} - -bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){ - if(_authorization.length()){ - if(_isDigest) - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); - else if(!passwordIsHash) - return checkBasicAuthentication(_authorization.c_str(), username, password); - else - return _authorization.equals(password); - } - return false; -} - -bool AsyncWebServerRequest::authenticate(const char * hash){ - if(!_authorization.length() || hash == NULL) - return false; - - if(_isDigest){ - String hStr = String(hash); - int separator = hStr.indexOf(":"); - if(separator <= 0) - return false; - String username = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - separator = hStr.indexOf(":"); - if(separator <= 0) - return false; - String realm = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); - } - - return (_authorization.equals(hash)); -} - -void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){ - AsyncWebServerResponse * r = beginResponse(401); - if(!isDigest && realm == NULL){ - r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); - } else if(!isDigest){ - String header = "Basic realm=\""; - header.concat(realm); - header.concat("\""); - r->addHeader("WWW-Authenticate", header); - } else { - String header = "Digest "; - header.concat(requestDigestAuthentication(realm)); - r->addHeader("WWW-Authenticate", header); - } - send(r); -} - -bool AsyncWebServerRequest::hasArg(const char* name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ - return true; - } - } - return false; -} - -bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - bool result = hasArg( name ); - free(name); - return result; - } else { - return false; - } -} - - -const String& AsyncWebServerRequest::arg(const String& name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ - return arg->value(); - } - } - return SharedEmptyString; -} - -const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - const String & result = arg( String(name) ); - free(name); - return result; - } else { - return SharedEmptyString; - } - -} - -const String& AsyncWebServerRequest::arg(size_t i) const { - return getParam(i)->value(); -} - -const String& AsyncWebServerRequest::argName(size_t i) const { - return getParam(i)->name(); -} - -const String& AsyncWebServerRequest::pathArg(size_t i) const { - auto param = _pathParams.nth(i); - return param ? **param : SharedEmptyString; -} - -const String& AsyncWebServerRequest::header(const char* name) const { - AsyncWebHeader* h = getHeader(String(name)); - return h ? h->value() : SharedEmptyString; -} - -const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - const String & result = header( (const char *)name ); - free(name); - return result; - } else { - return SharedEmptyString; - } -}; - - -const String& AsyncWebServerRequest::header(size_t i) const { - AsyncWebHeader* h = getHeader(i); - return h ? h->value() : SharedEmptyString; -} - -const String& AsyncWebServerRequest::headerName(size_t i) const { - AsyncWebHeader* h = getHeader(i); - return h ? h->name() : SharedEmptyString; -} - -String AsyncWebServerRequest::urlDecode(const String& text) const { - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - String decoded = String(); - decoded.reserve(len); // Allocate the string internal buffer - never longer from source text - while (i < len){ - char decodedChar; - char encodedChar = text.charAt(i++); - if ((encodedChar == '%') && (i + 1 < len)){ - temp[2] = text.charAt(i++); - temp[3] = text.charAt(i++); - decodedChar = strtol(temp, NULL, 16); - } else if (encodedChar == '+') { - decodedChar = ' '; - } else { - decodedChar = encodedChar; // normal ascii char - } - decoded.concat(decodedChar); - } - return decoded; -} - - -const char * AsyncWebServerRequest::methodToString() const { - if(_method == HTTP_ANY) return "ANY"; - else if(_method & HTTP_GET) return "GET"; - else if(_method & HTTP_POST) return "POST"; - else if(_method & HTTP_DELETE) return "DELETE"; - else if(_method & HTTP_PUT) return "PUT"; - else if(_method & HTTP_PATCH) return "PATCH"; - else if(_method & HTTP_HEAD) return "HEAD"; - else if(_method & HTTP_OPTIONS) return "OPTIONS"; - return "UNKNOWN"; -} - -const char *AsyncWebServerRequest::requestedConnTypeToString() const { - switch (_reqconntype) { - case RCT_NOT_USED: return "RCT_NOT_USED"; - case RCT_DEFAULT: return "RCT_DEFAULT"; - case RCT_HTTP: return "RCT_HTTP"; - case RCT_WS: return "RCT_WS"; - case RCT_EVENT: return "RCT_EVENT"; - default: return "ERROR"; - } -} - -bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { - bool res = false; - if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true; - if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true; - if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true; - return res; -} diff --git a/lib/ESPAsyncWebServer/src/WebResponseImpl.h b/lib/ESPAsyncWebServer/src/WebResponseImpl.h deleted file mode 100644 index 9a64e3a5..00000000 --- a/lib/ESPAsyncWebServer/src/WebResponseImpl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_ -#define ASYNCWEBSERVERRESPONSEIMPL_H_ - -#ifdef Arduino_h -// arduino is not compatible with std::vector -#undef min -#undef max -#endif -#include -// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. - -class AsyncBasicResponse: public AsyncWebServerResponse { - private: - String _content; - public: - AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String()); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } -}; - -class AsyncAbstractResponse: public AsyncWebServerResponse { - private: - String _head; - // Data is inserted into cache at begin(). - // This is inefficient with vector, but if we use some other container, - // we won't be able to access it as contiguous array of bytes when reading from it, - // so by gaining performance in one place, we'll lose it in another. - std::vector _cache; - size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); - size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); - protected: - AwsTemplateProcessor _callback; - public: - AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return false; } - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } -}; - -#ifndef TEMPLATE_PLACEHOLDER -#define TEMPLATE_PLACEHOLDER '%' -#endif - -#define TEMPLATE_PARAM_NAME_LENGTH 32 -class AsyncFileResponse: public AsyncAbstractResponse { - using File = fs::File; - using FS = fs::FS; - private: - File _content; - String _path; - void _setContentType(const String& path); - public: - AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - ~AsyncFileResponse(); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; -}; - -class AsyncStreamResponse: public AsyncAbstractResponse { - private: - Stream *_content; - public: - AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; -}; - -class AsyncCallbackResponse: public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - public: - AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; -}; - -class AsyncChunkedResponse: public AsyncAbstractResponse { - private: - AwsResponseFiller _content; - size_t _filledLength; - public: - AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; -}; - -class AsyncProgmemResponse: public AsyncAbstractResponse { - private: - const uint8_t * _content; - size_t _readLength; - public: - AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - bool _sourceValid() const { return true; } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; -}; - -class cbuf; - -class AsyncResponseStream: public AsyncAbstractResponse, public Print { - private: - cbuf *_content; - public: - AsyncResponseStream(const String& contentType, size_t bufferSize); - ~AsyncResponseStream(); - bool _sourceValid() const { return (_state < RESPONSE_END); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; - size_t write(const uint8_t *data, size_t len); - size_t write(uint8_t data); - using Print::write; -}; - -#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */ diff --git a/lib/ESPAsyncWebServer/src/WebResponses.cpp b/lib/ESPAsyncWebServer/src/WebResponses.cpp deleted file mode 100644 index a22e991a..00000000 --- a/lib/ESPAsyncWebServer/src/WebResponses.cpp +++ /dev/null @@ -1,699 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "ESPAsyncWebServer.h" -#include "WebResponseImpl.h" -#include "cbuf.h" - -// Since ESP8266 does not link memchr by default, here's its implementation. -void* memchr(void* ptr, int ch, size_t count) -{ - unsigned char* p = static_cast(ptr); - while(count--) - if(*p++ == static_cast(ch)) - return --p; - return nullptr; -} - - -/* - * Abstract Response - * */ -const char* AsyncWebServerResponse::_responseCodeToString(int code) { - switch (code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Time-out"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Large"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested range not satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Time-out"; - case 505: return "HTTP Version not supported"; - default: return ""; - } -} - -AsyncWebServerResponse::AsyncWebServerResponse() - : _code(0) - , _headers(LinkedList([](AsyncWebHeader *h){ delete h; })) - , _contentType() - , _contentLength(0) - , _sendContentLength(true) - , _chunked(false) - , _headLength(0) - , _sentLength(0) - , _ackedLength(0) - , _writtenLength(0) - , _state(RESPONSE_SETUP) -{ - for(auto header: DefaultHeaders::Instance()) { - _headers.add(new AsyncWebHeader(header->name(), header->value())); - } -} - -AsyncWebServerResponse::~AsyncWebServerResponse(){ - _headers.free(); -} - -void AsyncWebServerResponse::setCode(int code){ - if(_state == RESPONSE_SETUP) - _code = code; -} - -void AsyncWebServerResponse::setContentLength(size_t len){ - if(_state == RESPONSE_SETUP) - _contentLength = len; -} - -void AsyncWebServerResponse::setContentType(const String& type){ - if(_state == RESPONSE_SETUP) - _contentType = type; -} - -void AsyncWebServerResponse::addHeader(const String& name, const String& value){ - _headers.add(new AsyncWebHeader(name, value)); -} - -String AsyncWebServerResponse::_assembleHead(uint8_t version){ - if(version){ - addHeader("Accept-Ranges","none"); - if(_chunked) - addHeader("Transfer-Encoding","chunked"); - } - String out = String(); - int bufSize = 300; - char buf[bufSize]; - - snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code)); - out.concat(buf); - - if(_sendContentLength) { - snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength); - out.concat(buf); - } - if(_contentType.length()) { - snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str()); - out.concat(buf); - } - - for(const auto& header: _headers){ - snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str()); - out.concat(buf); - } - _headers.free(); - - out.concat("\r\n"); - _headLength = out.length(); - return out; -} - -bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } -bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } -bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } -bool AsyncWebServerResponse::_sourceValid() const { return false; } -void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } -size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; } - -/* - * String/Code Response - * */ -AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){ - _code = code; - _content = content; - _contentType = contentType; - if(_content.length()){ - _contentLength = _content.length(); - if(!_contentType.length()) - _contentType = "text/plain"; - } - addHeader("Connection","close"); -} - -void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ - _state = RESPONSE_HEADERS; - String out = _assembleHead(request->version()); - size_t outLen = out.length(); - size_t space = request->client()->space(); - if(!_contentLength && space >= outLen){ - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if(_contentLength && space >= outLen + _contentLength){ - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if(space && space < outLen){ - String partial = out.substring(0, space); - _content = out.substring(space) + _content; - _contentLength += outLen - space; - _writtenLength += request->client()->write(partial.c_str(), partial.length()); - _state = RESPONSE_CONTENT; - } else if(space > outLen && space < (outLen + _contentLength)){ - size_t shift = space - outLen; - outLen += shift; - _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_CONTENT; - } else { - _content = out + _content; - _contentLength += outLen; - _state = RESPONSE_CONTENT; - } -} - -size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ - (void)time; - _ackedLength += len; - if(_state == RESPONSE_CONTENT){ - size_t available = _contentLength - _sentLength; - size_t space = request->client()->space(); - //we can fit in this packet - if(space > available){ - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); - _state = RESPONSE_WAIT_ACK; - return available; - } - //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); - _sentLength += space; - _writtenLength += request->client()->write(out.c_str(), space); - return space; - } else if(_state == RESPONSE_WAIT_ACK){ - if(_ackedLength >= _writtenLength){ - _state = RESPONSE_END; - } - } - return 0; -} - - -/* - * Abstract Response - * */ - -AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) -{ - // In case of template processing, we're unable to determine real response size - if(callback) { - _contentLength = 0; - _sendContentLength = false; - _chunked = true; - } -} - -void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ - addHeader("Connection","close"); - _head = _assembleHead(request->version()); - _state = RESPONSE_HEADERS; - _ack(request, 0, 0); -} - -size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ - (void)time; - if(!_sourceValid()){ - _state = RESPONSE_FAILED; - request->client()->close(); - return 0; - } - _ackedLength += len; - size_t space = request->client()->space(); - - size_t headLen = _head.length(); - if(_state == RESPONSE_HEADERS){ - if(space >= headLen){ - _state = RESPONSE_CONTENT; - space -= headLen; - } else { - String out = _head.substring(0, space); - _head = _head.substring(space); - _writtenLength += request->client()->write(out.c_str(), out.length()); - return out.length(); - } - } - - if(_state == RESPONSE_CONTENT){ - size_t outLen; - if(_chunked){ - if(space <= 8){ - return 0; - } - outLen = space; - } else if(!_sendContentLength){ - outLen = space; - } else { - outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength); - } - - uint8_t *buf = (uint8_t *)malloc(outLen+headLen); - if (!buf) { - // os_printf("_ack malloc %d failed\n", outLen+headLen); - return 0; - } - - if(headLen){ - memcpy(buf, _head.c_str(), _head.length()); - } - - size_t readLen = 0; - - if(_chunked){ - // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. - // See RFC2616 sections 2, 3.6.1. - readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; - } - outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen; - while(outLen < headLen + 4) buf[outLen++] = ' '; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - outLen += readLen; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - } else { - readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; - } - outLen = readLen + headLen; - } - - if(headLen){ - _head = String(); - } - - if(outLen){ - _writtenLength += request->client()->write((const char*)buf, outLen); - } - - if(_chunked){ - _sentLength += readLen; - } else { - _sentLength += outLen - headLen; - } - - free(buf); - - if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){ - _state = RESPONSE_WAIT_ACK; - } - return outLen; - - } else if(_state == RESPONSE_WAIT_ACK){ - if(!_sendContentLength || _ackedLength >= _writtenLength){ - _state = RESPONSE_END; - if(!_chunked && !_sendContentLength) - request->client()->close(true); - } - } - return 0; -} - -size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) -{ - // If we have something in cache, copy it to buffer - const size_t readFromCache = std::min(len, _cache.size()); - if(readFromCache) { - memcpy(data, _cache.data(), readFromCache); - _cache.erase(_cache.begin(), _cache.begin() + readFromCache); - } - // If we need to read more... - const size_t needFromFile = len - readFromCache; - const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); - return readFromCache + readFromContent; -} - -size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) -{ - if(!_callback) - return _fillBuffer(data, len); - - const size_t originalLen = len; - len = _readDataFromCacheOrContent(data, len); - // Now we've read 'len' bytes, either from cache or from file - // Search for template placeholders - uint8_t* pTemplateStart = data; - while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] - uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; - // temporary buffer to hold parameter name - uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; - String paramName; - // If closing placeholder is found: - if(pTemplateEnd) { - // prepare argument to callback - const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1)); - if(paramNameLength) { - memcpy(buf, pTemplateStart + 1, paramNameLength); - buf[paramNameLength] = 0; - paramName = String(reinterpret_cast(buf)); - } else { // double percent sign encountered, this is single percent sign escaped. - // remove the 2nd percent sign - memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; - ++pTemplateStart; - } - } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data - memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); - const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); - if(readFromCacheOrContent) { - pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); - if(pTemplateEnd) { - // prepare argument to callback - *pTemplateEnd = 0; - paramName = String(reinterpret_cast(buf)); - // Copy remaining read-ahead data into cache - _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - pTemplateEnd = &data[len - 1]; - } - else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position - { - // but first, store read file data in cache - _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - ++pTemplateStart; - } - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - if(paramName.length()) { - // call callback and replace with result. - // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. - // Data after pTemplateEnd may need to be moved. - // The first byte of data after placeholder is located at pTemplateEnd + 1. - // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). - const String paramValue(_callback(paramName)); - const char* pvstr = paramValue.c_str(); - const unsigned int pvlen = paramValue.length(); - const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); - // make room for param value - // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store - if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { - _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); - //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); - len = originalLen; // fix issue with truncated data, not sure if it has any side effects - } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) - //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. - // Move the entire data after the placeholder - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - // 3. replace placeholder with actual value - memcpy(pTemplateStart, pvstr, numBytesCopied); - // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) - if(numBytesCopied < pvlen) { - _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); - } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... - // there is some free room, fill it from cache - const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; - const size_t totalFreeRoom = originalLen - len + roomFreed; - len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; - } else { // result is copied fully; it is longer than placeholder text - const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; - len = std::min(len + roomTaken, originalLen); - } - } - } // while(pTemplateStart) - return len; -} - - -/* - * File Response - * */ - -AsyncFileResponse::~AsyncFileResponse(){ - if(_content) - _content.close(); -} - -void AsyncFileResponse::_setContentType(const String& path){ - if (path.endsWith(".html")) _contentType = "text/html"; - else if (path.endsWith(".htm")) _contentType = "text/html"; - else if (path.endsWith(".css")) _contentType = "text/css"; - else if (path.endsWith(".json")) _contentType = "application/json"; - else if (path.endsWith(".js")) _contentType = "application/javascript"; - else if (path.endsWith(".png")) _contentType = "image/png"; - else if (path.endsWith(".gif")) _contentType = "image/gif"; - else if (path.endsWith(".jpg")) _contentType = "image/jpeg"; - else if (path.endsWith(".ico")) _contentType = "image/x-icon"; - else if (path.endsWith(".svg")) _contentType = "image/svg+xml"; - else if (path.endsWith(".eot")) _contentType = "font/eot"; - else if (path.endsWith(".woff")) _contentType = "font/woff"; - else if (path.endsWith(".woff2")) _contentType = "font/woff2"; - else if (path.endsWith(".ttf")) _contentType = "font/ttf"; - else if (path.endsWith(".xml")) _contentType = "text/xml"; - else if (path.endsWith(".pdf")) _contentType = "application/pdf"; - else if (path.endsWith(".zip")) _contentType = "application/zip"; - else if(path.endsWith(".gz")) _contentType = "application/x-gzip"; - else _contentType = "text/plain"; -} - -AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ - _code = 200; - _path = path; - - if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){ - _path = _path+".gz"; - addHeader("Content-Encoding", "gzip"); - _callback = nullptr; // Unable to process zipped templates - _sendContentLength = true; - _chunked = false; - } - - _content = fs.open(_path, "r"); - _contentLength = _content.size(); - - if(contentType == "") - _setContentType(path); - else - _contentType = contentType; - - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; - - if(download) { - // set filename and force download - snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); - } else { - // set filename and force rendering - snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); - } - addHeader("Content-Disposition", buf); -} - -AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ - _code = 200; - _path = path; - - if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){ - addHeader("Content-Encoding", "gzip"); - _callback = nullptr; // Unable to process gzipped templates - _sendContentLength = true; - _chunked = false; - } - - _content = content; - _contentLength = _content.size(); - - if(contentType == "") - _setContentType(path); - else - _contentType = contentType; - - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; - - if(download) { - snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); - } else { - snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); - } - addHeader("Content-Disposition", buf); -} - -size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ - return _content.read(data, len); -} - -/* - * Stream Response - * */ - -AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { - _code = 200; - _content = &stream; - _contentLength = len; - _contentType = contentType; -} - -size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t available = _content->available(); - size_t outLen = (available > len)?len:available; - size_t i; - for(i=0;iread(); - return outLen; -} - -/* - * Callback Response - * */ - -AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) { - _code = 200; - _content = callback; - _contentLength = len; - if(!len) - _sendContentLength = false; - _contentType = contentType; - _filledLength = 0; -} - -size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; - } - return ret; -} - -/* - * Chunked Response - * */ - -AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { - _code = 200; - _content = callback; - _contentLength = 0; - _contentType = contentType; - _sendContentLength = false; - _chunked = true; - _filledLength = 0; -} - -size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; - } - return ret; -} - -/* - * Progmem Response - * */ - -AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { - _code = code; - _content = content; - _contentType = contentType; - _contentLength = len; - _readLength = 0; -} - -size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t left = _contentLength - _readLength; - if (left > len) { - memcpy_P(data, _content + _readLength, len); - _readLength += len; - return len; - } - memcpy_P(data, _content + _readLength, left); - _readLength += left; - return left; -} - - -/* - * Response Stream (You can print/write/printf to it, up to the contentLen bytes) - * */ - -AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){ - _code = 200; - _contentLength = 0; - _contentType = contentType; - _content = new cbuf(bufferSize); -} - -AsyncResponseStream::~AsyncResponseStream(){ - delete _content; -} - -size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ - return _content->read((char*)buf, maxLen); -} - -size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ - if(_started()) - return 0; - - if(len > _content->room()){ - size_t needed = len - _content->room(); - _content->resizeAdd(needed); - } - size_t written = _content->write((const char*)data, len); - _contentLength += written; - return written; -} - -size_t AsyncResponseStream::write(uint8_t data){ - return write(&data, 1); -} diff --git a/lib/ESPAsyncWebServer/src/WebServer.cpp b/lib/ESPAsyncWebServer/src/WebServer.cpp deleted file mode 100644 index 95c2dd69..00000000 --- a/lib/ESPAsyncWebServer/src/WebServer.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#include "ESPAsyncWebServer.h" -#include "WebHandlerImpl.h" - -bool ON_STA_FILTER(AsyncWebServerRequest *request) { - return WiFi.localIP() == request->client()->localIP(); -} - -bool ON_AP_FILTER(AsyncWebServerRequest *request) { - return WiFi.localIP() != request->client()->localIP(); -} - - -AsyncWebServer::AsyncWebServer(uint16_t port) - : _server(port) - , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; })) - , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; })) -{ - _catchAllHandler = new AsyncCallbackWebHandler(); - if(_catchAllHandler == NULL) - return; - _server.onClient([](void *s, AsyncClient* c){ - if(c == NULL) - return; - c->setRxTimeout(3); - AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); - if(r == NULL){ - c->close(true); - c->free(); - delete c; - } - }, this); -} - -AsyncWebServer::~AsyncWebServer(){ - reset(); - end(); - if(_catchAllHandler) delete _catchAllHandler; -} - -AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){ - _rewrites.add(rewrite); - return *rewrite; -} - -bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){ - return _rewrites.remove(rewrite); -} - -AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){ - return addRewrite(new AsyncWebRewrite(from, to)); -} - -AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){ - _handlers.add(handler); - return *handler; -} - -bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ - return _handlers.remove(handler); -} - -void AsyncWebServer::begin(){ - _server.setNoDelay(true); - _server.begin(); -} - -void AsyncWebServer::end(){ - _server.end(); -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ - _server.onSslFileRequest(cb, arg); -} - -void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){ - _server.beginSecure(cert, key, password); -} -#endif - -void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){ - delete request; -} - -void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){ - for(const auto& r: _rewrites){ - if (r->match(request)){ - request->_url = r->toUrl(); - request->_addGetParams(r->params()); - } - } -} - -void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ - for(const auto& h: _handlers){ - if (h->filter(request) && h->canHandle(request)){ - request->setHandler(h); - return; - } - } - - request->addInterestingHeader("ANY"); - request->setHandler(_catchAllHandler); -} - - -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - handler->onBody(onBody); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; -} - -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; -} - -AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){ - AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); - addHandler(handler); - return *handler; -} - -void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ - _catchAllHandler->onRequest(fn); -} - -void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){ - _catchAllHandler->onUpload(fn); -} - -void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){ - _catchAllHandler->onBody(fn); -} - -void AsyncWebServer::reset(){ - _rewrites.free(); - _handlers.free(); - - if (_catchAllHandler != NULL){ - _catchAllHandler->onRequest(NULL); - _catchAllHandler->onUpload(NULL); - _catchAllHandler->onBody(NULL); - } -} - diff --git a/lib/ESPAsyncWebServer/src/edit.htm b/lib/ESPAsyncWebServer/src/edit.htm deleted file mode 100644 index 43d49845..00000000 --- a/lib/ESPAsyncWebServer/src/edit.htm +++ /dev/null @@ -1,627 +0,0 @@ - - - - -ESP Editor - - - - - - -
-
-
-
- - - - diff --git a/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino b/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino deleted file mode 100644 index c6db1517..00000000 --- a/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino +++ /dev/null @@ -1,16 +0,0 @@ -#include "GyverFilters.h" -GFilterRA analog0; // фильтр назовём analog0 - -void setup() { - Serial.begin(9600); - - // установка коэффициента фильтрации (0.0... 1.0). Чем меньше, тем плавнее фильтр - analog0.setCoef(0.01); - - // установка шага фильтрации (мс). Чем меньше, тем резче фильтр - analog0.setStep(10); -} - -void loop() { - Serial.println(analog0.filteredTime(analogRead(0))); -} diff --git a/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino b/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino deleted file mode 100644 index 6c407ec4..00000000 --- a/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino +++ /dev/null @@ -1,32 +0,0 @@ -/* - Пример линейной аппроксимации методом наименьших квадратов - Два массива: по оси Х и по оси У - Линейная аппроксимация повозоляет получить уравнение прямой, - равноудалённой от точек на плоскости ХУ. Удобно для расчёта - роста изменяющейся шумящей величины. Уравнение вида у = A*x + B - В папке с данным примером есть скриншот из excel, - иллюстрирующий работу аппроксимации с такими же исходными -*/ - -// два массива с данными (одинаковой размероности и размера) -int x_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; -int y_array[] = {1, 5, 2, 8, 3, 9, 10, 5, 15, 12}; - -#include -GLinear test; // указываем тип данных в <> - -void setup() { - Serial.begin(9600); - - // передаём массивы и размер одного из них - test.compute((int*)x_array, (int*)y_array, sizeof(x_array)); - - // Уравнение вида у = A*x + B - Serial.println(test.getA()); // получить коэффициент А - Serial.println(test.getB()); // получить коэффициент В - Serial.println(test.getDelta()); // получить изменение (аппроксимированное) -} - -void loop() { - -} \ No newline at end of file diff --git a/lib/GyverFilters/examples/GLinear_arrays/excel.jpg b/lib/GyverFilters/examples/GLinear_arrays/excel.jpg deleted file mode 100644 index 9cd71117..00000000 Binary files a/lib/GyverFilters/examples/GLinear_arrays/excel.jpg and /dev/null differ diff --git a/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino b/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino deleted file mode 100644 index 575c50dc..00000000 --- a/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino +++ /dev/null @@ -1,34 +0,0 @@ -/* - Пример линейной аппроксимации методом наименьших квадратов - Два массива: по оси Х и по оси У - Наполнение массивов осуществляется динамически: сдвигом и записью в крайнюю ячейку, - то есть аппроксимация по последним ARRAY_SIZE изменениям!! -*/ -#define ARRAY_SIZE 10 // размер пространства для аппроксимации - -// два массива с данными (одинаковой размероности и размера) -int x_array[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // ось x от 1 до 10, допустим СЕКУНД -int y_array[ARRAY_SIZE]; // значения по оси У будем брать с датчика - -#include -GLinear test; // указываем тип данных в <> - -void setup() { - Serial.begin(9600); -} - -void loop() { - for (byte i = 0; i < ARRAY_SIZE - 1; i++) { // счётчик от 0 до ARRAY_SIZE - y_array[i] = y_array[i + 1]; // сдвинуть массив давлений КРОМЕ ПОСЛЕДНЕЙ ЯЧЕЙКИ на шаг назад - } - // последний элемент массива теперь - новое значение (просто с аналог. датчика) - y_array[ARRAY_SIZE - 1] = analogRead(0); - - // передаём массивы и размер одного из них - test.compute((int*)x_array, (int*)y_array, sizeof(x_array)); - - // по нашим исходным данным это будет производная, т.е. "изменение единиц в секунду" - Serial.println(test.getDelta()); // получить изменение (аппроксимированное) - - delay(1000); // секундная задержка -} diff --git a/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino b/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino deleted file mode 100644 index 8f43b77a..00000000 --- a/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino +++ /dev/null @@ -1,24 +0,0 @@ -/* - Пример альфа-бета фильтра -*/ - -#include "GyverFilters.h" - -// параметры: период дискретизации (измерений), process variation, noise variation -GABfilter testFilter(0.08, 40, 1); - -void setup() { - Serial.begin(9600); -} - -void loop() { - delay(80); - int value = analogRead(0); - value += random(2) * random(-1, 2) * random(10, 70); - Serial.print("$"); - Serial.print(value); - Serial.print(" "); - value = testFilter.filtered((int)value); - Serial.print(value); - Serial.println(";"); -} diff --git a/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino b/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino deleted file mode 100644 index b981993a..00000000 --- a/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino +++ /dev/null @@ -1,30 +0,0 @@ -/* - Сравнение калмана и бегущего среднего -*/ -#include "GyverFilters.h" - -// параметры: разброс измерения, разброс оценки, скорость изменения значений -// разброс измерения: шум измерений -// разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения -// скорость изменения значений: 0.001-1, варьировать самому - -GKalman kalman(90, 90, 0.5); -GFilterRA average(0.5, 80); - -void setup() { - Serial.begin(9600); -} - -void loop() { - int value = analogRead(0); - value += random(2) * random(-1, 2) * random(50, 100); - Serial.print("$"); - Serial.print(value); - Serial.print(" "); - - Serial.print((int)kalman.filtered(value)); - Serial.print(" "); - Serial.print((int)average.filtered(value)); - Serial.println(";"); - delay(80); -} diff --git a/lib/GyverFilters/examples/kalman_example/kalman_example.ino b/lib/GyverFilters/examples/kalman_example/kalman_example.ino deleted file mode 100644 index 802307af..00000000 --- a/lib/GyverFilters/examples/kalman_example/kalman_example.ino +++ /dev/null @@ -1,31 +0,0 @@ -/* - Пример простого одномерного фильтра -*/ - -#include "GyverFilters.h" - -// параметры: разброс измерения, разброс оценки, скорость изменения значений -// разброс измерения: шум измерений -// разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения -// скорость изменения значений: 0.001-1, варьировать самому - -GKalman testFilter(40, 40, 0.5); - -// также может быть объявлен как (разброс измерения, скорость изменения значений) -// GKalman testFilter(40, 0.5); - -void setup() { - Serial.begin(9600); -} - -void loop() { - delay(80); - int value = analogRead(0); - value += random(2) * random(-1, 2) * random(10, 70); - Serial.print("$"); - Serial.print(value); - Serial.print(" "); - value = testFilter.filtered((int)value); - Serial.print(value); - Serial.println(";"); -} diff --git a/lib/GyverFilters/examples/median3_example/median3_example.ino b/lib/GyverFilters/examples/median3_example/median3_example.ino deleted file mode 100644 index c99176bc..00000000 --- a/lib/GyverFilters/examples/median3_example/median3_example.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - Пример использования быстрого медианного фильтра 3 порядка -*/ - -#include "GyverFilters.h" -GMedian3 testFilter; // указываем тип данных в <> - -void setup() { - Serial.begin(9600); -} - -void loop() { - int value = analogRead(0); - // добавляем шум "выбросы" - value += random(2) * random(2) * random(-1, 2) * random(50, 250); - Serial.print(value); - Serial.print(','); - value = testFilter.filtered(value); - Serial.println(value); - delay(80); -} diff --git a/lib/GyverFilters/examples/median_example/median_example.ino b/lib/GyverFilters/examples/median_example/median_example.ino deleted file mode 100644 index c3fb0067..00000000 --- a/lib/GyverFilters/examples/median_example/median_example.ino +++ /dev/null @@ -1,23 +0,0 @@ -/* - Пример использования медианного фильтра. -*/ - -#include "GyverFilters.h" - -// указываем размер окна и тип данных в <> -GMedian<10, int> testFilter; - -void setup() { - Serial.begin(9600); -} - -void loop() { - delay(80); - int value = analogRead(0); - // добавляем шум "выбросы" - value += random(2) * random(2) * random(-1, 2) * random(50, 250); - Serial.print(value); - Serial.print(','); - value = testFilter.filtered(value); - Serial.println(value); -} \ No newline at end of file diff --git a/lib/GyverFilters/keywords.txt b/lib/GyverFilters/keywords.txt deleted file mode 100644 index 57f38dbc..00000000 --- a/lib/GyverFilters/keywords.txt +++ /dev/null @@ -1,28 +0,0 @@ -####################################### -# Syntax Coloring Map For GyverFilters -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -GyverFilters KEYWORD1 -GFilterRA KEYWORD1 -GMedian3 KEYWORD1 -GMedian KEYWORD1 -GABfilter KEYWORD1 -GKalman KEYWORD1 -GLinear KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -setCoef KEYWORD2 -setStep KEYWORD2 -filteredTime KEYWORD2 -filtered KEYWORD2 -setParameters KEYWORD2 -getA KEYWORD2 -getB KEYWORD2 -getDelta KEYWORD2 \ No newline at end of file diff --git a/lib/GyverFilters/library.properties b/lib/GyverFilters/library.properties deleted file mode 100644 index 7c211d3d..00000000 --- a/lib/GyverFilters/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=GyverFilters -version=2.0 -author=AlexGyver -maintainer=AlexGyver -sentence=Library with few filters for data. -paragraph=Includes median, running average, AB, simplified Kalman and linear approximation filtering algorithms. -category=Data Processing -url=https://github.com/AlexGyver/GyverLibs -architectures=* \ No newline at end of file diff --git a/lib/GyverFilters/src/GyverFilters.h b/lib/GyverFilters/src/GyverFilters.h deleted file mode 100644 index e03e4bf5..00000000 --- a/lib/GyverFilters/src/GyverFilters.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -/* - GyverFilters - библиотека с некоторыми удобными фильтрами: - - GFilterRA - компактная альтернатива фильтра экспоненциальное бегущее среднее (Running Average) - - GMedian3 - быстрый медианный фильтр 3-го порядка (отсекает выбросы) - - GMedian - медианный фильтр N-го порядка. Порядок настраивается в GyverFilters.h - MEDIAN_FILTER_SIZE - - GABfilter - альфа-бета фильтр (разновидность Калмана для одномерного случая) - - GKalman - упрощённый Калман для одномерного случая (на мой взгляд лучший из фильтров) - - GLinear - линейная аппроксимация методом наименьших квадратов для двух массивов - - Версии - - 1.6 от 12.11.2019 - - 1.7: исправлен GLinear - - 1.8: небольшие улучшения - - 2.0: - - Улучшен и исправлен median и median3 - - Улучшен linear - - Смотрите примеры! Использование этих фильтров чуть изменилось -*/ \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/alfaBeta.h b/lib/GyverFilters/src/filters/alfaBeta.h deleted file mode 100644 index 51b09726..00000000 --- a/lib/GyverFilters/src/filters/alfaBeta.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include - -// альфа-бета фильтр -class GABfilter { -public: - // период дискретизации (измерений), process variation, noise variation - GABfilter(float delta, float sigma_process, float sigma_noise) {setParameters(delta, sigma_process, sigma_noise);} - - // период дискретизации (измерений), process variation, noise variation - void setParameters(float delta, float sigma_process, float sigma_noise) { - dt = delta; - float lambda = (float)sigma_process * dt * dt / sigma_noise; - float r = (4 + lambda - (float)sqrt(8 * lambda + lambda * lambda)) / 4; - a = (float)1 - r * r; - b = (float)2 * (2 - a) - 4 * (float)sqrt(1 - a); - } - - // возвращает фильтрованное значение - float filtered(float value) { - xm = value; - xk = xk_1 + ((float) vk_1 * dt ); - vk = vk_1; - rk = xm - xk; - xk += (float)a * rk; - vk += (float)( b * rk ) / dt; - xk_1 = xk; - vk_1 = vk; - return xk_1; - } - -private: - float dt; - float xk_1, vk_1, a, b; - float xk, vk, rk; - float xm; -}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/kalman.h b/lib/GyverFilters/src/filters/kalman.h deleted file mode 100644 index 10458090..00000000 --- a/lib/GyverFilters/src/filters/kalman.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include - -// упрощённый Калман для одномерного случая -class GKalman { -public: - // разброс измерения, разброс оценки, скорость изменения значений - GKalman(float mea_e, float est_e, float q) { setParameters(mea_e, est_e, q); } - - // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) - GKalman(float mea_e, float q) {GKalman::setParameters(mea_e, mea_e, q);} - - // разброс измерения, разброс оценки, скорость изменения значений - void setParameters(float mea_e, float est_e, float q) { - _err_measure = mea_e; - _err_estimate = est_e; - _q = q; - } - - // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) - void setParameters(float mea_e, float q) {setParameters(mea_e, mea_e, q);} - - // возвращает фильтрованное значение - float filtered(float value) { - float _kalman_gain, _current_estimate; - _kalman_gain = _err_estimate / (_err_estimate + _err_measure); - _current_estimate = _last_estimate + _kalman_gain * (value - _last_estimate); - _err_estimate = (1.0 - _kalman_gain)*_err_estimate + fabs(_last_estimate-_current_estimate)*_q; - _last_estimate=_current_estimate; - return _current_estimate; - } - -private: - float _err_measure = 0.0; - float _err_estimate = 0.0; - float _q = 0.0; - float _last_estimate = 0.0; -}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/linear.h b/lib/GyverFilters/src/filters/linear.h deleted file mode 100644 index 65a134f3..00000000 --- a/lib/GyverFilters/src/filters/linear.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -// линейная аппроксимация методом наименьших квадратов -template < typename TYPE > -class GLinear { -public: - GLinear(){}; - void compute(TYPE *x_array, TYPE *y_array, int arrSize) { // аппроксимировать - int32_t sumX = 0, sumY = 0, sumX2 = 0, sumXY = 0; - arrSize /= sizeof(int); - for (int i = 0; i < arrSize; i++) { // для всех элементов массива - sumX += x_array[i]; - sumY += (long)y_array[i]; - sumX2 += x_array[i] * x_array[i]; - sumXY += (long)y_array[i] * x_array[i]; - } - a = (long)arrSize * sumXY; // расчёт коэффициента наклона приямой - a = a - (long)sumX * sumY; - a = (float)a / (arrSize * sumX2 - sumX * sumX); - b = (float)(sumY - (float)a * sumX) / arrSize; - delta = a * arrSize; // расчёт изменения - } - float getA() {return a;} // получить коэффициент А - float getB() {return b;} // получить коэффициент В - float getDelta() {return delta;} // получить аппроксимированное изменение - -private: - float a, b, delta; -}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/median.h b/lib/GyverFilters/src/filters/median.h deleted file mode 100644 index f7580806..00000000 --- a/lib/GyverFilters/src/filters/median.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -// медианный фильтр N-го порядка -template < int SIZE, typename TYPE > -class GMedian { -public: - TYPE filtered(TYPE newVal) { - buffer[_count] = newVal; - if ((_count < _numRead - 1) && (buffer[_count] > buffer[_count + 1])) { - for (int i = _count; i < _numRead - 1; i++) { - if (buffer[i] > buffer[i + 1]) { - float buff = buffer[i]; - buffer[i] = buffer[i + 1]; - buffer[i + 1] = buff; - } - } - } else { - if ((_count > 0) and (buffer[_count - 1] > buffer[_count])) { - for (int i = _count; i > 0; i--) { - if (buffer[i] < buffer[i - 1]) { - float buff = buffer[i]; - buffer[i] = buffer[i - 1]; - buffer[i - 1] = buff; - } - } - } - } - if (++_count >= _numRead) _count = 0; - return buffer[(int)_numRead / 2]; - } -private: - TYPE buffer[SIZE]; - byte _count = 0; - byte _numRead = SIZE; -}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/median3.h b/lib/GyverFilters/src/filters/median3.h deleted file mode 100644 index 8b99cc1e..00000000 --- a/lib/GyverFilters/src/filters/median3.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -// быстрый медианный фильтр 3-го порядка -template < typename TYPE > -class GMedian3 { -public: - TYPE filtered(TYPE value) { // возвращает фильтрованное значение - buffer[_counter] = value; - if (++_counter > 2) _counter = 0; - - TYPE middle; - - if ((buffer[0] <= buffer[1]) && (buffer[0] <= buffer[2])) { - middle = (buffer[1] <= buffer[2]) ? buffer[1] : buffer[2]; - } - else { - if ((buffer[1] <= buffer[0]) && (buffer[1] <= buffer[2])) { - middle = (buffer[0] <= buffer[2]) ? buffer[0] : buffer[2]; - } - else { - middle = (buffer[0] <= buffer[1]) ? buffer[0] : buffer[1]; - } - } - return middle; - } - -private: - TYPE buffer[3]; - uint8_t _counter = 0; -}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/runningAverage.cpp b/lib/GyverFilters/src/filters/runningAverage.cpp deleted file mode 100644 index 9a1b2b03..00000000 --- a/lib/GyverFilters/src/filters/runningAverage.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include - -GFilterRA::GFilterRA() {} - -GFilterRA::GFilterRA(float coef, uint16_t interval) { - _coef = coef; - _filterInterval = interval; -} - -GFilterRA::GFilterRA(float coef) { - _coef = coef; -} - -void GFilterRA::setCoef(float coef) { - _coef = coef; -} -void GFilterRA::setStep(uint16_t interval) { - _filterInterval = interval; -} - -float GFilterRA::filteredTime(int16_t value) { - if (millis() - _filterTimer >= _filterInterval) { - _filterTimer = millis(); - return GFilterRA::filtered(value); - } -} - -float GFilterRA::filteredTime(float value) { - if (millis() - _filterTimer >= _filterInterval) { - _filterTimer = millis(); - return GFilterRA::filtered(value); - } -} - -float GFilterRA::filtered(int16_t value) { - _lastValue += (float)(value - _lastValue) * _coef; - return _lastValue; -} - -float GFilterRA::filtered(float value) { - _lastValue += (float)(value - _lastValue) * _coef; - return _lastValue; -} \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/runningAverage.h b/lib/GyverFilters/src/filters/runningAverage.h deleted file mode 100644 index 7910a0fe..00000000 --- a/lib/GyverFilters/src/filters/runningAverage.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -// экспоненциальное бегущее среднее -class GFilterRA -{ -public: - GFilterRA(); // инициализация фильтра - GFilterRA(float coef); // расширенная инициализация фильтра (коэффициент) - GFilterRA(float coef, uint16_t interval); // расширенная инициализация фильтра (коэффициент, шаг фильтрации) - void setCoef(float coef); // настройка коэффициента фильтрации (0.00 - 1.00). Чем меньше, тем плавнее - void setStep(uint16_t interval); // установка шага фильтрации (мс). Чем меньше, тем резче фильтр - - float filteredTime(int16_t value); // возвращает фильтрованное значение с опорой на встроенный таймер - float filtered(int16_t value); // возвращает фильтрованное значение - - float filteredTime(float value); // возвращает фильтрованное значение с опорой на встроенный таймер - float filtered(float value); // возвращает фильтрованное значение - -private: - float _coef = 0.0, _lastValue = 0.0; - uint32_t _filterTimer = 0; - uint16_t _filterInterval = 0; -}; \ No newline at end of file diff --git a/lib/LITTLEFS/LICENSE b/lib/LITTLEFS/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/lib/LITTLEFS/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/lib/LITTLEFS/README.md b/lib/LITTLEFS/README.md deleted file mode 100644 index fa8f6850..00000000 --- a/lib/LITTLEFS/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# LITTLEFS -## LittleFS library for arduino-esp32 - -#### Warning: Tested only with git arduino-esp32 #b92c58d core, which is for ESP-IDF 3.3!
With other versions/releases of the core including release 1.0.4, especially against different IDF this will NOT not work. - -- A LittleFS wrapper for Arduino ESP32 of [Mbed LittleFS](https://github.com/ARMmbed/littlefs) -- Based on [ESP-IDF port of joltwallet/esp_littlefs](https://github.com/joltwallet/esp_littlefs) , thank you Brian! -- Functionality is the same and SPIFFS partition scheme and data folder meaning are kept -- You can use either LITTLEFS or SPIFFS but not both simultaneously on given Arduino project -- A PR to embed it to esp32 core is made too. See the [PR status here](https://github.com/espressif/arduino-esp32/pull/4096) - -### Installation - -- Copy LITTLEFS folder to Arduino IDE embedded libraries place -- For Win, the default place of arduino-esp32 core libraries is somewhere like: -```C:\Users\\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries ``` -- Alternatively, you can put it to your usual libraries place - -### Usage - -- In your existing code, replace SPIFFS like this -``` -#define USE_LittleFS - -#include -#ifdef USE_LittleFS - #define SPIFFS LITTLEFS - #include -#else - #include -#endif - ``` -### Differences with SPIFFS (and in this implementation) - -- LittleFS has folders, you my need to tweak your code to iterate files in folders -- Root: /someting = something, so attention to / -- Lower level littlefs library cannot mount on NULL as partition_label name, while SPIFFS can -- Lower level littlefs library does not need maxOpenFiles parameter -- Speed (LITTLEFS_Test.ino) 1048576 bytes written in 16238 ms / 1048576 bytes read in 918 ms -- Speed (SPIFFS_Test.ino) 1048576 bytes written in 65971 ms / 1048576 bytes read in 680 ms - - -### Arduino ESP32 LittleFS filesystem upload tool - -- Download the tool archive from [here](https://github.com/lorol/arduino-esp32littlefs-plugin/raw/master/src/bin/esp32littlefs.jar) -- In your Arduino sketchbook directory, create tools directory if it doesn't exist yet. -- Unpack the tool into tools directory (the path will look like ```/Arduino/tools/ESP32LittleFS/tool/esp32littlefs.jar```). -- You need the [mklittlefs tool](https://github.com/earlephilhower/mklittlefs) Download the [release](https://github.com/earlephilhower/mklittlefs/releases) and copy it to -packages\esp32\tools\mkspiffs\\ or on checkout (dev) environment to: packages\esp32\hardware\esp32\\tools\mklittlefs\ -- Restart Arduino IDE. - -## Credits and license - -- This work is based on [Mbed LittleFS](https://github.com/ARMmbed/littlefs) , [ESP-IDF port of joltwallet/esp_littlefs](https://github.com/joltwallet/esp_littlefs) , [Espressif Arduino core for the ESP32, the ESP-IDF - SPIFFS Library](https://github.com/espressif/arduino-esp32/tree/master/libraries/SPIFFS) -- Licensed under GPL v2 ([text](LICENSE)) - -## To Do - -- Supporting different IDF versions \ No newline at end of file diff --git a/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino b/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino deleted file mode 100644 index b760a401..00000000 --- a/lib/LITTLEFS/examples/LittleFS_test/LittleFS_test.ino +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include "FS.h" -#include - -/* You only need to format SPIFFS the first time you run a - test or else use the LITTLEFS plugin to create a partition - https://github.com/me-no-dev/arduino-esp32fs-plugin */ -#define FORMAT_LITTLEFS_IF_FAILED true - -void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ - Serial.printf("Listing directory: %s\r\n", dirname); - - File root = fs.open(dirname); - if(!root){ - Serial.println("- failed to open directory"); - return; - } - if(!root.isDirectory()){ - Serial.println(" - not a directory"); - return; - } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - Serial.print(" DIR : "); - Serial.println(file.name()); - if(levels){ - listDir(fs, file.name(), levels -1); - } - } else { - Serial.print(" FILE: "); - Serial.print(file.name()); - Serial.print("\tSIZE: "); - Serial.println(file.size()); - } - file = root.openNextFile(); - } -} - -void readFile(fs::FS &fs, const char * path){ - Serial.printf("Reading file: %s\r\n", path); - - File file = fs.open(path); - if(!file || file.isDirectory()){ - Serial.println("- failed to open file for reading"); - return; - } - - Serial.println("- read from file:"); - while(file.available()){ - Serial.write(file.read()); - } - file.close(); -} - -void writeFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Writing file: %s\r\n", path); - - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - if(file.print(message)){ - Serial.println("- file written"); - } else { - Serial.println("- write failed"); - } - file.close(); -} - -void appendFile(fs::FS &fs, const char * path, const char * message){ - Serial.printf("Appending to file: %s\r\n", path); - - File file = fs.open(path, FILE_APPEND); - if(!file){ - Serial.println("- failed to open file for appending"); - return; - } - if(file.print(message)){ - Serial.println("- message appended"); - } else { - Serial.println("- append failed"); - } - file.close(); -} - -void renameFile(fs::FS &fs, const char * path1, const char * path2){ - Serial.printf("Renaming file %s to %s\r\n", path1, path2); - if (fs.rename(path1, path2)) { - Serial.println("- file renamed"); - } else { - Serial.println("- rename failed"); - } -} - -void deleteFile(fs::FS &fs, const char * path){ - Serial.printf("Deleting file: %s\r\n", path); - if(fs.remove(path)){ - Serial.println("- file deleted"); - } else { - Serial.println("- delete failed"); - } -} - -void testFileIO(fs::FS &fs, const char * path){ - Serial.printf("Testing file I/O with %s\r\n", path); - - static uint8_t buf[512]; - size_t len = 0; - File file = fs.open(path, FILE_WRITE); - if(!file){ - Serial.println("- failed to open file for writing"); - return; - } - - size_t i; - Serial.print("- writing" ); - uint32_t start = millis(); - for(i=0; i<2048; i++){ - if ((i & 0x001F) == 0x001F){ - Serial.print("."); - } - file.write(buf, 512); - } - Serial.println(""); - uint32_t end = millis() - start; - Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end); - file.close(); - - file = fs.open(path); - start = millis(); - end = start; - i = 0; - if(file && !file.isDirectory()){ - len = file.size(); - size_t flen = len; - start = millis(); - Serial.print("- reading" ); - while(len){ - size_t toRead = len; - if(toRead > 512){ - toRead = 512; - } - file.read(buf, toRead); - if ((i++ & 0x001F) == 0x001F){ - Serial.print("."); - } - len -= toRead; - } - Serial.println(""); - end = millis() - start; - Serial.printf("- %u bytes read in %u ms\r\n", flen, end); - file.close(); - } else { - Serial.println("- failed to open file for reading"); - } -} - -void setup(){ - Serial.begin(115200); - if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){ - Serial.println("LITTLEFS Mount Failed"); - return; - } - - listDir(LITTLEFS, "/", 0); - writeFile(LITTLEFS, "/hello.txt", "Hello "); - appendFile(LITTLEFS, "/hello.txt", "World!\r\n"); - readFile(LITTLEFS, "/hello.txt"); - renameFile(LITTLEFS, "/hello.txt", "/foo.txt"); - readFile(LITTLEFS, "/foo.txt"); - deleteFile(LITTLEFS, "/foo.txt"); - testFileIO(LITTLEFS, "/test.txt"); - deleteFile(LITTLEFS, "/test.txt"); - Serial.println( "Test complete" ); -} - -void loop(){ - -} diff --git a/lib/LITTLEFS/library.json b/lib/LITTLEFS/library.json deleted file mode 100644 index 26c79422..00000000 --- a/lib/LITTLEFS/library.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name":"LITTLEFS", - "description":"LITTLEFS File System Library for ESP32", - "keywords":"littlefs, spiffs", - "authors": - { - "name": "LL", - "maintainer": true - }, - "repository": - { - "type": "git", - "url": "https://github.com/lorol/LITTLEFS.git" - }, - "version": "1.0", - "license": "LGPL-2.0", - "frameworks": "arduino", - "platforms": "espressif32", - "build": { - "libCompatMode": 2 - } -} \ No newline at end of file diff --git a/lib/LITTLEFS/library.properties b/lib/LITTLEFS/library.properties deleted file mode 100644 index d57d320c..00000000 --- a/lib/LITTLEFS/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=LITTLEFS -version=1.0 -author=LL -maintainer=LL -sentence=ESP32 LITTLEFS File System -paragraph= -category=Data Storage -url= -architectures=esp32 \ No newline at end of file diff --git a/lib/LITTLEFS/src/LITTLEFS.cpp b/lib/LITTLEFS/src/LITTLEFS.cpp deleted file mode 100644 index 32dce386..00000000 --- a/lib/LITTLEFS/src/LITTLEFS.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -static constexpr const char LFS_NAME[] = "spiffs"; - -#include "vfs_api.h" - -extern "C" { -#include -#include -#include -#include "esp_littlefs.h" -} - -#include "LITTLEFS.h" - -using namespace fs; - -LITTLEFSFS::LITTLEFSFS() : FS(FSImplPtr(new VFSImpl())) -{ - -} - -bool LITTLEFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles) -{ - if(esp_littlefs_mounted(LFS_NAME)){ - log_w("LITTLEFS Already Mounted!"); - return true; - } - - esp_vfs_littlefs_conf_t conf = { - .base_path = basePath, - .partition_label = LFS_NAME, - //.max_files = maxOpenFiles, - .format_if_mount_failed = false - }; - - esp_err_t err = esp_vfs_littlefs_register(&conf); - if(err == ESP_FAIL && formatOnFail){ - if(format()){ - err = esp_vfs_littlefs_register(&conf); - } - } - if(err != ESP_OK){ - log_e("Mounting LITTLEFS failed! Error: %d", err); - return false; - } - _impl->mountpoint(basePath); - return true; -} - -void LITTLEFSFS::end() -{ - if(esp_littlefs_mounted(LFS_NAME)){ - esp_err_t err = esp_vfs_littlefs_unregister(LFS_NAME); - if(err){ - log_e("Unmounting LITTLEFS failed! Error: %d", err); - return; - } - _impl->mountpoint(NULL); - } -} - -bool LITTLEFSFS::format() -{ - disableCore0WDT(); - esp_err_t err = esp_littlefs_format(LFS_NAME); - enableCore0WDT(); - if(err){ - log_e("Formatting LITTLEFS failed! Error: %d", err); - return false; - } - return true; -} - -size_t LITTLEFSFS::totalBytes() -{ - size_t total,used; - if(esp_littlefs_info(LFS_NAME, &total, &used)){ - return 0; - } - return total; -} - -size_t LITTLEFSFS::usedBytes() -{ - size_t total,used; - if(esp_littlefs_info(LFS_NAME, &total, &used)){ - return 0; - } - return used; -} - -LITTLEFSFS LITTLEFS; - diff --git a/lib/LITTLEFS/src/LITTLEFS.h b/lib/LITTLEFS/src/LITTLEFS.h deleted file mode 100644 index fbd6f09e..00000000 --- a/lib/LITTLEFS/src/LITTLEFS.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _LITTLEFS_H_ -#define _LITTLEFS_H_ - -#include "FS.h" - -namespace fs -{ - -class LITTLEFSFS : public FS -{ -public: - LITTLEFSFS(); - bool begin(bool formatOnFail=false, const char * basePath="/littlefs", uint8_t maxOpenFiles=5); - bool format(); - size_t totalBytes(); - size_t usedBytes(); - void end(); -}; - -} - -extern fs::LITTLEFSFS LITTLEFS; - - -#endif diff --git a/lib/LITTLEFS/src/esp_littlefs.c b/lib/LITTLEFS/src/esp_littlefs.c deleted file mode 100644 index 352cfa57..00000000 --- a/lib/LITTLEFS/src/esp_littlefs.c +++ /dev/null @@ -1,1481 +0,0 @@ -/** - * @file esp_littlefs.c - * @brief Maps LittleFS <-> ESP_VFS - * @author Brian Pugh - */ - -//#define LOG_LOCAL_LEVEL 4 - -#include "esp_littlefs.h" - -#include -#include -#include -#include -#include -#include - -#include "esp_log.h" -#include "esp_spi_flash.h" -#include "esp_system.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "littlefs_api.h" -#include "rom/spi_flash.h" - -static const char TAG[] = "esp_littlefs"; - -#define CONFIG_LITTLEFS_BLOCK_SIZE 4096 /* ESP32 can only operate at 4kb */ - -/* File Descriptor Caching Params */ -#define CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR 2 /* Amount to resize FD cache by */ -#define CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE 4 /* Minimum size of FD cache */ -#define CONFIG_LITTLEFS_FD_CACHE_HYST 4 /* When shrinking, leave this many trailing FD slots available */ - -#define CONFIG_LITTLEFS_MAX_PARTITIONS 3 -#define CONFIG_LITTLEFS_PAGE_SIZE 256 -#define CONFIG_LITTLEFS_OBJ_NAME_LEN 64 -#define CONFIG_LITTLEFS_READ_SIZE 128 -#define CONFIG_LITTLEFS_WRITE_SIZE 128 -#define CONFIG_LITTLEFS_LOOKAHEAD_SIZE 128 -#define CONFIG_LITTLEFS_CACHE_SIZE 128 -#define CONFIG_LITTLEFS_BLOCK_CYCLES 512 -#define CONFIG_LITTLEFS_USE_MTIME 0 -#define CONFIG_LITTLEFS_MTIME_USE_SECONDS 1 - -/** - * @brief littlefs DIR structure - */ -typedef struct { - DIR dir; /*!< VFS DIR struct */ - lfs_dir_t d; /*!< littlefs DIR struct */ - struct dirent e; /*!< Last open dirent */ - long offset; /*!< Offset of the current dirent */ - char *path; /*!< Requested directory name */ -} vfs_littlefs_dir_t; - -static int vfs_littlefs_open(void *ctx, const char *path, int flags, int mode); -static ssize_t vfs_littlefs_write(void *ctx, int fd, const void *data, size_t size); -static ssize_t vfs_littlefs_read(void *ctx, int fd, void *dst, size_t size); -static int vfs_littlefs_close(void *ctx, int fd); -static off_t vfs_littlefs_lseek(void *ctx, int fd, off_t offset, int mode); -static int vfs_littlefs_stat(void *ctx, const char *path, struct stat *st); -static int vfs_littlefs_unlink(void *ctx, const char *path); -static int vfs_littlefs_rename(void *ctx, const char *src, const char *dst); -static DIR *vfs_littlefs_opendir(void *ctx, const char *name); -static int vfs_littlefs_closedir(void *ctx, DIR *pdir); -static struct dirent *vfs_littlefs_readdir(void *ctx, DIR *pdir); -static int vfs_littlefs_readdir_r(void *ctx, DIR *pdir, - struct dirent *entry, struct dirent **out_dirent); -static long vfs_littlefs_telldir(void *ctx, DIR *pdir); -static void vfs_littlefs_seekdir(void *ctx, DIR *pdir, long offset); -static int vfs_littlefs_mkdir(void *ctx, const char *name, mode_t mode); -static int vfs_littlefs_rmdir(void *ctx, const char *name); -static int vfs_littlefs_fsync(void *ctx, int fd); - -static esp_err_t esp_littlefs_init(const esp_vfs_littlefs_conf_t *conf); -static esp_err_t esp_littlefs_erase_partition(const char *partition_label); -static esp_err_t esp_littlefs_by_label(const char *label, int *index); -static esp_err_t esp_littlefs_get_empty(int *index); -static void esp_littlefs_free(esp_littlefs_t **efs); -static void esp_littlefs_dir_free(vfs_littlefs_dir_t *dir); -static int esp_littlefs_flags_conv(int m); -#if CONFIG_LITTLEFS_USE_MTIME -static int vfs_littlefs_utime(void *ctx, const char *path, const struct utimbuf *times); -static void vfs_littlefs_update_mtime(esp_littlefs_t *efs, const char *path); -static int vfs_littlefs_update_mtime_value(esp_littlefs_t *efs, const char *path, time_t t); -static time_t vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path); -#endif - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH -/* The only way in LittleFS to get info is via a path (lfs_stat), so it cannot - * be done if the path isn't stored. */ -static int vfs_littlefs_fstat(void *ctx, int fd, struct stat *st); -#endif - -static int sem_take(esp_littlefs_t *efs); -static int sem_give(esp_littlefs_t *efs); - -static SemaphoreHandle_t _efs_lock = NULL; -static esp_littlefs_t *_efs[CONFIG_LITTLEFS_MAX_PARTITIONS] = {0}; - -/******************** - * Helper Functions * - ********************/ -void esp_littlefs_free_fds(esp_littlefs_t *efs) { - /* Need to free all files that were opened */ - while (efs->file) { - vfs_littlefs_file_t *next = efs->file->next; - free(efs->file); - efs->file = next; - } - free(efs->cache); - efs->cache = 0; - efs->cache_size = efs->fd_count = 0; -} - -/******************** - * Public Functions * - ********************/ - -bool esp_littlefs_mounted(const char *partition_label) { - int index; - esp_err_t err; - - err = esp_littlefs_by_label(partition_label, &index); - if (err != ESP_OK) return false; - return _efs[index]->cache_size > 0; -} - -esp_err_t esp_littlefs_info(const char *partition_label, size_t *total_bytes, size_t *used_bytes) { - int index; - esp_err_t err; - esp_littlefs_t *efs = NULL; - - err = esp_littlefs_by_label(partition_label, &index); - if (err != ESP_OK) return false; - efs = _efs[index]; - - if (total_bytes) *total_bytes = efs->cfg.block_size * efs->cfg.block_count; - if (used_bytes) *used_bytes = efs->cfg.block_size * lfs_fs_size(efs->fs); - - return ESP_OK; -} - -esp_err_t esp_vfs_littlefs_register(const esp_vfs_littlefs_conf_t *conf) { - assert(conf->base_path); - const esp_vfs_t vfs = { - .flags = ESP_VFS_FLAG_CONTEXT_PTR, - .write_p = &vfs_littlefs_write, - .lseek_p = &vfs_littlefs_lseek, - .read_p = &vfs_littlefs_read, - .open_p = &vfs_littlefs_open, - .close_p = &vfs_littlefs_close, -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - .fstat_p = &vfs_littlefs_fstat, -#else - .fstat_p = NULL, /* Not supported */ -#endif - .stat_p = &vfs_littlefs_stat, - .link_p = NULL, /* Not Supported */ - .unlink_p = &vfs_littlefs_unlink, - .rename_p = &vfs_littlefs_rename, - .opendir_p = &vfs_littlefs_opendir, - .closedir_p = &vfs_littlefs_closedir, - .readdir_p = &vfs_littlefs_readdir, - .readdir_r_p = &vfs_littlefs_readdir_r, - .seekdir_p = &vfs_littlefs_seekdir, - .telldir_p = &vfs_littlefs_telldir, - .mkdir_p = &vfs_littlefs_mkdir, - .rmdir_p = &vfs_littlefs_rmdir, - .fsync_p = &vfs_littlefs_fsync, -#if CONFIG_LITTLEFS_USE_MTIME - .utime_p = &vfs_littlefs_utime, -#endif // CONFIG_LITTLEFS_USE_MTIME - }; - - esp_err_t err = esp_littlefs_init(conf); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to initialize LittleFS"); - return err; - } - - int index; - if (esp_littlefs_by_label(conf->partition_label, &index) != ESP_OK) { - ESP_LOGE(TAG, "Unable to find partition \"%s\"", conf->partition_label); - return ESP_ERR_NOT_FOUND; - } - - strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1); - err = esp_vfs_register(conf->base_path, &vfs, _efs[index]); - if (err != ESP_OK) { - esp_littlefs_free(&_efs[index]); - ESP_LOGE(TAG, "Failed to register Littlefs to \"%s\"", conf->base_path); - return err; - } - - ESP_LOGD(TAG, "Successfully registered LittleFS to \"%s\"", conf->base_path); - return ESP_OK; -} - -esp_err_t esp_vfs_littlefs_unregister(const char *partition_label) { - assert(partition_label); - int index; - if (esp_littlefs_by_label(partition_label, &index) != ESP_OK) { - ESP_LOGE(TAG, "Partition was never registered."); - return ESP_ERR_INVALID_STATE; - } - ESP_LOGD(TAG, "Unregistering \"%s\"", partition_label); - esp_err_t err = esp_vfs_unregister(_efs[index]->base_path); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to unregister \"%s\"", partition_label); - return err; - } - esp_littlefs_free(&_efs[index]); - _efs[index] = NULL; - return ESP_OK; -} - -esp_err_t esp_littlefs_format(const char *partition_label) { - assert(partition_label); - - bool was_mounted = false; - bool efs_free = false; - int index = -1; - esp_err_t err; - esp_littlefs_t *efs = NULL; - - ESP_LOGI(TAG, "Formatting \"%s\"", partition_label); - - /* Get a context */ - err = esp_littlefs_by_label(partition_label, &index); - - if (err != ESP_OK) { - /* Create a tmp context */ - ESP_LOGD(TAG, "Temporarily creating EFS context."); - efs_free = true; - const esp_vfs_littlefs_conf_t conf = { - /* base_name not necessary for initializing */ - .dont_mount = true, - .partition_label = partition_label, - }; - err = esp_littlefs_init(&conf); /* Internally MIGHT call esp_littlefs_format */ - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to initialize to format."); - goto exit; - } - - err = esp_littlefs_by_label(partition_label, &index); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error obtaining context."); - goto exit; - } - } - - efs = _efs[index]; - assert(efs); - - /* Unmount if mounted */ - if (efs->cache_size > 0) { - int res; - ESP_LOGD(TAG, "Partition was mounted. Unmounting..."); - was_mounted = true; - res = lfs_unmount(efs->fs); - if (res != LFS_ERR_OK) { - ESP_LOGE(TAG, "Failed to unmount."); - return ESP_FAIL; - } - esp_littlefs_free_fds(efs); - } - - /* Erase and Format */ - { - int res; - ESP_LOGD(TAG, "Formatting filesystem"); - esp_littlefs_erase_partition(partition_label); - res = lfs_format(efs->fs, &efs->cfg); - if (res != LFS_ERR_OK) { - ESP_LOGE(TAG, "Failed to format filesystem"); - return ESP_FAIL; - } - } - - /* Mount filesystem */ - if (was_mounted) { - int res; - /* Remount the partition */ - ESP_LOGD(TAG, "Remounting formatted partition"); - res = lfs_mount(efs->fs, &efs->cfg); - if (res != LFS_ERR_OK) { - ESP_LOGE(TAG, "Failed to re-mount filesystem"); - return ESP_FAIL; - } - efs->cache_size = CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE; // Initial size of cache; will resize ondemand - efs->cache = calloc(sizeof(*efs->cache), efs->cache_size); - } - ESP_LOGD(TAG, "Format Success!"); - - err = ESP_OK; - -exit: - if (efs_free && index >= 0) esp_littlefs_free(&_efs[index]); - return err; -} - -#if CONFIG_LITTLEFS_HUMAN_READABLE -/** - * @brief converts an enumerated lfs error into a string. - * @param lfs_error The littlefs error. - */ -const char *esp_littlefs_errno(enum lfs_error lfs_errno) { - switch (lfs_errno) { - case LFS_ERR_OK: - return "LFS_ERR_OK"; - case LFS_ERR_IO: - return "LFS_ERR_IO"; - case LFS_ERR_CORRUPT: - return "LFS_ERR_CORRUPT"; - case LFS_ERR_NOENT: - return "LFS_ERR_NOENT"; - case LFS_ERR_EXIST: - return "LFS_ERR_EXIST"; - case LFS_ERR_NOTDIR: - return "LFS_ERR_NOTDIR"; - case LFS_ERR_ISDIR: - return "LFS_ERR_ISDIR"; - case LFS_ERR_NOTEMPTY: - return "LFS_ERR_NOTEMPTY"; - case LFS_ERR_BADF: - return "LFS_ERR_BADF"; - case LFS_ERR_FBIG: - return "LFS_ERR_FBIG"; - case LFS_ERR_INVAL: - return "LFS_ERR_INVAL"; - case LFS_ERR_NOSPC: - return "LFS_ERR_NOSPC"; - case LFS_ERR_NOMEM: - return "LFS_ERR_NOMEM"; - case LFS_ERR_NOATTR: - return "LFS_ERR_NOATTR"; - case LFS_ERR_NAMETOOLONG: - return "LFS_ERR_NAMETOOLONG"; - default: - return "LFS_ERR_UNDEFINED"; - } - return ""; -} -#else -#define esp_littlefs_errno(x) "" -#endif - -/******************** - * Static Functions * - ********************/ - -/*** Helpers ***/ - -/** - * @brief Free and clear a littlefs definition structure. - * @param efs Pointer to pointer to struct. Done this way so we can also zero - * out the pointer. - */ -static void esp_littlefs_free(esp_littlefs_t **efs) { - esp_littlefs_t *e = *efs; - if (e == NULL) return; - *efs = NULL; - - if (e->fs) { - if (e->cache_size > 0) lfs_unmount(e->fs); - free(e->fs); - } - if (e->lock) vSemaphoreDelete(e->lock); - esp_littlefs_free_fds(e); - free(e); -} - -/** - * @brief Free a vfs_littlefs_dir_t struct. - */ -static void esp_littlefs_dir_free(vfs_littlefs_dir_t *dir) { - if (dir == NULL) return; - if (dir->path) free(dir->path); - free(dir); -} - -/** - * Get a mounted littlefs filesystem by label. - * @param[in] label - * @param[out] index index into _efs - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_by_label(const char *label, int *index) { - int i; - esp_littlefs_t *p; - - if (!label || !index) return ESP_ERR_INVALID_ARG; - - ESP_LOGD(TAG, "Searching for existing filesystem for partition \"%s\"", label); - - for (i = 0; i < CONFIG_LITTLEFS_MAX_PARTITIONS; i++) { - p = _efs[i]; - if (p) { - if (strncmp(label, p->partition->label, 17) == 0) { - *index = i; - ESP_LOGD(TAG, "Found existing filesystem \"%s\" at index %d", label, *index); - return ESP_OK; - } - } - } - - ESP_LOGD(TAG, "Existing filesystem \%s\" not found", label); - return ESP_ERR_NOT_FOUND; -} - -/** - * @brief Get the index of an unallocated LittleFS slot. - * @param[out] index Indexd of free LittleFS slot - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_get_empty(int *index) { - assert(index); - for (uint8_t i = 0; i < CONFIG_LITTLEFS_MAX_PARTITIONS; i++) { - if (_efs[i] == NULL) { - *index = i; - return ESP_OK; - } - } - ESP_LOGE(TAG, "No more free partitions available."); - return ESP_FAIL; -} - -/** - * @brief erase a partition; make sure LittleFS is unmounted first. - * @param partition_label NULL-terminated string of partition to erase - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_erase_partition(const char *partition_label) { - ESP_LOGD(TAG, "Erasing partition..."); - - const esp_partition_t *partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, - partition_label); - if (!partition) { - ESP_LOGE(TAG, "partition \"%s\" could not be found", partition_label); - return ESP_ERR_NOT_FOUND; - } - - if (esp_partition_erase_range(partition, 0, partition->size) != ESP_OK) { - ESP_LOGE(TAG, "Failed to erase partition"); - return ESP_FAIL; - } - - return ESP_OK; -} - -/** - * @brief Convert fcntl flags to littlefs flags - * @param m fcntl flags - * @return lfs flags - */ -static int esp_littlefs_flags_conv(int m) { - int lfs_flags = 0; - if (m == O_APPEND) lfs_flags |= LFS_O_APPEND; - if (m == O_RDONLY) lfs_flags |= LFS_O_RDONLY; - if (m & O_WRONLY) lfs_flags |= LFS_O_WRONLY; - if (m & O_RDWR) lfs_flags |= LFS_O_RDWR; - if (m & O_EXCL) lfs_flags |= LFS_O_EXCL; - if (m & O_CREAT) lfs_flags |= LFS_O_CREAT; - if (m & O_TRUNC) lfs_flags |= LFS_O_TRUNC; - return lfs_flags; -} - -/** - * @brief Initialize and mount littlefs - * @param[in] conf Filesystem Configuration - * @return ESP_OK on success - */ -static esp_err_t esp_littlefs_init(const esp_vfs_littlefs_conf_t *conf) { - int index = -1; - esp_err_t err = ESP_FAIL; - const esp_partition_t *partition = NULL; - esp_littlefs_t *efs = NULL; - - if (_efs_lock == NULL) { - static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - portENTER_CRITICAL(&mux); - if (_efs_lock == NULL) { - _efs_lock = xSemaphoreCreateMutex(); - assert(_efs_lock); - } - portEXIT_CRITICAL(&mux); - } - - xSemaphoreTake(_efs_lock, portMAX_DELAY); - - if (esp_littlefs_get_empty(&index) != ESP_OK) { - ESP_LOGE(TAG, "max mounted partitions reached"); - err = ESP_ERR_INVALID_STATE; - goto exit; - } - - /* Input and Environment Validation */ - if (esp_littlefs_by_label(conf->partition_label, &index) == ESP_OK) { - ESP_LOGE(TAG, "Partition already used"); - err = ESP_ERR_INVALID_STATE; - goto exit; - } - - { - uint32_t flash_page_size = g_rom_flashchip.page_size; - uint32_t log_page_size = CONFIG_LITTLEFS_PAGE_SIZE; - if (log_page_size % flash_page_size != 0) { - ESP_LOGE(TAG, "LITTLEFS_PAGE_SIZE is not multiple of flash chip page size (%d)", - flash_page_size); - err = ESP_ERR_INVALID_ARG; - goto exit; - } - } - - if (NULL == conf->partition_label) { - ESP_LOGE(TAG, "Partition label must be provided."); - err = ESP_ERR_INVALID_ARG; - goto exit; - } - - partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, - conf->partition_label); - - if (!partition) { - ESP_LOGE(TAG, "partition \"%s\" could not be found", conf->partition_label); - err = ESP_ERR_NOT_FOUND; - goto exit; - } - - if (partition->encrypted) { - // TODO: allow encryption; should probably be fine, - // just not allowing until tested. - ESP_LOGE(TAG, "littlefs can not run on encrypted partition"); - err = ESP_ERR_INVALID_STATE; - goto exit; - } - - /* Allocate Context */ - efs = calloc(1, sizeof(esp_littlefs_t)); - if (efs == NULL) { - ESP_LOGE(TAG, "esp_littlefs could not be malloced"); - err = ESP_ERR_NO_MEM; - goto exit; - } - efs->partition = partition; - - { /* LittleFS Configuration */ - efs->cfg.context = efs; - - // block device operations - efs->cfg.read = littlefs_api_read; - efs->cfg.prog = littlefs_api_prog; - efs->cfg.erase = littlefs_api_erase; - efs->cfg.sync = littlefs_api_sync; - - // block device configuration - efs->cfg.read_size = CONFIG_LITTLEFS_READ_SIZE; - efs->cfg.prog_size = CONFIG_LITTLEFS_WRITE_SIZE; - efs->cfg.block_size = CONFIG_LITTLEFS_BLOCK_SIZE; - ; - efs->cfg.block_count = efs->partition->size / efs->cfg.block_size; - efs->cfg.cache_size = CONFIG_LITTLEFS_CACHE_SIZE; - efs->cfg.lookahead_size = CONFIG_LITTLEFS_LOOKAHEAD_SIZE; - efs->cfg.block_cycles = CONFIG_LITTLEFS_BLOCK_CYCLES; - } - - efs->lock = xSemaphoreCreateRecursiveMutex(); - if (efs->lock == NULL) { - ESP_LOGE(TAG, "mutex lock could not be created"); - err = ESP_ERR_NO_MEM; - goto exit; - } - - efs->fs = calloc(1, sizeof(lfs_t)); - if (efs->fs == NULL) { - ESP_LOGE(TAG, "littlefs could not be malloced"); - err = ESP_ERR_NO_MEM; - goto exit; - } - - // Mount and Error Check - _efs[index] = efs; - if (!conf->dont_mount) { - int res = lfs_mount(efs->fs, &efs->cfg); - - if (conf->format_if_mount_failed && res != LFS_ERR_OK) { - esp_err_t err; - ESP_LOGW(TAG, "mount failed, %s (%i). formatting...", esp_littlefs_errno(res), res); - err = esp_littlefs_format(efs->partition->label); - if (err != ESP_OK) { - ESP_LOGE(TAG, "format failed"); - err = ESP_FAIL; - goto exit; - } - res = lfs_mount(efs->fs, &efs->cfg); - } - if (res != LFS_ERR_OK) { - ESP_LOGE(TAG, "mount failed, %s (%i)", esp_littlefs_errno(res), res); - err = ESP_FAIL; - goto exit; - } - efs->cache_size = 4; - efs->cache = calloc(sizeof(*efs->cache), efs->cache_size); - } - - err = ESP_OK; - -exit: - if (err != ESP_OK) { - if (index >= 0) { - esp_littlefs_free(&_efs[index]); - } else { - esp_littlefs_free(&efs); - } - } - xSemaphoreGive(_efs_lock); - return err; -} - -/** - * @brief - * @parameter efs file system context - */ -static inline int sem_take(esp_littlefs_t *efs) { - int res; -#if LOG_LOCAL_LEVEL >= 4 - ESP_LOGD(TAG, "------------------------ Sem Taking [%s]", pcTaskGetTaskName(NULL)); -#endif - res = xSemaphoreTakeRecursive(efs->lock, portMAX_DELAY); -#if LOG_LOCAL_LEVEL >= 4 - ESP_LOGD(TAG, "--------------------->>> Sem Taken [%s]", pcTaskGetTaskName(NULL)); -#endif - return res; -} - -/** - * @brief - * @parameter efs file system context - */ -static inline int sem_give(esp_littlefs_t *efs) { -#if LOG_LOCAL_LEVEL >= 4 - ESP_LOGD(TAG, "---------------------<<< Sem Give [%s]", pcTaskGetTaskName(NULL)); -#endif - return xSemaphoreGiveRecursive(efs->lock); -} - -/* We are using a double allocation system here, which an array and a linked list. - The array contains the pointer to the file descriptor (the index in the array is what's returned to the user). - The linked list is used for file descriptors. - This means that position of nodes in the list must stay consistent: - - Allocation is obvious (append to the list from the head, and realloc the pointers array) - There is still a O(N) search in the cache for a free position to store - - Searching is a O(1) process (good) - - Deallocation is more tricky. That is, for example, - if you need to remove node 5 in a 12 nodes list, you'll have to: - 1) Mark the 5th position as freed (if it's the last position of the array realloc smaller) - 2) Walk the list until finding the pointer to the node O(N) and scrub the node so the chained list stays consistent - 3) Deallocate the node -*/ - -/** - * @brief Get a file descriptor - * @param[in,out] efs file system context - * @param[out] file pointer to a file that'll be filled with a file object - * @param[in] path_len the length of the filepath in bytes (including terminating zero byte) - * @return integer file descriptor. Returns -1 if a FD cannot be obtained. - * @warning This must be called with lock taken - */ -static int esp_littlefs_allocate_fd(esp_littlefs_t *efs, vfs_littlefs_file_t **file -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - , - const size_t path_len -#endif -) { - int i = -1; - - assert(efs->fd_count < UINT16_MAX); - assert(efs->cache_size < UINT16_MAX); - - /* Make sure there is enough space in the cache to store new fd */ - if (efs->fd_count + 1 > efs->cache_size) { - uint16_t new_size = (uint16_t)MIN(UINT16_MAX, CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR * efs->cache_size); - /* Resize the cache */ - vfs_littlefs_file_t **new_cache = realloc(efs->cache, new_size * sizeof(*efs->cache)); - if (!new_cache) { - ESP_LOGE(TAG, "Unable to allocate file cache"); - return -1; /* If it fails here, no harm is done to the filesystem, so it's safe */ - } - /* Zero out the new portions of the cache */ - memset(&new_cache[efs->cache_size], 0, (new_size - efs->cache_size) * sizeof(*efs->cache)); - efs->cache = new_cache; - efs->cache_size = new_size; - } - - /* Allocate file descriptor here now */ -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - *file = calloc(1, sizeof(**file) + path_len); -#else - *file = calloc(1, sizeof(**file)); -#endif - - if (*file == NULL) { - /* If it fails here, the file system might have a larger cache, but it's harmless, no need to reverse it */ - ESP_LOGE(TAG, "Unable to allocate FD"); - return -1; - } - - /* Starting from here, nothing can fail anymore */ - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - /* The trick here is to avoid dual allocation so the path pointer - should point to the next byte after it: - file => [ lfs_file | # | next | path | free_space ] - | /\ - |__/ - */ - (*file)->path = (char *)(*file) + sizeof(**file); -#endif - - /* Now find a free place in cache */ - for (i = 0; i < efs->cache_size; i++) { - if (efs->cache[i] == NULL) { - efs->cache[i] = *file; - break; - } - } - /* Save file in the list */ - (*file)->next = efs->file; - efs->file = *file; - efs->fd_count++; - return i; -} - -/** - * @brief Release a file descriptor - * @param[in,out] efs file system context - * @param[in] fd File Descriptor to release - * @return 0 on success. -1 if a FD cannot be obtained. - * @warning This must be called with lock taken - */ -static int esp_littlefs_free_fd(esp_littlefs_t *efs, int fd) { - vfs_littlefs_file_t *file, *head; - - if ((uint32_t)fd >= efs->cache_size) { - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return -1; - } - - /* Get the file descriptor to free it */ - file = efs->cache[fd]; - head = efs->file; - /* Search for file in SLL to remove it */ - if (file == head) { - /* Last file, can't fail */ - efs->file = efs->file->next; - } else { - while (head && head->next != file) { - head = head->next; - } - if (!head) { - ESP_LOGE(TAG, "Inconsistent list"); - return -1; - } - /* Transaction starts here and can't fail anymore */ - head->next = file->next; - } - efs->cache[fd] = NULL; - efs->fd_count--; - - ESP_LOGD(TAG, "Clearing FD"); - free(file); - -#if 0 - /* Realloc smaller if its possible - * * Find and realloc based on number of trailing NULL ptrs in cache - * * Leave some hysteris to prevent thrashing around resize points - * This is disabled for now because it adds unnecessary complexity - * and binary size increase that outweights its ebenfits. - */ - if(efs->cache_size > CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE) { - uint16_t n_free; - uint16_t new_size = efs->cache_size / CONFIG_LITTLEFS_FD_CACHE_REALLOC_FACTOR; - - if(new_size >= CONFIG_LITTLEFS_FD_CACHE_MIN_SIZE) { - /* Count number of trailing NULL ptrs */ - for(n_free=0; n_free < efs->cache_size; n_free++) { - if(efs->cache[efs->cache_size - n_free - 1] != NULL) { - break; - } - } - - if(n_free >= (efs->cache_size - new_size)){ - new_size += CONFIG_LITTLEFS_FD_CACHE_HYST; - ESP_LOGD(TAG, "Reallocating cache %i -> %i", efs->cache_size, new_size); - vfs_littlefs_file_t ** new_cache; - new_cache = realloc(efs->cache, new_size * sizeof(*efs->cache)); - /* No harm on realloc failure, continue using the oversized cache */ - if(new_cache) { - efs->cache = new_cache; - efs->cache_size = new_size; - } - } - } - } -#endif - - return 0; -} - -/** - * @brief Compute the 32bit DJB2 hash of the given string. - * @param[in] path the path to hash - * @returns the hash for this path - */ -static uint32_t compute_hash(const char *path) { - uint32_t hash = 5381; - char c; - - while ((c = *path++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - return hash; -} - -/** - * @brief finds an open file descriptor by file name. - * @param[in,out] efs file system context - * @param[in] path File path to check. - * @returns integer file descriptor. Returns -1 if not found. - * @warning This must be called with lock taken - * @warning if CONFIG_LITTLEFS_USE_ONLY_HASH, there is a slim chance an - * erroneous FD may be returned on hash collision. - */ -static int esp_littlefs_get_fd_by_name(esp_littlefs_t *efs, const char *path) { - uint32_t hash = compute_hash(path); - - for (uint16_t i = 0, j = 0; i < efs->cache_size && j < efs->fd_count; i++) { - if (efs->cache[i]) { - ++j; - - if ( - efs->cache[i]->hash == hash // Faster than strcmp -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - && strcmp(path, efs->cache[i]->path) == 0 // May as well check incase of hash collision. Usually short-circuited. -#endif - ) { - ESP_LOGD(TAG, "Found \"%s\" at FD %d.", path, i); - return i; - } - } - } - ESP_LOGD(TAG, "Unable to get a find FD for \"%s\"", path); - return -1; -} - -/*** Filesystem Hooks ***/ - -static int vfs_littlefs_open(void *ctx, const char *path, int flags, int mode) { - /* Note: mode is currently unused */ - int fd = -1, lfs_flags, res; - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - vfs_littlefs_file_t *file = NULL; -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - size_t path_len = strlen(path) + 1; // include NULL terminator -#endif - assert(path); - - ESP_LOGD(TAG, "Opening %s", path); - - /* Convert flags to lfs flags */ - lfs_flags = esp_littlefs_flags_conv(flags); - - /* Get a FD */ - sem_take(efs); - fd = esp_littlefs_allocate_fd(efs, &file -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - , - path_len -#endif - ); - if (fd < 0) { - sem_give(efs); - ESP_LOGE(TAG, "Error obtaining FD"); - return LFS_ERR_INVAL; - } - /* Open File */ - res = lfs_file_open(efs->fs, &file->file, path, lfs_flags); - - if (res < 0) { - esp_littlefs_free_fd(efs, fd); - sem_give(efs); - ESP_LOGE(TAG, "Failed to open file. Error %s (%d)", - esp_littlefs_errno(res), res); - return LFS_ERR_INVAL; - } - - file->hash = compute_hash(path); -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - memcpy(file->path, path, path_len); -#endif - -#if CONFIG_LITTLEFS_USE_MTIME - if (!(lfs_flags & LFS_O_RDONLY)) { - /* If this is being opened as not read-only */ - vfs_littlefs_update_mtime(efs, path); - } -#endif - - sem_give(efs); - ESP_LOGD(TAG, "Done opening %s", path); - return fd; -} - -static ssize_t vfs_littlefs_write(void *ctx, int fd, const void *data, size_t size) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_write(efs->fs, &file->file, data, size); - sem_give(efs); - - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to write FD %d; path \"%s\". Error %s (%d)", - fd, file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to write FD %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - - return res; -} - -static ssize_t vfs_littlefs_read(void *ctx, int fd, void *dst, size_t size) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_read(efs->fs, &file->file, dst, size); - sem_give(efs); - - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to read file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to read FD %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - - return res; -} - -static int vfs_littlefs_close(void *ctx, int fd) { - // TODO update mtime on close? SPIFFS doesn't do this - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - int res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_close(efs->fs, &file->file); - if (res < 0) { - sem_give(efs); -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to close file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to close Fd %d. Error %s (%d)", - fd, esp_littlefs_errno(res), res); -#endif - return res; - } - esp_littlefs_free_fd(efs, fd); - sem_give(efs); - return res; -} - -static off_t vfs_littlefs_lseek(void *ctx, int fd, off_t offset, int mode) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - lfs_soff_t res; - vfs_littlefs_file_t *file = NULL; - int whence; - - switch (mode) { - case SEEK_SET: - whence = LFS_SEEK_SET; - break; - case SEEK_CUR: - whence = LFS_SEEK_CUR; - break; - case SEEK_END: - whence = LFS_SEEK_END; - break; - default: - ESP_LOGE(TAG, "Invalid mode"); - return -1; - } - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_seek(efs->fs, &file->file, offset, whence); - sem_give(efs); - - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to seek file \"%s\" to offset %08x. Error %s (%d)", - file->path, (unsigned int)offset, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to seek FD %d to offset %08x. Error (%d)", - fd, (unsigned int)offset, res); -#endif - return res; - } - - return res; -} - -static int vfs_littlefs_fsync(void *ctx, int fd) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - ssize_t res; - vfs_littlefs_file_t *file = NULL; - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_file_sync(efs->fs, &file->file); - sem_give(efs); - - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to sync file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to sync file %d. Error %d", fd, res); -#endif - return res; - } - - return res; -} - -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH -static int vfs_littlefs_fstat(void *ctx, int fd, struct stat *st) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - vfs_littlefs_file_t *file = NULL; - - memset(st, 0, sizeof(struct stat)); - st->st_blksize = efs->cfg.block_size; - - sem_take(efs); - if ((uint32_t)fd > efs->cache_size) { - sem_give(efs); - ESP_LOGE(TAG, "FD must be <%d.", efs->cache_size); - return LFS_ERR_BADF; - } - file = efs->cache[fd]; - res = lfs_stat(efs->fs, file->path, &info); - if (res < 0) { - sem_give(efs); - ESP_LOGE(TAG, "Failed to stat file \"%s\". Error %s (%d)", - file->path, esp_littlefs_errno(res), res); - return res; - } - -#if CONFIG_LITTLEFS_USE_MTIME - st->st_mtime = vfs_littlefs_get_mtime(efs, file->path); -#endif - - sem_give(efs); - - st->st_size = info.size; - st->st_mode = ((info.type == LFS_TYPE_REG) ? S_IFREG : S_IFDIR); - return 0; -} -#endif - -static int vfs_littlefs_stat(void *ctx, const char *path, struct stat *st) { - assert(path); - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - memset(st, 0, sizeof(struct stat)); - st->st_blksize = efs->cfg.block_size; - - sem_take(efs); - res = lfs_stat(efs->fs, path, &info); - if (res < 0) { - sem_give(efs); - /* Not strictly an error, since stat can be used to check - * if a file exists */ - ESP_LOGI(TAG, "Failed to stat path \"%s\". Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } -#if CONFIG_LITTLEFS_USE_MTIME - st->st_mtime = vfs_littlefs_get_mtime(efs, path); -#endif - sem_give(efs); - st->st_size = info.size; - st->st_mode = ((info.type == LFS_TYPE_REG) ? S_IFREG : S_IFDIR); - return 0; -} - -static int vfs_littlefs_unlink(void *ctx, const char *path) { -#define fail_str_1 "Failed to unlink path \"%s\"." - assert(path); - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - sem_take(efs); - res = lfs_stat(efs->fs, path, &info); - if (res < 0) { - sem_give(efs); - ESP_LOGE(TAG, fail_str_1 " Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } - - if (esp_littlefs_get_fd_by_name(efs, path) >= 0) { - sem_give(efs); - ESP_LOGE(TAG, fail_str_1 " Has open FD.", path); - return -1; - } - - if (info.type == LFS_TYPE_DIR) { - sem_give(efs); - ESP_LOGE(TAG, "Cannot unlink a directory."); - return LFS_ERR_ISDIR; - } - - res = lfs_remove(efs->fs, path); - if (res < 0) { - sem_give(efs); - ESP_LOGE(TAG, fail_str_1 " Error %s (%d)", - path, esp_littlefs_errno(res), res); - return res; - } - - sem_give(efs); - - return 0; -#undef fail_str_1 -} - -static int vfs_littlefs_rename(void *ctx, const char *src, const char *dst) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - int res; - - sem_take(efs); - - if (esp_littlefs_get_fd_by_name(efs, src) >= 0) { - sem_give(efs); - ESP_LOGE(TAG, "Cannot rename; src \"%s\" is open.", src); - return -1; - } else if (esp_littlefs_get_fd_by_name(efs, dst) >= 0) { - sem_give(efs); - ESP_LOGE(TAG, "Cannot rename; dst \"%s\" is open.", dst); - return -1; - } - - res = lfs_rename(efs->fs, src, dst); - sem_give(efs); - if (res < 0) { - ESP_LOGE(TAG, "Failed to rename \"%s\" -> \"%s\". Error %s (%d)", - src, dst, esp_littlefs_errno(res), res); - return res; - } - - return 0; -} - -static DIR *vfs_littlefs_opendir(void *ctx, const char *name) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - int res; - vfs_littlefs_dir_t *dir = NULL; - - dir = calloc(1, sizeof(vfs_littlefs_dir_t)); - if (dir == NULL) { - ESP_LOGE(TAG, "dir struct could not be malloced"); - goto exit; - } - - dir->path = strdup(name); - if (dir->path == NULL) { - ESP_LOGE(TAG, "dir path name could not be malloced"); - goto exit; - } - - sem_take(efs); - res = lfs_dir_open(efs->fs, &dir->d, dir->path); - sem_give(efs); - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to opendir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to opendir \"%s\". Error %d", dir->path, res); -#endif - goto exit; - } - - return (DIR *)dir; - -exit: - esp_littlefs_dir_free(dir); - return NULL; -} - -static int vfs_littlefs_closedir(void *ctx, DIR *pdir) { - assert(pdir); - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t *dir = (vfs_littlefs_dir_t *)pdir; - int res; - - sem_take(efs); - res = lfs_dir_close(efs->fs, &dir->d); - sem_give(efs); - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to closedir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to closedir \"%s\". Error %d", dir->path, res); -#endif - return res; - } - - esp_littlefs_dir_free(dir); - return 0; -} - -static struct dirent *vfs_littlefs_readdir(void *ctx, DIR *pdir) { - assert(pdir); - vfs_littlefs_dir_t *dir = (vfs_littlefs_dir_t *)pdir; - int res; - struct dirent *out_dirent; - - res = vfs_littlefs_readdir_r(ctx, pdir, &dir->e, &out_dirent); - if (res != 0) return NULL; - return out_dirent; -} - -static int vfs_littlefs_readdir_r(void *ctx, DIR *pdir, - struct dirent *entry, struct dirent **out_dirent) { - assert(pdir); - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t *dir = (vfs_littlefs_dir_t *)pdir; - int res; - struct lfs_info info = {0}; - - sem_take(efs); - do { /* Read until we get a real object name */ - res = lfs_dir_read(efs->fs, &dir->d, &info); - } while (res > 0 && (strcmp(info.name, ".") == 0 || strcmp(info.name, "..") == 0)); - sem_give(efs); - if (res < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGE(TAG, "Failed to readdir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); -#else - ESP_LOGE(TAG, "Failed to readdir \"%s\". Error %d", dir->path, res); -#endif - return -1; - } - - if (info.type == LFS_TYPE_REG) { - ESP_LOGD(TAG, "readdir a file of size %d named \"%s\"", - info.size, info.name); - } else { - ESP_LOGD(TAG, "readdir a dir named \"%s\"", info.name); - } - - if (res == 0) { - /* End of Objs */ - ESP_LOGD(TAG, "Reached the end of the directory."); - *out_dirent = NULL; - } else { - entry->d_ino = 0; - entry->d_type = info.type == LFS_TYPE_REG ? DT_REG : DT_DIR; - strncpy(entry->d_name, info.name, sizeof(entry->d_name)); - *out_dirent = entry; - } - dir->offset++; - - return 0; -} - -static long vfs_littlefs_telldir(void *ctx, DIR *pdir) { - assert(pdir); - vfs_littlefs_dir_t *dir = (vfs_littlefs_dir_t *)pdir; - return dir->offset; -} - -static void vfs_littlefs_seekdir(void *ctx, DIR *pdir, long offset) { - assert(pdir); - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - vfs_littlefs_dir_t *dir = (vfs_littlefs_dir_t *)pdir; - int res; - - if (offset < dir->offset) { - /* close and re-open dir to rewind to beginning */ - sem_take(efs); - res = lfs_dir_rewind(efs->fs, &dir->d); - sem_give(efs); - if (res < 0) { - ESP_LOGE(TAG, "Failed to rewind dir \"%s\". Error %s (%d)", - dir->path, esp_littlefs_errno(res), res); - return; - } - dir->offset = 0; - } - - while (dir->offset < offset) { - struct dirent *out_dirent; - res = vfs_littlefs_readdir_r(ctx, pdir, &dir->e, &out_dirent); - if (res != 0) { - ESP_LOGE(TAG, "Error readdir_r"); - return; - } - } -} - -static int vfs_littlefs_mkdir(void *ctx, const char *name, mode_t mode) { - /* Note: mode is currently unused */ - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - int res; - ESP_LOGD(TAG, "mkdir \"%s\"", name); - - sem_take(efs); - res = lfs_mkdir(efs->fs, name); - sem_give(efs); - if (res < 0) { - ESP_LOGE(TAG, "Failed to mkdir \"%s\". Error %s (%d)", - name, esp_littlefs_errno(res), res); - return res; - } - return 0; -} - -static int vfs_littlefs_rmdir(void *ctx, const char *name) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - struct lfs_info info; - int res; - - /* Error Checking */ - sem_take(efs); - res = lfs_stat(efs->fs, name, &info); - if (res < 0) { - sem_give(efs); - ESP_LOGE(TAG, "\"%s\" doesn't exist.", name); - return -1; - } - - if (info.type != LFS_TYPE_DIR) { - sem_give(efs); - ESP_LOGE(TAG, "\"%s\" is not a directory.", name); - return -1; - } - - /* Unlink the dir */ - res = lfs_remove(efs->fs, name); - sem_give(efs); - if (res < 0) { - ESP_LOGE(TAG, "Failed to unlink path \"%s\". Error %s (%d)", - name, esp_littlefs_errno(res), res); - return -1; - } - - return 0; -} - -#if CONFIG_LITTLEFS_USE_MTIME -/** - * Sets the mtime attr to t. - */ -static int vfs_littlefs_update_mtime_value(esp_littlefs_t *efs, const char *path, time_t t) { - int res; - res = lfs_setattr(efs->fs, path, LITTLEFS_ATTR_MTIME, - &t, sizeof(t)); - if (res < 0) { - ESP_LOGE(TAG, "Failed to update mtime (%d)", res); - } - - return res; -} - -/** - * Sets the mtime attr to an appropriate value - */ -static void vfs_littlefs_update_mtime(esp_littlefs_t *efs, const char *path) { - vfs_littlefs_utime(efs, path, NULL); -} - -static int vfs_littlefs_utime(void *ctx, const char *path, const struct utimbuf *times) { - esp_littlefs_t *efs = (esp_littlefs_t *)ctx; - time_t t; - - assert(path); - - if (times) { - t = times->modtime; - } else { -#if CONFIG_LITTLEFS_MTIME_USE_SECONDS - // use current time - t = time(NULL); -#elif CONFIG_LITTLEFS_MTIME_USE_NONCE - assert(sizeof(time_t) == 4); - t = vfs_littlefs_get_mtime(efs, path); - if (0 == t) - t = esp_random(); - else - t += 1; - - if (0 == t) t = 1; -#else -#error "Invalid MTIME configuration" -#endif - } - - int ret = vfs_littlefs_update_mtime_value(efs, path, t); - return ret; -} - -static time_t vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path) { - time_t t = 0; - int size; - size = lfs_getattr(efs->fs, path, LITTLEFS_ATTR_MTIME, - &t, sizeof(t)); - if (size < 0) { -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - ESP_LOGI(TAG, "Failed to get mtime attribute %s (%d)", - esp_littlefs_errno(size), size); -#else - ESP_LOGI(TAG, "Failed to get mtime attribute %d", size); -#endif - } - return t; -} -#endif //CONFIG_LITTLEFS_USE_MTIME diff --git a/lib/LITTLEFS/src/esp_littlefs.h b/lib/LITTLEFS/src/esp_littlefs.h deleted file mode 100644 index 5f5d3e37..00000000 --- a/lib/LITTLEFS/src/esp_littlefs.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef ESP_LITTLEFS_H__ -#define ESP_LITTLEFS_H__ - -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "sdkconfig.h" - -#include "lfs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - LITTLEFS_ATTR_MTIME, /**< Last Modified - time (seconds) */ - LITTLEFS_ATTR_MAX -}; - -/** - *Configuration structure for esp_vfs_littlefs_register. - */ -typedef struct { - const char *base_path; /**< Mounting point. */ - const char *partition_label; /**< Label of partition to use. */ - uint8_t format_if_mount_failed:1; /**< Format the file system if it fails to mount. */ - uint8_t dont_mount:1; /**< Don't attempt to mount or format. Overrides format_if_mount_failed */ -} esp_vfs_littlefs_conf_t; - -/** - * Register and mount littlefs to VFS with given path prefix. - * - * @param conf Pointer to esp_vfs_littlefs_conf_t configuration structure - * - * @return - * - ESP_OK if success - * - ESP_ERR_NO_MEM if objects could not be allocated - * - ESP_ERR_INVALID_STATE if already mounted or partition is encrypted - * - ESP_ERR_NOT_FOUND if partition for littlefs was not found - * - ESP_FAIL if mount or format fails - */ -esp_err_t esp_vfs_littlefs_register(const esp_vfs_littlefs_conf_t * conf); - -/** - * Unregister and unmount littlefs from VFS - * - * @param partition_label Label of the partition to unregister. - * - * @return - * - ESP_OK if successful - * - ESP_ERR_INVALID_STATE already unregistered - */ -esp_err_t esp_vfs_littlefs_unregister(const char* partition_label); - -/** - * Check if littlefs is mounted - * - * @param partition_label Label of the partition to check. - * - * @return - * - true if mounted - * - false if not mounted - */ -bool esp_littlefs_mounted(const char* partition_label); - -/** - * Format the littlefs partition - * - * @param partition_label Label of the partition to format. - * @return - * - ESP_OK if successful - * - ESP_FAIL on error - */ -esp_err_t esp_littlefs_format(const char* partition_label); - -/** - * Get information for littlefs - * - * @param partition_label Optional, label of the partition to get info for. - * @param[out] total_bytes Size of the file system - * @param[out] used_bytes Current used bytes in the file system - * - * @return - * - ESP_OK if success - * - ESP_ERR_INVALID_STATE if not mounted - */ -esp_err_t esp_littlefs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes); - -#if CONFIG_LITTLEFS_HUMAN_READABLE -/** - * @brief converts an enumerated lfs error into a string. - * @param lfs_errno The enumerated littlefs error. - */ -const char * esp_littlefs_errno(enum lfs_error lfs_errno); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/lib/LITTLEFS/src/lfs.c b/lib/LITTLEFS/src/lfs.c deleted file mode 100644 index eb832fa0..00000000 --- a/lib/LITTLEFS/src/lfs.c +++ /dev/null @@ -1,4913 +0,0 @@ -/* - * The little filesystem - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "lfs.h" -#include "lfs_util.h" - -#define LFS_BLOCK_NULL ((lfs_block_t)-1) -#define LFS_BLOCK_INLINE ((lfs_block_t)-2) - -/// Caching block device operations /// -static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { - // do not zero, cheaper if cache is readonly or only going to be - // written with identical data (during relocates) - (void)lfs; - rcache->block = LFS_BLOCK_NULL; -} - -static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { - // zero to avoid information leak - memset(pcache->buffer, 0xff, lfs->cfg->cache_size); - pcache->block = LFS_BLOCK_NULL; -} - -static int lfs_bd_read(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - if (block >= lfs->cfg->block_count || - off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && block == pcache->block && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (block == rcache->block && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - if (size >= hint && off % lfs->cfg->read_size == 0 && - size >= lfs->cfg->read_size) { - // bypass cache? - diff = lfs_aligndown(diff, lfs->cfg->read_size); - int err = lfs->cfg->read(lfs->cfg, block, off, data, diff); - if (err) { - return err; - } - - data += diff; - off += diff; - size -= diff; - continue; - } - - // load to cache, first condition can no longer fail - LFS_ASSERT(block < lfs->cfg->block_count); - rcache->block = block; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min( - lfs_min( - lfs_alignup(off+hint, lfs->cfg->read_size), - lfs->cfg->block_size) - - rcache->off, - lfs->cfg->cache_size); - int err = lfs->cfg->read(lfs->cfg, rcache->block, - rcache->off, rcache->buffer, rcache->size); - LFS_ASSERT(err <= 0); - if (err) { - return err; - } - } - - return 0; -} - -enum { - LFS_CMP_EQ = 0, - LFS_CMP_LT = 1, - LFS_CMP_GT = 2, -}; - -static int lfs_bd_cmp(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - - for (lfs_off_t i = 0; i < size; i++) { - uint8_t dat; - int err = lfs_bd_read(lfs, - pcache, rcache, hint-i, - block, off+i, &dat, 1); - if (err) { - return err; - } - - if (dat != data[i]) { - return (dat < data[i]) ? LFS_CMP_LT : LFS_CMP_GT; - } - } - - return LFS_CMP_EQ; -} - -static int lfs_bd_flush(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) { - LFS_ASSERT(pcache->block < lfs->cfg->block_count); - lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); - int err = lfs->cfg->prog(lfs->cfg, pcache->block, - pcache->off, pcache->buffer, diff); - LFS_ASSERT(err <= 0); - if (err) { - return err; - } - - if (validate) { - // check data on disk - lfs_cache_drop(lfs, rcache); - int res = lfs_bd_cmp(lfs, - NULL, rcache, diff, - pcache->block, pcache->off, pcache->buffer, diff); - if (res < 0) { - return res; - } - - if (res != LFS_CMP_EQ) { - return LFS_ERR_CORRUPT; - } - } - - lfs_cache_zero(lfs, pcache); - } - - return 0; -} - -static int lfs_bd_sync(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { - lfs_cache_drop(lfs, rcache); - - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - - err = lfs->cfg->sync(lfs->cfg); - LFS_ASSERT(err <= 0); - return err; -} - -static int lfs_bd_prog(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate, - lfs_block_t block, lfs_off_t off, - const void *buffer, lfs_size_t size) { - const uint8_t *data = buffer; - LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count); - LFS_ASSERT(off + size <= lfs->cfg->block_size); - - while (size > 0) { - if (block == pcache->block && - off >= pcache->off && - off < pcache->off + lfs->cfg->cache_size) { - // already fits in pcache? - lfs_size_t diff = lfs_min(size, - lfs->cfg->cache_size - (off-pcache->off)); - memcpy(&pcache->buffer[off-pcache->off], data, diff); - - data += diff; - off += diff; - size -= diff; - - pcache->size = lfs_max(pcache->size, off - pcache->off); - if (pcache->size == lfs->cfg->cache_size) { - // eagerly flush out pcache if we fill up - int err = lfs_bd_flush(lfs, pcache, rcache, validate); - if (err) { - return err; - } - } - - continue; - } - - // pcache must have been flushed, either by programming and - // entire block or manually flushing the pcache - LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); - - // prepare pcache, first condition can no longer fail - pcache->block = block; - pcache->off = lfs_aligndown(off, lfs->cfg->prog_size); - pcache->size = 0; - } - - return 0; -} - -static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { - LFS_ASSERT(block < lfs->cfg->block_count); - int err = lfs->cfg->erase(lfs->cfg, block); - LFS_ASSERT(err <= 0); - return err; -} - - -/// Small type-level utilities /// -// operations on block pairs -static inline void lfs_pair_swap(lfs_block_t pair[2]) { - lfs_block_t t = pair[0]; - pair[0] = pair[1]; - pair[1] = t; -} - -static inline bool lfs_pair_isnull(const lfs_block_t pair[2]) { - return pair[0] == LFS_BLOCK_NULL || pair[1] == LFS_BLOCK_NULL; -} - -static inline int lfs_pair_cmp( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return !(paira[0] == pairb[0] || paira[1] == pairb[1] || - paira[0] == pairb[1] || paira[1] == pairb[0]); -} - -static inline bool lfs_pair_sync( - const lfs_block_t paira[2], - const lfs_block_t pairb[2]) { - return (paira[0] == pairb[0] && paira[1] == pairb[1]) || - (paira[0] == pairb[1] && paira[1] == pairb[0]); -} - -static inline void lfs_pair_fromle32(lfs_block_t pair[2]) { - pair[0] = lfs_fromle32(pair[0]); - pair[1] = lfs_fromle32(pair[1]); -} - -static inline void lfs_pair_tole32(lfs_block_t pair[2]) { - pair[0] = lfs_tole32(pair[0]); - pair[1] = lfs_tole32(pair[1]); -} - -// operations on 32-bit entry tags -typedef uint32_t lfs_tag_t; -typedef int32_t lfs_stag_t; - -#define LFS_MKTAG(type, id, size) \ - (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size)) - -#define LFS_MKTAG_IF(cond, type, id, size) \ - ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0)) - -#define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \ - ((cond) ? LFS_MKTAG(type1, id1, size1) : LFS_MKTAG(type2, id2, size2)) - -static inline bool lfs_tag_isvalid(lfs_tag_t tag) { - return !(tag & 0x80000000); -} - -static inline bool lfs_tag_isdelete(lfs_tag_t tag) { - return ((int32_t)(tag << 22) >> 22) == -1; -} - -static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { - return (tag & 0x70000000) >> 20; -} - -static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { - return (tag & 0x7ff00000) >> 20; -} - -static inline uint8_t lfs_tag_chunk(lfs_tag_t tag) { - return (tag & 0x0ff00000) >> 20; -} - -static inline int8_t lfs_tag_splice(lfs_tag_t tag) { - return (int8_t)lfs_tag_chunk(tag); -} - -static inline uint16_t lfs_tag_id(lfs_tag_t tag) { - return (tag & 0x000ffc00) >> 10; -} - -static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { - return tag & 0x000003ff; -} - -static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { - return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); -} - -// operations on attributes in attribute lists -struct lfs_mattr { - lfs_tag_t tag; - const void *buffer; -}; - -struct lfs_diskoff { - lfs_block_t block; - lfs_off_t off; -}; - -#define LFS_MKATTRS(...) \ - (struct lfs_mattr[]){__VA_ARGS__}, \ - sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) - -// operations on global state -static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) { - for (int i = 0; i < 3; i++) { - ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; - } -} - -static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) { - for (int i = 0; i < 3; i++) { - if (((uint32_t*)a)[i] != 0) { - return false; - } - } - return true; -} - -static inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) { - return lfs_tag_size(a->tag); -} - -static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) { - return lfs_tag_size(a->tag); -} - -static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) { - return lfs_tag_type1(a->tag); -} - -static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a, - const lfs_block_t *pair) { - return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; -} - -static inline void lfs_gstate_fromle32(lfs_gstate_t *a) { - a->tag = lfs_fromle32(a->tag); - a->pair[0] = lfs_fromle32(a->pair[0]); - a->pair[1] = lfs_fromle32(a->pair[1]); -} - -static inline void lfs_gstate_tole32(lfs_gstate_t *a) { - a->tag = lfs_tole32(a->tag); - a->pair[0] = lfs_tole32(a->pair[0]); - a->pair[1] = lfs_tole32(a->pair[1]); -} - -// other endianness operations -static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { - ctz->head = lfs_fromle32(ctz->head); - ctz->size = lfs_fromle32(ctz->size); -} - -static void lfs_ctz_tole32(struct lfs_ctz *ctz) { - ctz->head = lfs_tole32(ctz->head); - ctz->size = lfs_tole32(ctz->size); -} - -static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) { - superblock->version = lfs_fromle32(superblock->version); - superblock->block_size = lfs_fromle32(superblock->block_size); - superblock->block_count = lfs_fromle32(superblock->block_count); - superblock->name_max = lfs_fromle32(superblock->name_max); - superblock->file_max = lfs_fromle32(superblock->file_max); - superblock->attr_max = lfs_fromle32(superblock->attr_max); -} - -static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { - superblock->version = lfs_tole32(superblock->version); - superblock->block_size = lfs_tole32(superblock->block_size); - superblock->block_count = lfs_tole32(superblock->block_count); - superblock->name_max = lfs_tole32(superblock->name_max); - superblock->file_max = lfs_tole32(superblock->file_max); - superblock->attr_max = lfs_tole32(superblock->attr_max); -} - - -/// Internal operations predeclared here /// -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount); -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end); -static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]); -static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *pdir); -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], - lfs_mdir_t *parent); -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]); -int lfs_fs_traverseraw(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data, - bool includeorphans); -static int lfs_fs_forceconsistency(lfs_t *lfs); -static int lfs_deinit(lfs_t *lfs); -#ifdef LFS_MIGRATE -static int lfs1_traverse(lfs_t *lfs, - int (*cb)(void*, lfs_block_t), void *data); -#endif - -/// Block allocator /// -static int lfs_alloc_lookahead(void *p, lfs_block_t block) { - lfs_t *lfs = (lfs_t*)p; - lfs_block_t off = ((block - lfs->free.off) - + lfs->cfg->block_count) % lfs->cfg->block_count; - - if (off < lfs->free.size) { - lfs->free.buffer[off / 32] |= 1U << (off % 32); - } - - return 0; -} - -static void lfs_alloc_ack(lfs_t *lfs) { - lfs->free.ack = lfs->cfg->block_count; -} - -// Invalidate the lookahead buffer. This is done during mounting and -// failed traversals -static void lfs_alloc_reset(lfs_t *lfs) { - lfs->free.off = lfs->seed % lfs->cfg->block_size; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); -} - -static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { - while (true) { - while (lfs->free.i != lfs->free.size) { - lfs_block_t off = lfs->free.i; - lfs->free.i += 1; - lfs->free.ack -= 1; - - if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { - // found a free block - *block = (lfs->free.off + off) % lfs->cfg->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs->free.i != lfs->free.size && - (lfs->free.buffer[lfs->free.i / 32] - & (1U << (lfs->free.i % 32)))) { - lfs->free.i += 1; - lfs->free.ack -= 1; - } - - return 0; - } - } - - // check if we have looked at all blocks since last ack - if (lfs->free.ack == 0) { - LFS_ERROR("No more free space %"PRIu32, - lfs->free.i + lfs->free.off); - return LFS_ERR_NOSPC; - } - - lfs->free.off = (lfs->free.off + lfs->free.size) - % lfs->cfg->block_count; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack); - lfs->free.i = 0; - - // find mask of free blocks from tree - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - int err = lfs_fs_traverseraw(lfs, lfs_alloc_lookahead, lfs, true); - if (err) { - lfs_alloc_reset(lfs); - return err; - } - } -} - -/// Metadata pair and directory operations /// -static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t goff, void *gbuffer, lfs_size_t gsize) { - lfs_off_t off = dir->off; - lfs_tag_t ntag = dir->etag; - lfs_stag_t gdiff = 0; - - if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair) && - lfs_tag_id(gmask) != 0 && - lfs_tag_id(lfs->gdisk.tag) <= lfs_tag_id(gtag)) { - // synthetic moves - gdiff -= LFS_MKTAG(0, 1, 0); - } - - // iterate over dir block backwards (for faster lookups) - while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { - off -= lfs_tag_dsize(ntag); - lfs_tag_t tag = ntag; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(ntag), - dir->pair[0], off, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff; - - if (lfs_tag_id(gmask) != 0 && - lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) { - if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { - // found where we were created - return LFS_ERR_NOENT; - } - - // move around splices - gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - if ((gmask & tag) == (gmask & (gtag - gdiff))) { - if (lfs_tag_isdelete(tag)) { - return LFS_ERR_NOENT; - } - - lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize); - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, diff, - dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); - if (err) { - return err; - } - - memset((uint8_t*)gbuffer + diff, 0, gsize - diff); - - return tag + gdiff; - } - } - - return LFS_ERR_NOENT; -} - -static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) { - return lfs_dir_getslice(lfs, dir, - gmask, gtag, - 0, buffer, lfs_tag_size(gtag)); -} - -static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir, - const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, - lfs_tag_t gmask, lfs_tag_t gtag, - lfs_off_t off, void *buffer, lfs_size_t size) { - uint8_t *data = buffer; - if (off+size > lfs->cfg->block_size) { - return LFS_ERR_CORRUPT; - } - - while (size > 0) { - lfs_size_t diff = size; - - if (pcache && pcache->block == LFS_BLOCK_INLINE && - off < pcache->off + pcache->size) { - if (off >= pcache->off) { - // is already in pcache? - diff = lfs_min(diff, pcache->size - (off-pcache->off)); - memcpy(data, &pcache->buffer[off-pcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // pcache takes priority - diff = lfs_min(diff, pcache->off-off); - } - - if (rcache->block == LFS_BLOCK_INLINE && - off < rcache->off + rcache->size) { - if (off >= rcache->off) { - // is already in rcache? - diff = lfs_min(diff, rcache->size - (off-rcache->off)); - memcpy(data, &rcache->buffer[off-rcache->off], diff); - - data += diff; - off += diff; - size -= diff; - continue; - } - - // rcache takes priority - diff = lfs_min(diff, rcache->off-off); - } - - // load to cache, first condition can no longer fail - rcache->block = LFS_BLOCK_INLINE; - rcache->off = lfs_aligndown(off, lfs->cfg->read_size); - rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size), - lfs->cfg->cache_size); - int err = lfs_dir_getslice(lfs, dir, gmask, gtag, - rcache->off, rcache->buffer, rcache->size); - if (err < 0) { - return err; - } - } - - return 0; -} - -static int lfs_dir_traverse_filter(void *p, - lfs_tag_t tag, const void *buffer) { - lfs_tag_t *filtertag = p; - (void)buffer; - - // which mask depends on unique bit in tag structure - uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0)) - ? LFS_MKTAG(0x7ff, 0x3ff, 0) - : LFS_MKTAG(0x700, 0x3ff, 0); - - // check for redundancy - if ((mask & tag) == (mask & *filtertag) || - lfs_tag_isdelete(*filtertag) || - (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == ( - LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { - return true; - } - - // check if we need to adjust for created/deleted tags - if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE && - lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) { - *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - - return false; -} - -static int lfs_dir_traverse(lfs_t *lfs, - const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, - const struct lfs_mattr *attrs, int attrcount, - lfs_tag_t tmask, lfs_tag_t ttag, - uint16_t begin, uint16_t end, int16_t diff, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // iterate over directory and attrs - while (true) { - lfs_tag_t tag; - const void *buffer; - struct lfs_diskoff disk; - if (off+lfs_tag_dsize(ptag) < dir->off) { - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - return err; - } - - tag = (lfs_frombe32(tag) ^ ptag) | 0x80000000; - disk.block = dir->pair[0]; - disk.off = off+sizeof(lfs_tag_t); - buffer = &disk; - ptag = tag; - } else if (attrcount > 0) { - tag = attrs[0].tag; - buffer = attrs[0].buffer; - attrs += 1; - attrcount -= 1; - } else { - return 0; - } - - lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); - if ((mask & tmask & tag) != (mask & tmask & ttag)) { - continue; - } - - // do we need to filter? inlining the filtering logic here allows - // for some minor optimizations - if (lfs_tag_id(tmask) != 0) { - // scan for duplicates and update tag based on creates/deletes - int filter = lfs_dir_traverse(lfs, - dir, off, ptag, attrs, attrcount, - 0, 0, 0, 0, 0, - lfs_dir_traverse_filter, &tag); - if (filter < 0) { - return filter; - } - - if (filter) { - continue; - } - - // in filter range? - if (!(lfs_tag_id(tag) >= begin && lfs_tag_id(tag) < end)) { - continue; - } - } - - // handle special cases for mcu-side operations - if (lfs_tag_type3(tag) == LFS_FROM_NOOP) { - // do nothing - } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { - uint16_t fromid = lfs_tag_size(tag); - uint16_t toid = lfs_tag_id(tag); - int err = lfs_dir_traverse(lfs, - buffer, 0, 0xffffffff, NULL, 0, - LFS_MKTAG(0x600, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), - fromid, fromid+1, toid-fromid+diff, - cb, data); - if (err) { - return err; - } - } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { - for (unsigned i = 0; i < lfs_tag_size(tag); i++) { - const struct lfs_attr *a = buffer; - int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type, - lfs_tag_id(tag) + diff, a[i].size), a[i].buffer); - if (err) { - return err; - } - } - } else { - int err = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer); - if (err) { - return err; - } - } - } -} - -static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2], - lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id, - int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { - // we can find tag very efficiently during a fetch, since we're already - // scanning the entire directory - lfs_stag_t besttag = -1; - - // if either block address is invalid we return LFS_ERR_CORRUPT here, - // otherwise later writes to the pair could fail - if (pair[0] >= lfs->cfg->block_count || pair[1] >= lfs->cfg->block_count) { - return LFS_ERR_CORRUPT; - } - - // find the block with the most recent revision - uint32_t revs[2] = {0, 0}; - int r = 0; - for (int i = 0; i < 2; i++) { - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(revs[i]), - pair[i], 0, &revs[i], sizeof(revs[i])); - revs[i] = lfs_fromle32(revs[i]); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - if (err != LFS_ERR_CORRUPT && - lfs_scmp(revs[i], revs[(i+1)%2]) > 0) { - r = i; - } - } - - dir->pair[0] = pair[(r+0)%2]; - dir->pair[1] = pair[(r+1)%2]; - dir->rev = revs[(r+0)%2]; - dir->off = 0; // nonzero = found some commits - - // now scan tags to fetch the actual dir and find possible match - for (int i = 0; i < 2; i++) { - lfs_off_t off = 0; - lfs_tag_t ptag = 0xffffffff; - - uint16_t tempcount = 0; - lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; - bool tempsplit = false; - lfs_stag_t tempbesttag = besttag; - - dir->rev = lfs_tole32(dir->rev); - uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - - while (true) { - // extract next tag - lfs_tag_t tag; - off += lfs_tag_dsize(ptag); - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off, &tag, sizeof(tag)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - // can't continue? - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &tag, sizeof(tag)); - tag = lfs_frombe32(tag) ^ ptag; - - // next commit not yet programmed or we're not in valid range - if (!lfs_tag_isvalid(tag)) { - dir->erased = (lfs_tag_type1(ptag) == LFS_TYPE_CRC && - dir->off % lfs->cfg->prog_size == 0); - break; - } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { - dir->erased = false; - break; - } - - ptag = tag; - - if (lfs_tag_type1(tag) == LFS_TYPE_CRC) { - // check the crc attr - uint32_t dcrc; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - dcrc = lfs_fromle32(dcrc); - - if (crc != dcrc) { - dir->erased = false; - break; - } - - // reset the next bit if we need to - ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31; - - // toss our crc into the filesystem seed for - // pseudorandom numbers - lfs->seed ^= crc; - - // update with what's found so far - besttag = tempbesttag; - dir->off = off + lfs_tag_dsize(tag); - dir->etag = ptag; - dir->count = tempcount; - dir->tail[0] = temptail[0]; - dir->tail[1] = temptail[1]; - dir->split = tempsplit; - - // reset crc - crc = 0xffffffff; - continue; - } - - // crc the entry first, hopefully leaving it in the cache - for (lfs_off_t j = sizeof(tag); j < lfs_tag_dsize(tag); j++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+j, &dat, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - // directory modification tags? - if (lfs_tag_type1(tag) == LFS_TYPE_NAME) { - // increase count of files if necessary - if (lfs_tag_id(tag) >= tempcount) { - tempcount = lfs_tag_id(tag) + 1; - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) { - tempcount += lfs_tag_splice(tag); - - if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | - (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { - tempbesttag |= 0x80000000; - } else if (tempbesttag != -1 && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); - } - } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { - tempsplit = (lfs_tag_chunk(tag) & 1); - - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, lfs->cfg->block_size, - dir->pair[0], off+sizeof(tag), &temptail, 8); - if (err) { - if (err == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - } - lfs_pair_fromle32(temptail); - } - - // found a match for our fetcher? - if ((fmask & tag) == (fmask & ftag)) { - int res = cb(data, tag, &(struct lfs_diskoff){ - dir->pair[0], off+sizeof(tag)}); - if (res < 0) { - if (res == LFS_ERR_CORRUPT) { - dir->erased = false; - break; - } - return res; - } - - if (res == LFS_CMP_EQ) { - // found a match - tempbesttag = tag; - } else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == - (LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) { - // found an identical tag, but contents didn't match - // this must mean that our besttag has been overwritten - tempbesttag = -1; - } else if (res == LFS_CMP_GT && - lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { - // found a greater match, keep track to keep things sorted - tempbesttag = tag | 0x80000000; - } - } - } - - // consider what we have good enough - if (dir->off > 0) { - // synthetic move - if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) { - if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) { - besttag |= 0x80000000; - } else if (besttag != -1 && - lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) { - besttag -= LFS_MKTAG(0, 1, 0); - } - } - - // found tag? or found best id? - if (id) { - *id = lfs_min(lfs_tag_id(besttag), dir->count); - } - - if (lfs_tag_isvalid(besttag)) { - return besttag; - } else if (lfs_tag_id(besttag) < dir->count) { - return LFS_ERR_NOENT; - } else { - return 0; - } - } - - // failed, try the other block? - lfs_pair_swap(dir->pair); - dir->rev = revs[(r+1)%2]; - } - - LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", - dir->pair[0], dir->pair[1]); - return LFS_ERR_CORRUPT; -} - -static int lfs_dir_fetch(lfs_t *lfs, - lfs_mdir_t *dir, const lfs_block_t pair[2]) { - // note, mask=-1, tag=-1 can never match a tag since this - // pattern has the invalid bit set - return (int)lfs_dir_fetchmatch(lfs, dir, pair, - (lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL); -} - -static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, - lfs_gstate_t *gstate) { - lfs_gstate_t temp; - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); - if (res < 0 && res != LFS_ERR_NOENT) { - return res; - } - - if (res != LFS_ERR_NOENT) { - // xor together to find resulting gstate - lfs_gstate_fromle32(&temp); - lfs_gstate_xor(gstate, &temp); - } - - return 0; -} - -static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, - uint16_t id, struct lfs_info *info) { - if (id == 0x3ff) { - // special case for root - strcpy(info->name, "/"); - info->type = LFS_TYPE_DIR; - return 0; - } - - lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); - if (tag < 0) { - return (int)tag; - } - - info->type = lfs_tag_type3(tag); - - struct lfs_ctz ctz; - tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - return (int)tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - info->size = ctz.size; - } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - info->size = lfs_tag_size(tag); - } - - return 0; -} - -struct lfs_dir_find_match { - lfs_t *lfs; - const void *name; - lfs_size_t size; -}; - -static int lfs_dir_find_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_dir_find_match *name = data; - lfs_t *lfs = name->lfs; - const struct lfs_diskoff *disk = buffer; - - // compare with disk - lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag)); - int res = lfs_bd_cmp(lfs, - NULL, &lfs->rcache, diff, - disk->block, disk->off, name->name, diff); - if (res != LFS_CMP_EQ) { - return res; - } - - // only equal if our size is still the same - if (name->size != lfs_tag_size(tag)) { - return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT; - } - - // found a match! - return LFS_CMP_EQ; -} - -static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, - const char **path, uint16_t *id) { - // we reduce path to a single name if we can find it - const char *name = *path; - if (id) { - *id = 0x3ff; - } - - // default to root dir - lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); - dir->tail[0] = lfs->root[0]; - dir->tail[1] = lfs->root[1]; - - while (true) { -nextname: - // skip slashes - name += strspn(name, "/"); - lfs_size_t namelen = strcspn(name, "/"); - - // skip '.' and root '..' - if ((namelen == 1 && memcmp(name, ".", 1) == 0) || - (namelen == 2 && memcmp(name, "..", 2) == 0)) { - name += namelen; - goto nextname; - } - - // skip if matched by '..' in name - const char *suffix = name + namelen; - lfs_size_t sufflen; - int depth = 1; - while (true) { - suffix += strspn(suffix, "/"); - sufflen = strcspn(suffix, "/"); - if (sufflen == 0) { - break; - } - - if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { - depth -= 1; - if (depth == 0) { - name = suffix + sufflen; - goto nextname; - } - } else { - depth += 1; - } - - suffix += sufflen; - } - - // found path - if (name[0] == '\0') { - return tag; - } - - // update what we've found so far - *path = name; - - // only continue if we hit a directory - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - return LFS_ERR_NOTDIR; - } - - // grab the entry data - if (lfs_tag_id(tag) != 0x3ff) { - lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); - if (res < 0) { - return res; - } - lfs_pair_fromle32(dir->tail); - } - - // find entry matching name - while (true) { - tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, - LFS_MKTAG(0x780, 0, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, name, namelen}); - if (tag < 0) { - return tag; - } - - if (tag) { - break; - } - - if (!dir->split) { - return LFS_ERR_NOENT; - } - } - - // to next name - name += namelen; - } -} - -// commit logic -struct lfs_commit { - lfs_block_t block; - lfs_off_t off; - lfs_tag_t ptag; - uint32_t crc; - - lfs_off_t begin; - lfs_off_t end; -}; - -static int lfs_dir_commitprog(lfs_t *lfs, struct lfs_commit *commit, - const void *buffer, lfs_size_t size) { - int err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off , - (const uint8_t*)buffer, size); - if (err) { - return err; - } - - commit->crc = lfs_crc(commit->crc, buffer, size); - commit->off += size; - return 0; -} - -static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, - lfs_tag_t tag, const void *buffer) { - // check if we fit - lfs_size_t dsize = lfs_tag_dsize(tag); - if (commit->off + dsize > commit->end) { - return LFS_ERR_NOSPC; - } - - // write out tag - lfs_tag_t ntag = lfs_tobe32((tag & 0x7fffffff) ^ commit->ptag); - int err = lfs_dir_commitprog(lfs, commit, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - if (!(tag & 0x80000000)) { - // from memory - err = lfs_dir_commitprog(lfs, commit, buffer, dsize-sizeof(tag)); - if (err) { - return err; - } - } else { - // from disk - const struct lfs_diskoff *disk = buffer; - for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { - // rely on caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dsize-sizeof(tag)-i, - disk->block, disk->off+i, &dat, 1); - if (err) { - return err; - } - - err = lfs_dir_commitprog(lfs, commit, &dat, 1); - if (err) { - return err; - } - } - } - - commit->ptag = tag & 0x7fffffff; - return 0; -} - -static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { - const lfs_off_t off1 = commit->off; - const uint32_t crc1 = commit->crc; - // align to program units - const lfs_off_t end = lfs_alignup(off1 + 2*sizeof(uint32_t), - lfs->cfg->prog_size); - - // create crc tags to fill up remainder of commit, note that - // padding is not crced, which lets fetches skip padding but - // makes committing a bit more complicated - while (commit->off < end) { - lfs_off_t off = commit->off + sizeof(lfs_tag_t); - lfs_off_t noff = lfs_min(end - off, 0x3fe) + off; - if (noff < end) { - noff = lfs_min(noff, end - 2*sizeof(uint32_t)); - } - - // read erased state from next program unit - lfs_tag_t tag = 0xffffffff; - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(tag), - commit->block, noff, &tag, sizeof(tag)); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // build crc tag - bool reset = ~lfs_frombe32(tag) >> 31; - tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, noff - off); - - // write out crc - uint32_t footer[2]; - footer[0] = lfs_tobe32(tag ^ commit->ptag); - commit->crc = lfs_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs_tole32(commit->crc); - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); - if (err) { - return err; - } - - commit->off += sizeof(tag)+lfs_tag_size(tag); - commit->ptag = tag ^ ((lfs_tag_t)reset << 31); - commit->crc = 0xffffffff; // reset crc for next "commit" - } - - // flush buffers - int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); - if (err) { - return err; - } - - // successful commit, check checksums to make sure - lfs_off_t off = commit->begin; - lfs_off_t noff = off1 + sizeof(uint32_t); - while (off < end) { - uint32_t crc = 0xffffffff; - for (lfs_off_t i = off; i < noff+sizeof(uint32_t); i++) { - // check against written crc, may catch blocks that - // become readonly and match our commit size exactly - if (i == off1 && crc != crc1) { - return LFS_ERR_CORRUPT; - } - - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, noff+sizeof(uint32_t)-i, - commit->block, i, &dat, 1); - if (err) { - return err; - } - - crc = lfs_crc(crc, &dat, 1); - } - - // detected write error? - if (crc != 0) { - return LFS_ERR_CORRUPT; - } - - // skip padding - off = lfs_min(end - noff, 0x3fe) + noff; - if (off < end) { - off = lfs_min(off, end - 2*sizeof(uint32_t)); - } - noff = off + sizeof(uint32_t); - } - - return 0; -} - -static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { - // allocate pair of dir blocks (backwards, so we write block 1 first) - for (int i = 0; i < 2; i++) { - int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); - if (err) { - return err; - } - } - - // zero for reproducability in case initial block is unreadable - dir->rev = 0; - - // rather than clobbering one of the blocks we just pretend - // the revision may be valid - int err = lfs_bd_read(lfs, - NULL, &lfs->rcache, sizeof(dir->rev), - dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - - // make sure we don't immediately evict - dir->rev += dir->rev & 1; - - // set defaults - dir->off = sizeof(dir->rev); - dir->etag = 0xffffffff; - dir->count = 0; - dir->tail[0] = LFS_BLOCK_NULL; - dir->tail[1] = LFS_BLOCK_NULL; - dir->erased = false; - dir->split = false; - - // don't write out yet, let caller take care of that - return 0; -} - -static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { - // steal state - int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); - if (err) { - return err; - } - - // steal tail - lfs_pair_tole32(tail->tail); - err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); - lfs_pair_fromle32(tail->tail); - if (err) { - return err; - } - - return 0; -} - -static int lfs_dir_split(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t split, uint16_t end) { - // create tail directory - lfs_alloc_ack(lfs); - lfs_mdir_t tail; - int err = lfs_dir_alloc(lfs, &tail); - if (err) { - return err; - } - - tail.split = dir->split; - tail.tail[0] = dir->tail[0]; - tail.tail[1] = dir->tail[1]; - - err = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end); - if (err) { - return err; - } - - dir->tail[0] = tail.pair[0]; - dir->tail[1] = tail.pair[1]; - dir->split = true; - - // update root if needed - if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && split == 0) { - lfs->root[0] = tail.pair[0]; - lfs->root[1] = tail.pair[1]; - } - - return 0; -} - -static int lfs_dir_commit_size(void *p, lfs_tag_t tag, const void *buffer) { - lfs_size_t *size = p; - (void)buffer; - - *size += lfs_tag_dsize(tag); - return 0; -} - -struct lfs_dir_commit_commit { - lfs_t *lfs; - struct lfs_commit *commit; -}; - -static int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) { - struct lfs_dir_commit_commit *commit = p; - return lfs_dir_commitattr(commit->lfs, commit->commit, tag, buffer); -} - -static int lfs_dir_compact(lfs_t *lfs, - lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, - lfs_mdir_t *source, uint16_t begin, uint16_t end) { - // save some state in case block is bad - const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]}; - bool relocated = false; - bool tired = false; - - // should we split? - while (end - begin > 1) { - // find size - lfs_size_t size = 0; - int err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_size, &size); - if (err) { - return err; - } - - // space is complicated, we need room for tail, crc, gstate, - // cleanup delete, and we cap at half a block to give room - // for metadata updates. - if (end - begin < 0xff && - size <= lfs_min(lfs->cfg->block_size - 36, - lfs_alignup(lfs->cfg->block_size/2, - lfs->cfg->prog_size))) { - break; - } - - // can't fit, need to split, we should really be finding the - // largest size that fits with a small binary search, but right now - // it's not worth the code size - uint16_t split = (end - begin) / 2; - err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin+split, end); - if (err) { - // if we fail to split, we may be able to overcompact, unless - // we're too big for even the full block, in which case our - // only option is to error - if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) { - break; - } - return err; - } - - end = begin + split; - } - - // increment revision count - dir->rev += 1; - // If our revision count == n * block_cycles, we should force a relocation, - // this is how littlefs wear-levels at the metadata-pair level. Note that we - // actually use (block_cycles+1)|1, this is to avoid two corner cases: - // 1. block_cycles = 1, which would prevent relocations from terminating - // 2. block_cycles = 2n, which, due to aliasing, would only ever relocate - // one metadata block in the pair, effectively making this useless - if (lfs->cfg->block_cycles > 0 && - (dir->rev % ((lfs->cfg->block_cycles+1)|1) == 0)) { - if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { - // oh no! we're writing too much to the superblock, - // should we expand? - lfs_ssize_t res = lfs_fs_size(lfs); - if (res < 0) { - return res; - } - - // do we have extra space? littlefs can't reclaim this space - // by itself, so expand cautiously - if ((lfs_size_t)res < lfs->cfg->block_count/2) { - LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); - int err = lfs_dir_split(lfs, dir, attrs, attrcount, - source, begin, end); - if (err && err != LFS_ERR_NOSPC) { - return err; - } - - // welp, we tried, if we ran out of space there's not much - // we can do, we'll error later if we've become frozen - if (!err) { - end = begin; - } - } -#ifdef LFS_MIGRATE - } else if (lfs->lfs1) { - // do not proactively relocate blocks during migrations, this - // can cause a number of failure states such: clobbering the - // v1 superblock if we relocate root, and invalidating directory - // pointers if we relocate the head of a directory. On top of - // this, relocations increase the overall complexity of - // lfs_migration, which is already a delicate operation. -#endif - } else { - // we're writing too much, time to relocate - tired = true; - goto relocate; - } - } - - // begin loop to commit compaction to blocks until a compact sticks - while (true) { - { - // setup commit state - struct lfs_commit commit = { - .block = dir->pair[1], - .off = 0, - .ptag = 0xffffffff, - .crc = 0xffffffff, - - .begin = 0, - .end = lfs->cfg->block_size - 8, - }; - - // erase block to write to - int err = lfs_bd_erase(lfs, dir->pair[1]); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // write out header - dir->rev = lfs_tole32(dir->rev); - err = lfs_dir_commitprog(lfs, &commit, - &dir->rev, sizeof(dir->rev)); - dir->rev = lfs_fromle32(dir->rev); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // traverse the directory, this time writing out all unique tags - err = lfs_dir_traverse(lfs, - source, 0, 0xffffffff, attrs, attrcount, - LFS_MKTAG(0x400, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_NAME, 0, 0), - begin, end, -begin, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // commit tail, which may be new after last size check - if (!lfs_pair_isnull(dir->tail)) { - lfs_pair_tole32(dir->tail); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), - dir->tail); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // bring over gstate? - lfs_gstate_t delta = {0}; - if (!relocated) { - lfs_gstate_xor(&delta, &lfs->gdisk); - lfs_gstate_xor(&delta, &lfs->gstate); - } - lfs_gstate_xor(&delta, &lfs->gdelta); - delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); - - err = lfs_dir_getgstate(lfs, dir, &delta); - if (err) { - return err; - } - - if (!lfs_gstate_iszero(&delta)) { - lfs_gstate_tole32(&delta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(delta)), &delta); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // complete commit with crc - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // successful compaction, swap dir pair to indicate most recent - LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); - lfs_pair_swap(dir->pair); - dir->count = end - begin; - dir->off = commit.off; - dir->etag = commit.ptag; - // update gstate - lfs->gdelta = (lfs_gstate_t){0}; - if (!relocated) { - lfs->gdisk = lfs->gstate; - } - } - break; - -relocate: - // commit was corrupted, drop caches and prepare to relocate block - relocated = true; - lfs_cache_drop(lfs, &lfs->pcache); - if (!tired) { - LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]); - } - - // can't relocate superblock, filesystem is now frozen - if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { - LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", - dir->pair[1]); - return LFS_ERR_NOSPC; - } - - // relocate half of pair - int err = lfs_alloc(lfs, &dir->pair[1]); - if (err && (err != LFS_ERR_NOSPC || !tired)) { - return err; - } - - tired = false; - continue; - } - - if (relocated) { - // update references if we relocated - LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); - int err = lfs_fs_relocate(lfs, oldpair, dir->pair); - if (err) { - return err; - } - } - - return 0; -} - -static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs, int attrcount) { - // check for any inline files that aren't RAM backed and - // forcefully evict them, needed for filesystem consistency - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 && - f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && - f->ctz.size > lfs->cfg->cache_size) { - int err = lfs_file_outline(lfs, f); - if (err) { - return err; - } - - err = lfs_file_flush(lfs, f); - if (err) { - return err; - } - } - } - - // calculate changes to the directory - lfs_mdir_t olddir = *dir; - bool hasdelete = false; - for (int i = 0; i < attrcount; i++) { - if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { - dir->count += 1; - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) { - LFS_ASSERT(dir->count > 0); - dir->count -= 1; - hasdelete = true; - } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { - dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; - dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; - dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); - lfs_pair_fromle32(dir->tail); - } - } - - // should we actually drop the directory block? - if (hasdelete && dir->count == 0) { - lfs_mdir_t pdir; - int err = lfs_fs_pred(lfs, dir->pair, &pdir); - if (err && err != LFS_ERR_NOENT) { - *dir = olddir; - return err; - } - - if (err != LFS_ERR_NOENT && pdir.split) { - err = lfs_dir_drop(lfs, &pdir, dir); - if (err) { - *dir = olddir; - return err; - } - } - } - - if (dir->erased || dir->count >= 0xff) { - // try to commit - struct lfs_commit commit = { - .block = dir->pair[0], - .off = dir->off, - .ptag = dir->etag, - .crc = 0xffffffff, - - .begin = dir->off, - .end = lfs->cfg->block_size - 8, - }; - - // traverse attrs that need to be written out - lfs_pair_tole32(dir->tail); - int err = lfs_dir_traverse(lfs, - dir, dir->off, dir->etag, attrs, attrcount, - 0, 0, 0, 0, 0, - lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ - lfs, &commit}); - lfs_pair_fromle32(dir->tail); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - - // commit any global diffs if we have any - lfs_gstate_t delta = {0}; - lfs_gstate_xor(&delta, &lfs->gstate); - lfs_gstate_xor(&delta, &lfs->gdisk); - lfs_gstate_xor(&delta, &lfs->gdelta); - delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); - if (!lfs_gstate_iszero(&delta)) { - err = lfs_dir_getgstate(lfs, dir, &delta); - if (err) { - *dir = olddir; - return err; - } - - lfs_gstate_tole32(&delta); - err = lfs_dir_commitattr(lfs, &commit, - LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, - sizeof(delta)), &delta); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - } - - // finalize commit with the crc - err = lfs_dir_commitcrc(lfs, &commit); - if (err) { - if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { - goto compact; - } - *dir = olddir; - return err; - } - - // successful commit, update dir - LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); - dir->off = commit.off; - dir->etag = commit.ptag; - // and update gstate - lfs->gdisk = lfs->gstate; - lfs->gdelta = (lfs_gstate_t){0}; - } else { -compact: - // fall back to compaction - lfs_cache_drop(lfs, &lfs->pcache); - - int err = lfs_dir_compact(lfs, dir, attrs, attrcount, - dir, 0, dir->count); - if (err) { - *dir = olddir; - return err; - } - } - - // this complicated bit of logic is for fixing up any active - // metadata-pairs that we may have affected - // - // note we have to make two passes since the mdir passed to - // lfs_dir_commit could also be in this list, and even then - // we need to copy the pair so they don't get clobbered if we refetch - // our mdir. - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (&d->m != dir && lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { - d->m = *dir; - for (int i = 0; i < attrcount; i++) { - if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && - d->id == lfs_tag_id(attrs[i].tag)) { - d->m.pair[0] = LFS_BLOCK_NULL; - d->m.pair[1] = LFS_BLOCK_NULL; - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && - d->id > lfs_tag_id(attrs[i].tag)) { - d->id -= 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos -= 1; - } - } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE && - d->id >= lfs_tag_id(attrs[i].tag)) { - d->id += 1; - if (d->type == LFS_TYPE_DIR) { - ((lfs_dir_t*)d)->pos += 1; - } - } - } - } - } - - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(d->m.pair, olddir.pair) == 0) { - while (d->id >= d->m.count && d->m.split) { - // we split and id is on tail now - d->id -= d->m.count; - int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); - if (err) { - return err; - } - } - } - } - - return 0; -} - - -/// Top level directory operations /// -int lfs_mkdir(lfs_t *lfs, const char *path) { - LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path); - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - struct lfs_mlist cwd; - cwd.next = lfs->mlist; - uint16_t id; - err = lfs_dir_find(lfs, &cwd.m, &path, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - LFS_TRACE("lfs_mkdir -> %d", (err < 0) ? err : LFS_ERR_EXIST); - return (err < 0) ? err : LFS_ERR_EXIST; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - LFS_TRACE("lfs_mkdir -> %d", LFS_ERR_NAMETOOLONG); - return LFS_ERR_NAMETOOLONG; - } - - // build up new directory - lfs_alloc_ack(lfs); - lfs_mdir_t dir; - err = lfs_dir_alloc(lfs, &dir); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - // find end of list - lfs_mdir_t pred = cwd.m; - while (pred.split) { - err = lfs_dir_fetch(lfs, &pred, pred.tail); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - } - - // setup dir - lfs_pair_tole32(pred.tail); - err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); - lfs_pair_fromle32(pred.tail); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - // current block end of list? - if (cwd.m.split) { - // update tails, this creates a desync - lfs_fs_preporphans(lfs, +1); - - // it's possible our predecessor has to be relocated, and if - // our parent is our predecessor's predecessor, this could have - // caused our parent to go out of date, fortunately we can hook - // ourselves into littlefs to catch this - cwd.type = 0; - cwd.id = 0; - lfs->mlist = &cwd; - - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - lfs->mlist = cwd.next; - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - lfs->mlist = cwd.next; - lfs_fs_preporphans(lfs, -1); - } - - // now insert into our parent block - lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, - {LFS_MKTAG_IF(!cwd.m.split, - LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); - lfs_pair_fromle32(dir.pair); - if (err) { - LFS_TRACE("lfs_mkdir -> %d", err); - return err; - } - - LFS_TRACE("lfs_mkdir -> %d", 0); - return 0; -} - -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { - LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path); - lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_dir_open -> %"PRId32, tag); - return tag; - } - - if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { - LFS_TRACE("lfs_dir_open -> %d", LFS_ERR_NOTDIR); - return LFS_ERR_NOTDIR; - } - - lfs_block_t pair[2]; - if (lfs_tag_id(tag) == 0x3ff) { - // handle root dir separately - pair[0] = lfs->root[0]; - pair[1] = lfs->root[1]; - } else { - // get dir pair from parent - lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - LFS_TRACE("lfs_dir_open -> %"PRId32, res); - return res; - } - lfs_pair_fromle32(pair); - } - - // fetch first pair - int err = lfs_dir_fetch(lfs, &dir->m, pair); - if (err) { - LFS_TRACE("lfs_dir_open -> %d", err); - return err; - } - - // setup entry - dir->head[0] = dir->m.pair[0]; - dir->head[1] = dir->m.pair[1]; - dir->id = 0; - dir->pos = 0; - - // add to list of mdirs - dir->type = LFS_TYPE_DIR; - dir->next = (lfs_dir_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)dir; - - LFS_TRACE("lfs_dir_open -> %d", 0); - return 0; -} - -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir); - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)dir) { - *p = (*p)->next; - break; - } - } - - LFS_TRACE("lfs_dir_close -> %d", 0); - return 0; -} - -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { - LFS_TRACE("lfs_dir_read(%p, %p, %p)", - (void*)lfs, (void*)dir, (void*)info); - memset(info, 0, sizeof(*info)); - - // special offset for '.' and '..' - if (dir->pos == 0) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, "."); - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; - } else if (dir->pos == 1) { - info->type = LFS_TYPE_DIR; - strcpy(info->name, ".."); - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; - } - - while (true) { - if (dir->id == dir->m.count) { - if (!dir->m.split) { - LFS_TRACE("lfs_dir_read -> %d", false); - return false; - } - - int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - LFS_TRACE("lfs_dir_read -> %d", err); - return err; - } - - dir->id = 0; - } - - int err = lfs_dir_getinfo(lfs, &dir->m, dir->id, info); - if (err && err != LFS_ERR_NOENT) { - LFS_TRACE("lfs_dir_read -> %d", err); - return err; - } - - dir->id += 1; - if (err != LFS_ERR_NOENT) { - break; - } - } - - dir->pos += 1; - LFS_TRACE("lfs_dir_read -> %d", true); - return true; -} - -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { - LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")", - (void*)lfs, (void*)dir, off); - // simply walk from head dir - int err = lfs_dir_rewind(lfs, dir); - if (err) { - LFS_TRACE("lfs_dir_seek -> %d", err); - return err; - } - - // first two for ./.. - dir->pos = lfs_min(2, off); - off -= dir->pos; - - // skip superblock entry - dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0); - - while (off > 0) { - int diff = lfs_min(dir->m.count - dir->id, off); - dir->id += diff; - dir->pos += diff; - off -= diff; - - if (dir->id == dir->m.count) { - if (!dir->m.split) { - LFS_TRACE("lfs_dir_seek -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); - if (err) { - LFS_TRACE("lfs_dir_seek -> %d", err); - return err; - } - - dir->id = 0; - } - } - - LFS_TRACE("lfs_dir_seek -> %d", 0); - return 0; -} - -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir); - (void)lfs; - LFS_TRACE("lfs_dir_tell -> %"PRId32, dir->pos); - return dir->pos; -} - -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { - LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir); - // reload the head dir - int err = lfs_dir_fetch(lfs, &dir->m, dir->head); - if (err) { - LFS_TRACE("lfs_dir_rewind -> %d", err); - return err; - } - - dir->id = 0; - dir->pos = 0; - LFS_TRACE("lfs_dir_rewind -> %d", 0); - return 0; -} - - -/// File index list operations /// -static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) { - lfs_off_t size = *off; - lfs_off_t b = lfs->cfg->block_size - 2*4; - lfs_off_t i = size / b; - if (i == 0) { - return 0; - } - - i = (size - 4*(lfs_popc(i-1)+2)) / b; - *off = size - b*i - 4*lfs_popc(i); - return i; -} - -static int lfs_ctz_find(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) { - if (size == 0) { - *block = LFS_BLOCK_NULL; - *off = 0; - return 0; - } - - lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - lfs_off_t target = lfs_ctz_index(lfs, &pos); - - while (current > target) { - lfs_size_t skip = lfs_min( - lfs_npw2(current-target+1) - 1, - lfs_ctz(current)); - - int err = lfs_bd_read(lfs, - pcache, rcache, sizeof(head), - head, 4*skip, &head, sizeof(head)); - head = lfs_fromle32(head); - if (err) { - return err; - } - - current -= 1 << skip; - } - - *block = head; - *off = pos; - return 0; -} - -static int lfs_ctz_extend(lfs_t *lfs, - lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - lfs_block_t *block, lfs_off_t *off) { - while (true) { - // go ahead and grab a block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - - { - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (size == 0) { - *block = nblock; - *off = 0; - return 0; - } - - lfs_size_t noff = size - 1; - lfs_off_t index = lfs_ctz_index(lfs, &noff); - noff = noff + 1; - - // just copy out the last block if it is incomplete - if (noff != lfs->cfg->block_size) { - for (lfs_off_t i = 0; i < noff; i++) { - uint8_t data; - err = lfs_bd_read(lfs, - NULL, rcache, noff-i, - head, i, &data, 1); - if (err) { - return err; - } - - err = lfs_bd_prog(lfs, - pcache, rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - *block = nblock; - *off = noff; - return 0; - } - - // append block - index += 1; - lfs_size_t skips = lfs_ctz(index) + 1; - lfs_block_t nhead = head; - for (lfs_off_t i = 0; i < skips; i++) { - nhead = lfs_tole32(nhead); - err = lfs_bd_prog(lfs, pcache, rcache, true, - nblock, 4*i, &nhead, 4); - nhead = lfs_fromle32(nhead); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - if (i != skips-1) { - err = lfs_bd_read(lfs, - NULL, rcache, sizeof(nhead), - nhead, 4*i, &nhead, sizeof(nhead)); - nhead = lfs_fromle32(nhead); - if (err) { - return err; - } - } - } - - *block = nblock; - *off = 4*skips; - return 0; - } - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, pcache); - } -} - -static int lfs_ctz_traverse(lfs_t *lfs, - const lfs_cache_t *pcache, lfs_cache_t *rcache, - lfs_block_t head, lfs_size_t size, - int (*cb)(void*, lfs_block_t), void *data) { - if (size == 0) { - return 0; - } - - lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); - - while (true) { - int err = cb(data, head); - if (err) { - return err; - } - - if (index == 0) { - return 0; - } - - lfs_block_t heads[2]; - int count = 2 - (index & 1); - err = lfs_bd_read(lfs, - pcache, rcache, count*sizeof(head), - head, 0, &heads, count*sizeof(head)); - heads[0] = lfs_fromle32(heads[0]); - heads[1] = lfs_fromle32(heads[1]); - if (err) { - return err; - } - - for (int i = 0; i < count-1; i++) { - err = cb(data, heads[i]); - if (err) { - return err; - } - } - - head = heads[count-1]; - index -= count; - } -} - - -/// Top level file operations /// -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags, - const struct lfs_file_config *cfg) { - LFS_TRACE("lfs_file_opencfg(%p, %p, \"%s\", %x, %p {" - ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", - (void*)lfs, (void*)file, path, flags, - (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); - - // deorphan if we haven't yet, needed at most once after poweron - if ((flags & 3) != LFS_O_RDONLY) { - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_file_opencfg -> %d", err); - return err; - } - } - - // setup simple file details - int err; - file->cfg = cfg; - file->flags = flags | LFS_F_OPENED; - file->pos = 0; - file->off = 0; - file->cache.buffer = NULL; - - // allocate entry for file if it doesn't exist - lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { - err = tag; - goto cleanup; - } - - // get id, add to list of mdirs to catch update changes - file->type = LFS_TYPE_REG; - file->next = (lfs_file_t*)lfs->mlist; - lfs->mlist = (struct lfs_mlist*)file; - - if (tag == LFS_ERR_NOENT) { - if (!(flags & LFS_O_CREAT)) { - err = LFS_ERR_NOENT; - goto cleanup; - } - - // check that name fits - lfs_size_t nlen = strlen(path); - if (nlen > lfs->name_max) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - // get next slot and create entry to remember name - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL})); - if (err) { - err = LFS_ERR_NAMETOOLONG; - goto cleanup; - } - - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0); - } else if (flags & LFS_O_EXCL) { - err = LFS_ERR_EXIST; - goto cleanup; - } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) { - err = LFS_ERR_ISDIR; - goto cleanup; - } else if (flags & LFS_O_TRUNC) { - // truncate if requested - tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0); - file->flags |= LFS_F_DIRTY; - } else { - // try to load what's on disk, if it's inlined we'll fix it later - tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_ctz_fromle32(&file->ctz); - } - - // fetch attrs - for (unsigned i = 0; i < file->cfg->attr_count; i++) { - if ((file->flags & 3) != LFS_O_WRONLY) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type, - file->id, file->cfg->attrs[i].size), - file->cfg->attrs[i].buffer); - if (res < 0 && res != LFS_ERR_NOENT) { - err = res; - goto cleanup; - } - } - - if ((file->flags & 3) != LFS_O_RDONLY) { - if (file->cfg->attrs[i].size > lfs->attr_max) { - err = LFS_ERR_NOSPC; - goto cleanup; - } - - file->flags |= LFS_F_DIRTY; - } - } - - // allocate buffer if needed - if (file->cfg->buffer) { - file->cache.buffer = file->cfg->buffer; - } else { - file->cache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!file->cache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leak - lfs_cache_zero(lfs, &file->cache); - - if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { - // load inline files - file->ctz.head = LFS_BLOCK_INLINE; - file->ctz.size = lfs_tag_size(tag); - file->flags |= LFS_F_INLINE; - file->cache.block = file->ctz.head; - file->cache.off = 0; - file->cache.size = lfs->cfg->cache_size; - - // don't always read (may be new/trunc file) - if (file->ctz.size > 0) { - lfs_stag_t res = lfs_dir_get(lfs, &file->m, - LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, file->id, - lfs_min(file->cache.size, 0x3fe)), - file->cache.buffer); - if (res < 0) { - err = res; - goto cleanup; - } - } - } - - LFS_TRACE("lfs_file_opencfg -> %d", 0); - return 0; - -cleanup: - // clean up lingering resources - file->flags |= LFS_F_ERRED; - lfs_file_close(lfs, file); - LFS_TRACE("lfs_file_opencfg -> %d", err); - return err; -} - -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags) { - LFS_TRACE("lfs_file_open(%p, %p, \"%s\", %x)", - (void*)lfs, (void*)file, path, flags); - static const struct lfs_file_config defaults = {0}; - int err = lfs_file_opencfg(lfs, file, path, flags, &defaults); - LFS_TRACE("lfs_file_open -> %d", err); - return err; -} - -int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - int err = lfs_file_sync(lfs, file); - - // remove from list of mdirs - for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs_mlist*)file) { - *p = (*p)->next; - break; - } - } - - // clean up memory - if (!file->cfg->buffer) { - lfs_free(file->cache.buffer); - } - - file->flags &= ~LFS_F_OPENED; - LFS_TRACE("lfs_file_close -> %d", err); - return err; -} - -static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { - LFS_ASSERT(file->flags & LFS_F_OPENED); - - while (true) { - // just relocate what exists into new block - lfs_block_t nblock; - int err = lfs_alloc(lfs, &nblock); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, nblock); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - // either read from dirty cache or disk - for (lfs_off_t i = 0; i < file->off; i++) { - uint8_t data; - if (file->flags & LFS_F_INLINE) { - err = lfs_dir_getread(lfs, &file->m, - // note we evict inline files before they can be dirty - NULL, &file->cache, file->off-i, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - i, &data, 1); - if (err) { - return err; - } - } else { - err = lfs_bd_read(lfs, - &file->cache, &lfs->rcache, file->off-i, - file->block, i, &data, 1); - if (err) { - return err; - } - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - nblock, i, &data, 1); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - } - - // copy over new state of file - memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->cache_size); - file->cache.block = lfs->pcache.block; - file->cache.off = lfs->pcache.off; - file->cache.size = lfs->pcache.size; - lfs_cache_zero(lfs, &lfs->pcache); - - file->block = nblock; - file->flags |= LFS_F_WRITING; - return 0; - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); - - // just clear cache and try a new block - lfs_cache_drop(lfs, &lfs->pcache); - } -} - -static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) { - file->off = file->pos; - lfs_alloc_ack(lfs); - int err = lfs_file_relocate(lfs, file); - if (err) { - return err; - } - - file->flags &= ~LFS_F_INLINE; - return 0; -} - -static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { - LFS_ASSERT(file->flags & LFS_F_OPENED); - - if (file->flags & LFS_F_READING) { - if (!(file->flags & LFS_F_INLINE)) { - lfs_cache_drop(lfs, &file->cache); - } - file->flags &= ~LFS_F_READING; - } - - if (file->flags & LFS_F_WRITING) { - lfs_off_t pos = file->pos; - - if (!(file->flags & LFS_F_INLINE)) { - // copy over anything after current branch - lfs_file_t orig = { - .ctz.head = file->ctz.head, - .ctz.size = file->ctz.size, - .flags = LFS_O_RDONLY | LFS_F_OPENED, - .pos = file->pos, - .cache = lfs->rcache, - }; - lfs_cache_drop(lfs, &lfs->rcache); - - while (file->pos < file->ctz.size) { - // copy over a byte at a time, leave it up to caching - // to make this efficient - uint8_t data; - lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1); - if (res < 0) { - return res; - } - - res = lfs_file_write(lfs, file, &data, 1); - if (res < 0) { - return res; - } - - // keep our reference to the rcache in sync - if (lfs->rcache.block != LFS_BLOCK_NULL) { - lfs_cache_drop(lfs, &orig.cache); - lfs_cache_drop(lfs, &lfs->rcache); - } - } - - // write out what we have - while (true) { - int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - return err; - } - - break; - -relocate: - LFS_DEBUG("Bad block at 0x%"PRIx32, file->block); - err = lfs_file_relocate(lfs, file); - if (err) { - return err; - } - } - } else { - file->pos = lfs_max(file->pos, file->ctz.size); - } - - // actual file updates - file->ctz.head = file->block; - file->ctz.size = file->pos; - file->flags &= ~LFS_F_WRITING; - file->flags |= LFS_F_DIRTY; - - file->pos = pos; - } - - return 0; -} - -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - if (file->flags & LFS_F_ERRED) { - // it's not safe to do anything if our file errored - LFS_TRACE("lfs_file_sync -> %d", 0); - return 0; - } - - int err = lfs_file_flush(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_sync -> %d", err); - return err; - } - - if ((file->flags & LFS_F_DIRTY) && - !lfs_pair_isnull(file->m.pair)) { - // update dir entry - uint16_t type; - const void *buffer; - lfs_size_t size; - struct lfs_ctz ctz; - if (file->flags & LFS_F_INLINE) { - // inline the whole file - type = LFS_TYPE_INLINESTRUCT; - buffer = file->cache.buffer; - size = file->ctz.size; - } else { - // update the ctz reference - type = LFS_TYPE_CTZSTRUCT; - // copy ctz so alloc will work during a relocate - ctz = file->ctz; - lfs_ctz_tole32(&ctz); - buffer = &ctz; - size = sizeof(ctz); - } - - // commit file data and attributes - err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( - {LFS_MKTAG(type, file->id, size), buffer}, - {LFS_MKTAG(LFS_FROM_USERATTRS, file->id, - file->cfg->attr_count), file->cfg->attrs})); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_sync -> %d", err); - return err; - } - - file->flags &= ~LFS_F_DIRTY; - } - - LFS_TRACE("lfs_file_sync -> %d", 0); - return 0; -} - -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")", - (void*)lfs, (void*)file, buffer, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_WRONLY); - - uint8_t *data = buffer; - lfs_size_t nsize = size; - - if (file->flags & LFS_F_WRITING) { - // flush out any writes - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } - - if (file->pos >= file->ctz.size) { - // eof if past end - LFS_TRACE("lfs_file_read -> %d", 0); - return 0; - } - - size = lfs_min(size, file->ctz.size - file->pos); - nsize = size; - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_READING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos, &file->block, &file->off); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } else { - file->block = LFS_BLOCK_INLINE; - file->off = file->pos; - } - - file->flags |= LFS_F_READING; - } - - // read as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - if (file->flags & LFS_F_INLINE) { - int err = lfs_dir_getread(lfs, &file->m, - NULL, &file->cache, lfs->cfg->block_size, - LFS_MKTAG(0xfff, 0x1ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), - file->off, data, diff); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } else { - int err = lfs_bd_read(lfs, - NULL, &file->cache, lfs->cfg->block_size, - file->block, file->off, data, diff); - if (err) { - LFS_TRACE("lfs_file_read -> %d", err); - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - } - - LFS_TRACE("lfs_file_read -> %"PRId32, size); - return size; -} - -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")", - (void*)lfs, (void*)file, buffer, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); - - const uint8_t *data = buffer; - lfs_size_t nsize = size; - - if (file->flags & LFS_F_READING) { - // drop any reads - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - if ((file->flags & LFS_O_APPEND) && file->pos < file->ctz.size) { - file->pos = file->ctz.size; - } - - if (file->pos + size > lfs->file_max) { - // Larger than file limit? - LFS_TRACE("lfs_file_write -> %d", LFS_ERR_FBIG); - return LFS_ERR_FBIG; - } - - if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) { - // fill with zeros - lfs_off_t pos = file->pos; - file->pos = file->ctz.size; - - while (file->pos < pos) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - LFS_TRACE("lfs_file_write -> %"PRId32, res); - return res; - } - } - } - - if ((file->flags & LFS_F_INLINE) && - lfs_max(file->pos+nsize, file->ctz.size) > - lfs_min(0x3fe, lfs_min( - lfs->cfg->cache_size, lfs->cfg->block_size/8))) { - // inline file doesn't fit anymore - int err = lfs_file_outline(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - while (nsize > 0) { - // check if we need a new block - if (!(file->flags & LFS_F_WRITING) || - file->off == lfs->cfg->block_size) { - if (!(file->flags & LFS_F_INLINE)) { - if (!(file->flags & LFS_F_WRITING) && file->pos > 0) { - // find out which block we're extending from - int err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - file->pos-1, &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - - // mark cache as dirty since we may have read data into it - lfs_cache_zero(lfs, &file->cache); - } - - // extend file with new blocks - lfs_alloc_ack(lfs); - int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache, - file->block, file->pos, - &file->block, &file->off); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } else { - file->block = LFS_BLOCK_INLINE; - file->off = file->pos; - } - - file->flags |= LFS_F_WRITING; - } - - // program as much as we can in current block - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); - while (true) { - int err = lfs_bd_prog(lfs, &file->cache, &lfs->rcache, true, - file->block, file->off, data, diff); - if (err) { - if (err == LFS_ERR_CORRUPT) { - goto relocate; - } - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - - break; -relocate: - err = lfs_file_relocate(lfs, file); - if (err) { - file->flags |= LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %d", err); - return err; - } - } - - file->pos += diff; - file->off += diff; - data += diff; - nsize -= diff; - - lfs_alloc_ack(lfs); - } - - file->flags &= ~LFS_F_ERRED; - LFS_TRACE("lfs_file_write -> %"PRId32, size); - return size; -} - -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence) { - LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)", - (void*)lfs, (void*)file, off, whence); - LFS_ASSERT(file->flags & LFS_F_OPENED); - - // write out everything beforehand, may be noop if rdonly - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_seek -> %d", err); - return err; - } - - // find new pos - lfs_off_t npos = file->pos; - if (whence == LFS_SEEK_SET) { - npos = off; - } else if (whence == LFS_SEEK_CUR) { - npos = file->pos + off; - } else if (whence == LFS_SEEK_END) { - npos = file->ctz.size + off; - } - - if (npos > lfs->file_max) { - // file position out of range - LFS_TRACE("lfs_file_seek -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - // update pos - file->pos = npos; - LFS_TRACE("lfs_file_seek -> %"PRId32, npos); - return npos; -} - -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { - LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")", - (void*)lfs, (void*)file, size); - LFS_ASSERT(file->flags & LFS_F_OPENED); - LFS_ASSERT((file->flags & 3) != LFS_O_RDONLY); - - if (size > LFS_FILE_MAX) { - LFS_TRACE("lfs_file_truncate -> %d", LFS_ERR_INVAL); - return LFS_ERR_INVAL; - } - - lfs_off_t pos = file->pos; - lfs_off_t oldsize = lfs_file_size(lfs, file); - if (size < oldsize) { - // need to flush since directly changing metadata - int err = lfs_file_flush(lfs, file); - if (err) { - LFS_TRACE("lfs_file_truncate -> %d", err); - return err; - } - - // lookup new head in ctz skip list - err = lfs_ctz_find(lfs, NULL, &file->cache, - file->ctz.head, file->ctz.size, - size, &file->block, &file->off); - if (err) { - LFS_TRACE("lfs_file_truncate -> %d", err); - return err; - } - - file->ctz.head = file->block; - file->ctz.size = size; - file->flags |= LFS_F_DIRTY | LFS_F_READING; - } else if (size > oldsize) { - // flush+seek if not already at end - if (file->pos != oldsize) { - lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - } - - // fill with zeros - while (file->pos < size) { - lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - } - } - - // restore pos - lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET); - if (res < 0) { - LFS_TRACE("lfs_file_truncate -> %"PRId32, res); - return (int)res; - } - - LFS_TRACE("lfs_file_truncate -> %d", 0); - return 0; -} - -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - (void)lfs; - LFS_TRACE("lfs_file_tell -> %"PRId32, file->pos); - return file->pos; -} - -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file); - lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET); - if (res < 0) { - LFS_TRACE("lfs_file_rewind -> %"PRId32, res); - return (int)res; - } - - LFS_TRACE("lfs_file_rewind -> %d", 0); - return 0; -} - -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { - LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); - LFS_ASSERT(file->flags & LFS_F_OPENED); - (void)lfs; - if (file->flags & LFS_F_WRITING) { - LFS_TRACE("lfs_file_size -> %"PRId32, - lfs_max(file->pos, file->ctz.size)); - return lfs_max(file->pos, file->ctz.size); - } else { - LFS_TRACE("lfs_file_size -> %"PRId32, file->ctz.size); - return file->ctz.size; - } -} - - -/// General fs operations /// -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { - LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info); - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_stat -> %"PRId32, tag); - return (int)tag; - } - - int err = lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info); - LFS_TRACE("lfs_stat -> %d", err); - return err; -} - -int lfs_remove(lfs_t *lfs, const char *path) { - LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path); - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0 || lfs_tag_id(tag) == 0x3ff) { - LFS_TRACE("lfs_remove -> %"PRId32, (tag < 0) ? tag : LFS_ERR_INVAL); - return (tag < 0) ? (int)tag : LFS_ERR_INVAL; - } - - struct lfs_mlist dir; - dir.next = lfs->mlist; - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); - if (res < 0) { - LFS_TRACE("lfs_remove -> %"PRId32, res); - return (int)res; - } - lfs_pair_fromle32(pair); - - err = lfs_dir_fetch(lfs, &dir.m, pair); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - if (dir.m.count > 0 || dir.m.split) { - LFS_TRACE("lfs_remove -> %d", LFS_ERR_NOTEMPTY); - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - - // I know it's crazy but yes, dir can be changed by our parent's - // commit (if predecessor is child) - dir.type = 0; - dir.id = 0; - lfs->mlist = &dir; - } - - // delete the entry - err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL})); - if (err) { - lfs->mlist = dir.next; - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - lfs->mlist = dir.next; - if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, dir.m.pair, &cwd); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - - err = lfs_dir_drop(lfs, &cwd, &dir.m); - if (err) { - LFS_TRACE("lfs_remove -> %d", err); - return err; - } - } - - LFS_TRACE("lfs_remove -> %d", 0); - return 0; -} - -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { - LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath); - - // deorphan if we haven't yet, needed at most once after poweron - int err = lfs_fs_forceconsistency(lfs); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - // find old entry - lfs_mdir_t oldcwd; - lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL); - if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) { - LFS_TRACE("lfs_rename -> %"PRId32, - (oldtag < 0) ? oldtag : LFS_ERR_INVAL); - return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL; - } - - // find new entry - lfs_mdir_t newcwd; - uint16_t newid; - lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); - if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { - LFS_TRACE("lfs_rename -> %"PRId32, - (prevtag < 0) ? prevtag : LFS_ERR_INVAL); - return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL; - } - - // if we're in the same pair there's a few special cases... - bool samepair = (lfs_pair_cmp(oldcwd.pair, newcwd.pair) == 0); - uint16_t newoldid = lfs_tag_id(oldtag); - - struct lfs_mlist prevdir; - prevdir.next = lfs->mlist; - if (prevtag == LFS_ERR_NOENT) { - // check that name fits - lfs_size_t nlen = strlen(newpath); - if (nlen > lfs->name_max) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_NAMETOOLONG); - return LFS_ERR_NAMETOOLONG; - } - - // there is a small chance we are being renamed in the same - // directory/ to an id less than our old id, the global update - // to handle this is a bit messy - if (samepair && newid <= newoldid) { - newoldid += 1; - } - } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_ISDIR); - return LFS_ERR_ISDIR; - } else if (samepair && newid == newoldid) { - // we're renaming to ourselves?? - LFS_TRACE("lfs_rename -> %d", 0); - return 0; - } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // must be empty before removal - lfs_block_t prevpair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); - if (res < 0) { - LFS_TRACE("lfs_rename -> %"PRId32, res); - return (int)res; - } - lfs_pair_fromle32(prevpair); - - // must be empty before removal - err = lfs_dir_fetch(lfs, &prevdir.m, prevpair); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - if (prevdir.m.count > 0 || prevdir.m.split) { - LFS_TRACE("lfs_rename -> %d", LFS_ERR_NOTEMPTY); - return LFS_ERR_NOTEMPTY; - } - - // mark fs as orphaned - lfs_fs_preporphans(lfs, +1); - - // I know it's crazy but yes, dir can be changed by our parent's - // commit (if predecessor is child) - prevdir.type = 0; - prevdir.id = 0; - lfs->mlist = &prevdir; - } - - if (!samepair) { - lfs_fs_prepmove(lfs, newoldid, oldcwd.pair); - } - - // move over all attributes - err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( - {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, - LFS_TYPE_DELETE, newid, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, - {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath}, - {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd}, - {LFS_MKTAG_IF(samepair, - LFS_TYPE_DELETE, newoldid, 0), NULL})); - if (err) { - lfs->mlist = prevdir.next; - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - // let commit clean up after move (if we're different! otherwise move - // logic already fixed it for us) - if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) { - // prep gstate and delete move id - lfs_fs_prepmove(lfs, 0x3ff, NULL); - err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0), NULL})); - if (err) { - lfs->mlist = prevdir.next; - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - } - - lfs->mlist = prevdir.next; - if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { - // fix orphan - lfs_fs_preporphans(lfs, -1); - - err = lfs_fs_pred(lfs, prevdir.m.pair, &newcwd); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - - err = lfs_dir_drop(lfs, &newcwd, &prevdir.m); - if (err) { - LFS_TRACE("lfs_rename -> %d", err); - return err; - } - } - - LFS_TRACE("lfs_rename -> %d", 0); - return 0; -} - -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs, path, type, buffer, size); - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - LFS_TRACE("lfs_getattr -> %"PRId32, tag); - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - LFS_TRACE("lfs_getattr -> %d", err); - return err; - } - } - - tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + type, - id, lfs_min(size, lfs->attr_max)), - buffer); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - LFS_TRACE("lfs_getattr -> %d", LFS_ERR_NOATTR); - return LFS_ERR_NOATTR; - } - - LFS_TRACE("lfs_getattr -> %"PRId32, tag); - return tag; - } - - size = lfs_tag_size(tag); - LFS_TRACE("lfs_getattr -> %"PRId32, size); - return size; -} - -static int lfs_commitattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - lfs_mdir_t cwd; - lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); - if (tag < 0) { - return tag; - } - - uint16_t id = lfs_tag_id(tag); - if (id == 0x3ff) { - // special case for root - id = 0; - int err = lfs_dir_fetch(lfs, &cwd, lfs->root); - if (err) { - return err; - } - } - - return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer})); -} - -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size) { - LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs, path, type, buffer, size); - if (size > lfs->attr_max) { - LFS_TRACE("lfs_setattr -> %d", LFS_ERR_NOSPC); - return LFS_ERR_NOSPC; - } - - int err = lfs_commitattr(lfs, path, type, buffer, size); - LFS_TRACE("lfs_setattr -> %d", err); - return err; -} - -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { - LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type); - int err = lfs_commitattr(lfs, path, type, NULL, 0x3ff); - LFS_TRACE("lfs_removeattr -> %d", err); - return err; -} - - -/// Filesystem operations /// -static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { - lfs->cfg = cfg; - int err = 0; - - // validate that the lfs-cfg sizes were initiated properly before - // performing any arithmetic logics with them - LFS_ASSERT(lfs->cfg->read_size != 0); - LFS_ASSERT(lfs->cfg->prog_size != 0); - LFS_ASSERT(lfs->cfg->cache_size != 0); - - // check that block size is a multiple of cache size is a multiple - // of prog and read sizes - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0); - LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0); - LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0); - - // check that the block size is large enough to fit ctz pointers - LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) - <= lfs->cfg->block_size); - - // block_cycles = 0 is no longer supported. - // - // block_cycles is the number of erase cycles before littlefs evicts - // metadata logs as a part of wear leveling. Suggested values are in the - // range of 100-1000, or set block_cycles to -1 to disable block-level - // wear-leveling. - LFS_ASSERT(lfs->cfg->block_cycles != 0); - - - // setup read cache - if (lfs->cfg->read_buffer) { - lfs->rcache.buffer = lfs->cfg->read_buffer; - } else { - lfs->rcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->rcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // setup program cache - if (lfs->cfg->prog_buffer) { - lfs->pcache.buffer = lfs->cfg->prog_buffer; - } else { - lfs->pcache.buffer = lfs_malloc(lfs->cfg->cache_size); - if (!lfs->pcache.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // zero to avoid information leaks - lfs_cache_zero(lfs, &lfs->rcache); - lfs_cache_zero(lfs, &lfs->pcache); - - // setup lookahead, must be multiple of 64-bits, 32-bit aligned - LFS_ASSERT(lfs->cfg->lookahead_size > 0); - LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0); - if (lfs->cfg->lookahead_buffer) { - lfs->free.buffer = lfs->cfg->lookahead_buffer; - } else { - lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size); - if (!lfs->free.buffer) { - err = LFS_ERR_NOMEM; - goto cleanup; - } - } - - // check that the size limits are sane - LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX); - lfs->name_max = lfs->cfg->name_max; - if (!lfs->name_max) { - lfs->name_max = LFS_NAME_MAX; - } - - LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX); - lfs->file_max = lfs->cfg->file_max; - if (!lfs->file_max) { - lfs->file_max = LFS_FILE_MAX; - } - - LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX); - lfs->attr_max = lfs->cfg->attr_max; - if (!lfs->attr_max) { - lfs->attr_max = LFS_ATTR_MAX; - } - - // setup default state - lfs->root[0] = LFS_BLOCK_NULL; - lfs->root[1] = LFS_BLOCK_NULL; - lfs->mlist = NULL; - lfs->seed = 0; - lfs->gdisk = (lfs_gstate_t){0}; - lfs->gstate = (lfs_gstate_t){0}; - lfs->gdelta = (lfs_gstate_t){0}; -#ifdef LFS_MIGRATE - lfs->lfs1 = NULL; -#endif - - return 0; - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs_deinit(lfs_t *lfs) { - // free allocated memory - if (!lfs->cfg->read_buffer) { - lfs_free(lfs->rcache.buffer); - } - - if (!lfs->cfg->prog_buffer) { - lfs_free(lfs->pcache.buffer); - } - - if (!lfs->cfg->lookahead_buffer) { - lfs_free(lfs->free.buffer); - } - - return 0; -} - -int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_format(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - LFS_TRACE("lfs_format -> %d", err); - return err; - } - - // create free lookahead - memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); - lfs->free.off = 0; - lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, - lfs->cfg->block_count); - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // create root dir - lfs_mdir_t root; - err = lfs_dir_alloc(lfs, &root); - if (err) { - goto cleanup; - } - - // write one superblock - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - // force compaction to prevent accidentally mounting any - // older version of littlefs that may live on disk - root.erased = false; - err = lfs_dir_commit(lfs, &root, NULL, 0); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs_deinit(lfs); - LFS_TRACE("lfs_format -> %d", err); - return err; -} - -int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_mount(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - int err = lfs_init(lfs, cfg); - if (err) { - LFS_TRACE("lfs_mount -> %d", err); - return err; - } - - // scan directory blocks for superblock and any global updates - lfs_mdir_t dir = {.tail = {0, 1}}; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(dir.tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - err = LFS_ERR_CORRUPT; - goto cleanup; - } - cycle += 1; - - // fetch next block in tail list - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, - LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), - NULL, - lfs_dir_find_match, &(struct lfs_dir_find_match){ - lfs, "littlefs", 8}); - if (tag < 0) { - err = tag; - goto cleanup; - } - - // has superblock? - if (tag && !lfs_tag_isdelete(tag)) { - // update root - lfs->root[0] = dir.pair[0]; - lfs->root[1] = dir.pair[1]; - - // grab superblock - lfs_superblock_t superblock; - tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - err = tag; - goto cleanup; - } - lfs_superblock_fromle32(&superblock); - - // check version - uint16_t major_version = (0xffff & (superblock.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.version >> 0)); - if ((major_version != LFS_DISK_VERSION_MAJOR || - minor_version > LFS_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version v%"PRIu16".%"PRIu16, - major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - // check superblock configuration - if (superblock.name_max) { - if (superblock.name_max > lfs->name_max) { - LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", - superblock.name_max, lfs->name_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->name_max = superblock.name_max; - } - - if (superblock.file_max) { - if (superblock.file_max > lfs->file_max) { - LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", - superblock.file_max, lfs->file_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->file_max = superblock.file_max; - } - - if (superblock.attr_max) { - if (superblock.attr_max > lfs->attr_max) { - LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", - superblock.attr_max, lfs->attr_max); - err = LFS_ERR_INVAL; - goto cleanup; - } - - lfs->attr_max = superblock.attr_max; - } - } - - // has gstate? - err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate); - if (err) { - goto cleanup; - } - } - - // found superblock? - if (lfs_pair_isnull(lfs->root)) { - err = LFS_ERR_INVAL; - goto cleanup; - } - - // update littlefs with gstate - if (!lfs_gstate_iszero(&lfs->gstate)) { - LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, - lfs->gstate.tag, - lfs->gstate.pair[0], - lfs->gstate.pair[1]); - } - lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag); - lfs->gdisk = lfs->gstate; - - // setup free lookahead - lfs_alloc_reset(lfs); - - LFS_TRACE("lfs_mount -> %d", 0); - return 0; - -cleanup: - lfs_unmount(lfs); - LFS_TRACE("lfs_mount -> %d", err); - return err; -} - -int lfs_unmount(lfs_t *lfs) { - LFS_TRACE("lfs_unmount(%p)", (void*)lfs); - int err = lfs_deinit(lfs); - LFS_TRACE("lfs_unmount -> %d", err); - return err; -} - - -/// Filesystem filesystem operations /// -int lfs_fs_traverseraw(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data, - bool includeorphans) { - // iterate over metadata pairs - lfs_mdir_t dir = {.tail = {0, 1}}; - -#ifdef LFS_MIGRATE - // also consider v1 blocks during migration - if (lfs->lfs1) { - int err = lfs1_traverse(lfs, cb, data); - if (err) { - return err; - } - - dir.tail[0] = lfs->root[0]; - dir.tail[1] = lfs->root[1]; - } -#endif - - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(dir.tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - for (int i = 0; i < 2; i++) { - int err = cb(data, dir.tail[i]); - if (err) { - return err; - } - } - - // iterate through ids in directory - int err = lfs_dir_fetch(lfs, &dir, dir.tail); - if (err) { - return err; - } - - for (uint16_t id = 0; id < dir.count; id++) { - struct lfs_ctz ctz; - lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); - if (tag < 0) { - if (tag == LFS_ERR_NOENT) { - continue; - } - return tag; - } - lfs_ctz_fromle32(&ctz); - - if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - ctz.head, ctz.size, cb, data); - if (err) { - return err; - } - } else if (includeorphans && - lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { - for (int i = 0; i < 2; i++) { - err = cb(data, (&ctz.head)[i]); - if (err) { - return err; - } - } - } - } - } - - // iterate over any open files - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (f->type != LFS_TYPE_REG) { - continue; - } - - if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->ctz.head, f->ctz.size, cb, data); - if (err) { - return err; - } - } - - if ((f->flags & LFS_F_WRITING) && !(f->flags & LFS_F_INLINE)) { - int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, - f->block, f->pos, cb, data); - if (err) { - return err; - } - } - } - - return 0; -} - -int lfs_fs_traverse(lfs_t *lfs, - int (*cb)(void *data, lfs_block_t block), void *data) { - LFS_TRACE("lfs_fs_traverse(%p, %p, %p)", - (void*)lfs, (void*)(uintptr_t)cb, data); - int err = lfs_fs_traverseraw(lfs, cb, data, true); - LFS_TRACE("lfs_fs_traverse -> %d", 0); - return err; -} - -static int lfs_fs_pred(lfs_t *lfs, - const lfs_block_t pair[2], lfs_mdir_t *pdir) { - // iterate over all directory directory entries - pdir->tail[0] = 0; - pdir->tail[1] = 1; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(pdir->tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - if (lfs_pair_cmp(pdir->tail, pair) == 0) { - return 0; - } - - int err = lfs_dir_fetch(lfs, pdir, pdir->tail); - if (err) { - return err; - } - } - - return LFS_ERR_NOENT; -} - -struct lfs_fs_parent_match { - lfs_t *lfs; - const lfs_block_t pair[2]; -}; - -static int lfs_fs_parent_match(void *data, - lfs_tag_t tag, const void *buffer) { - struct lfs_fs_parent_match *find = data; - lfs_t *lfs = find->lfs; - const struct lfs_diskoff *disk = buffer; - (void)tag; - - lfs_block_t child[2]; - int err = lfs_bd_read(lfs, - &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, - disk->block, disk->off, &child, sizeof(child)); - if (err) { - return err; - } - - lfs_pair_fromle32(child); - return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; -} - -static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], - lfs_mdir_t *parent) { - // use fetchmatch with callback to find pairs - parent->tail[0] = 0; - parent->tail[1] = 1; - lfs_block_t cycle = 0; - while (!lfs_pair_isnull(parent->tail)) { - if (cycle >= lfs->cfg->block_count/2) { - // loop detected - return LFS_ERR_CORRUPT; - } - cycle += 1; - - lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, - LFS_MKTAG(0x7ff, 0, 0x3ff), - LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), - NULL, - lfs_fs_parent_match, &(struct lfs_fs_parent_match){ - lfs, {pair[0], pair[1]}}); - if (tag && tag != LFS_ERR_NOENT) { - return tag; - } - } - - return LFS_ERR_NOENT; -} - -static int lfs_fs_relocate(lfs_t *lfs, - const lfs_block_t oldpair[2], lfs_block_t newpair[2]) { - // update internal root - if (lfs_pair_cmp(oldpair, lfs->root) == 0) { - lfs->root[0] = newpair[0]; - lfs->root[1] = newpair[1]; - } - - // update internally tracked dirs - for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { - if (lfs_pair_cmp(oldpair, d->m.pair) == 0) { - d->m.pair[0] = newpair[0]; - d->m.pair[1] = newpair[1]; - } - - if (d->type == LFS_TYPE_DIR && - lfs_pair_cmp(oldpair, ((lfs_dir_t*)d)->head) == 0) { - ((lfs_dir_t*)d)->head[0] = newpair[0]; - ((lfs_dir_t*)d)->head[1] = newpair[1]; - } - } - - // find parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, oldpair, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag != LFS_ERR_NOENT) { - // update disk, this creates a desync - lfs_fs_preporphans(lfs, +1); - - // fix pending move in this pair? this looks like an optimization but - // is in fact _required_ since relocating may outdate the move. - uint16_t moveid = 0x3ff; - if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { - moveid = lfs_tag_id(lfs->gstate.tag); - LFS_DEBUG("Fixing move while relocating " - "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", - parent.pair[0], parent.pair[1], moveid); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - if (moveid < lfs_tag_id(tag)) { - tag -= LFS_MKTAG(0, 1, 0); - } - } - - lfs_pair_tole32(newpair); - int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( - {LFS_MKTAG_IF(moveid != 0x3ff, - LFS_TYPE_DELETE, moveid, 0), NULL}, - {tag, newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - - // next step, clean up orphans - lfs_fs_preporphans(lfs, -1); - } - - // find pred - int err = lfs_fs_pred(lfs, oldpair, &parent); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - // if we can't find dir, it must be new - if (err != LFS_ERR_NOENT) { - // fix pending move in this pair? this looks like an optimization but - // is in fact _required_ since relocating may outdate the move. - uint16_t moveid = 0x3ff; - if (lfs_gstate_hasmovehere(&lfs->gstate, parent.pair)) { - moveid = lfs_tag_id(lfs->gstate.tag); - LFS_DEBUG("Fixing move while relocating " - "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", - parent.pair[0], parent.pair[1], moveid); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - } - - // replace bad pair, either we clean up desync, or no desync occured - lfs_pair_tole32(newpair); - err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( - {LFS_MKTAG_IF(moveid != 0x3ff, - LFS_TYPE_DELETE, moveid, 0), NULL}, - {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); - lfs_pair_fromle32(newpair); - if (err) { - return err; - } - } - - return 0; -} - -static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { - LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0 || orphans >= 0); - lfs->gstate.tag += orphans; - lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) | - ((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31)); -} - -static void lfs_fs_prepmove(lfs_t *lfs, - uint16_t id, const lfs_block_t pair[2]) { - lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x7ff, 0x3ff, 0)) | - ((id != 0x3ff) ? LFS_MKTAG(LFS_TYPE_DELETE, id, 0) : 0)); - lfs->gstate.pair[0] = (id != 0x3ff) ? pair[0] : 0; - lfs->gstate.pair[1] = (id != 0x3ff) ? pair[1] : 0; -} - -static int lfs_fs_demove(lfs_t *lfs) { - if (!lfs_gstate_hasmove(&lfs->gdisk)) { - return 0; - } - - // Fix bad moves - LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16, - lfs->gdisk.pair[0], - lfs->gdisk.pair[1], - lfs_tag_id(lfs->gdisk.tag)); - - // fetch and delete the moved entry - lfs_mdir_t movedir; - int err = lfs_dir_fetch(lfs, &movedir, lfs->gdisk.pair); - if (err) { - return err; - } - - // prep gstate and delete move id - uint16_t moveid = lfs_tag_id(lfs->gdisk.tag); - lfs_fs_prepmove(lfs, 0x3ff, NULL); - err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0), NULL})); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_deorphan(lfs_t *lfs) { - if (!lfs_gstate_hasorphans(&lfs->gstate)) { - return 0; - } - - // Fix any orphans - lfs_mdir_t pdir = {.split = true, .tail = {0, 1}}; - lfs_mdir_t dir; - - // iterate over all directory directory entries - while (!lfs_pair_isnull(pdir.tail)) { - int err = lfs_dir_fetch(lfs, &dir, pdir.tail); - if (err) { - return err; - } - - // check head blocks for orphans - if (!pdir.split) { - // check if we have a parent - lfs_mdir_t parent; - lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); - if (tag < 0 && tag != LFS_ERR_NOENT) { - return tag; - } - - if (tag == LFS_ERR_NOENT) { - // we are an orphan - LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", - pdir.tail[0], pdir.tail[1]); - - err = lfs_dir_drop(lfs, &pdir, &dir); - if (err) { - return err; - } - - // refetch tail - continue; - } - - lfs_block_t pair[2]; - lfs_stag_t res = lfs_dir_get(lfs, &parent, - LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); - if (res < 0) { - return res; - } - lfs_pair_fromle32(pair); - - if (!lfs_pair_sync(pair, pdir.tail)) { - // we have desynced - LFS_DEBUG("Fixing half-orphan {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - pdir.tail[0], pdir.tail[1], pair[0], pair[1]); - - lfs_pair_tole32(pair); - err = lfs_dir_commit(lfs, &pdir, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pair})); - lfs_pair_fromle32(pair); - if (err) { - return err; - } - - // refetch tail - continue; - } - } - - pdir = dir; - } - - // mark orphans as fixed - lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); - return 0; -} - -static int lfs_fs_forceconsistency(lfs_t *lfs) { - int err = lfs_fs_demove(lfs); - if (err) { - return err; - } - - err = lfs_fs_deorphan(lfs); - if (err) { - return err; - } - - return 0; -} - -static int lfs_fs_size_count(void *p, lfs_block_t block) { - (void)block; - lfs_size_t *size = p; - *size += 1; - return 0; -} - -lfs_ssize_t lfs_fs_size(lfs_t *lfs) { - LFS_TRACE("lfs_fs_size(%p)", (void*)lfs); - lfs_size_t size = 0; - int err = lfs_fs_traverseraw(lfs, lfs_fs_size_count, &size, false); - if (err) { - LFS_TRACE("lfs_fs_size -> %d", err); - return err; - } - - LFS_TRACE("lfs_fs_size -> %d", err); - return size; -} - -#ifdef LFS_MIGRATE -////// Migration from littelfs v1 below this ////// - -/// Version info /// - -// Software library version -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_VERSION 0x00010007 -#define LFS1_VERSION_MAJOR (0xffff & (LFS1_VERSION >> 16)) -#define LFS1_VERSION_MINOR (0xffff & (LFS1_VERSION >> 0)) - -// Version of On-disk data structures -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS1_DISK_VERSION 0x00010001 -#define LFS1_DISK_VERSION_MAJOR (0xffff & (LFS1_DISK_VERSION >> 16)) -#define LFS1_DISK_VERSION_MINOR (0xffff & (LFS1_DISK_VERSION >> 0)) - - -/// v1 Definitions /// - -// File types -enum lfs1_type { - LFS1_TYPE_REG = 0x11, - LFS1_TYPE_DIR = 0x22, - LFS1_TYPE_SUPERBLOCK = 0x2e, -}; - -typedef struct lfs1 { - lfs_block_t root[2]; -} lfs1_t; - -typedef struct lfs1_entry { - lfs_off_t off; - - struct lfs1_disk_entry { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - union { - struct { - lfs_block_t head; - lfs_size_t size; - } file; - lfs_block_t dir[2]; - } u; - } d; -} lfs1_entry_t; - -typedef struct lfs1_dir { - struct lfs1_dir *next; - lfs_block_t pair[2]; - lfs_off_t off; - - lfs_block_t head[2]; - lfs_off_t pos; - - struct lfs1_disk_dir { - uint32_t rev; - lfs_size_t size; - lfs_block_t tail[2]; - } d; -} lfs1_dir_t; - -typedef struct lfs1_superblock { - lfs_off_t off; - - struct lfs1_disk_superblock { - uint8_t type; - uint8_t elen; - uint8_t alen; - uint8_t nlen; - lfs_block_t root[2]; - uint32_t block_size; - uint32_t block_count; - uint32_t version; - char magic[8]; - } d; -} lfs1_superblock_t; - - -/// Low-level wrappers v1->v2 /// -static void lfs1_crc(uint32_t *crc, const void *buffer, size_t size) { - *crc = lfs_crc(*crc, buffer, size); -} - -static int lfs1_bd_read(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - // if we ever do more than writes to alternating pairs, - // this may need to consider pcache - return lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, size, - block, off, buffer, size); -} - -static int lfs1_bd_crc(lfs_t *lfs, lfs_block_t block, - lfs_off_t off, lfs_size_t size, uint32_t *crc) { - for (lfs_off_t i = 0; i < size; i++) { - uint8_t c; - int err = lfs1_bd_read(lfs, block, off+i, &c, 1); - if (err) { - return err; - } - - lfs1_crc(crc, &c, 1); - } - - return 0; -} - - -/// Endian swapping functions /// -static void lfs1_dir_fromle32(struct lfs1_disk_dir *d) { - d->rev = lfs_fromle32(d->rev); - d->size = lfs_fromle32(d->size); - d->tail[0] = lfs_fromle32(d->tail[0]); - d->tail[1] = lfs_fromle32(d->tail[1]); -} - -static void lfs1_dir_tole32(struct lfs1_disk_dir *d) { - d->rev = lfs_tole32(d->rev); - d->size = lfs_tole32(d->size); - d->tail[0] = lfs_tole32(d->tail[0]); - d->tail[1] = lfs_tole32(d->tail[1]); -} - -static void lfs1_entry_fromle32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_fromle32(d->u.dir[0]); - d->u.dir[1] = lfs_fromle32(d->u.dir[1]); -} - -static void lfs1_entry_tole32(struct lfs1_disk_entry *d) { - d->u.dir[0] = lfs_tole32(d->u.dir[0]); - d->u.dir[1] = lfs_tole32(d->u.dir[1]); -} - -static void lfs1_superblock_fromle32(struct lfs1_disk_superblock *d) { - d->root[0] = lfs_fromle32(d->root[0]); - d->root[1] = lfs_fromle32(d->root[1]); - d->block_size = lfs_fromle32(d->block_size); - d->block_count = lfs_fromle32(d->block_count); - d->version = lfs_fromle32(d->version); -} - - -///// Metadata pair and directory operations /// -static inline lfs_size_t lfs1_entry_size(const lfs1_entry_t *entry) { - return 4 + entry->d.elen + entry->d.alen + entry->d.nlen; -} - -static int lfs1_dir_fetch(lfs_t *lfs, - lfs1_dir_t *dir, const lfs_block_t pair[2]) { - // copy out pair, otherwise may be aliasing dir - const lfs_block_t tpair[2] = {pair[0], pair[1]}; - bool valid = false; - - // check both blocks for the most recent revision - for (int i = 0; i < 2; i++) { - struct lfs1_disk_dir test; - int err = lfs1_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) { - continue; - } - - if ((0x7fffffff & test.size) < sizeof(test)+4 || - (0x7fffffff & test.size) > lfs->cfg->block_size) { - continue; - } - - uint32_t crc = 0xffffffff; - lfs1_dir_tole32(&test); - lfs1_crc(&crc, &test, sizeof(test)); - lfs1_dir_fromle32(&test); - err = lfs1_bd_crc(lfs, tpair[i], sizeof(test), - (0x7fffffff & test.size) - sizeof(test), &crc); - if (err) { - if (err == LFS_ERR_CORRUPT) { - continue; - } - return err; - } - - if (crc != 0) { - continue; - } - - valid = true; - - // setup dir in case it's valid - dir->pair[0] = tpair[(i+0) % 2]; - dir->pair[1] = tpair[(i+1) % 2]; - dir->off = sizeof(dir->d); - dir->d = test; - } - - if (!valid) { - LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", - tpair[0], tpair[1]); - return LFS_ERR_CORRUPT; - } - - return 0; -} - -static int lfs1_dir_next(lfs_t *lfs, lfs1_dir_t *dir, lfs1_entry_t *entry) { - while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { - if (!(0x80000000 & dir->d.size)) { - entry->off = dir->off; - return LFS_ERR_NOENT; - } - - int err = lfs1_dir_fetch(lfs, dir, dir->d.tail); - if (err) { - return err; - } - - dir->off = sizeof(dir->d); - dir->pos += sizeof(dir->d) + 4; - } - - int err = lfs1_bd_read(lfs, dir->pair[0], dir->off, - &entry->d, sizeof(entry->d)); - lfs1_entry_fromle32(&entry->d); - if (err) { - return err; - } - - entry->off = dir->off; - dir->off += lfs1_entry_size(entry); - dir->pos += lfs1_entry_size(entry); - return 0; -} - -/// littlefs v1 specific operations /// -int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // iterate over metadata pairs - lfs1_dir_t dir; - lfs1_entry_t entry; - lfs_block_t cwd[2] = {0, 1}; - - while (true) { - for (int i = 0; i < 2; i++) { - int err = cb(data, cwd[i]); - if (err) { - return err; - } - } - - int err = lfs1_dir_fetch(lfs, &dir, cwd); - if (err) { - return err; - } - - // iterate over contents - while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { - err = lfs1_bd_read(lfs, dir.pair[0], dir.off, - &entry.d, sizeof(entry.d)); - lfs1_entry_fromle32(&entry.d); - if (err) { - return err; - } - - dir.off += lfs1_entry_size(&entry); - if ((0x70 & entry.d.type) == (0x70 & LFS1_TYPE_REG)) { - err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, - entry.d.u.file.head, entry.d.u.file.size, cb, data); - if (err) { - return err; - } - } - } - - // we also need to check if we contain a threaded v2 directory - lfs_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}}; - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - break; - } - - for (int i = 0; i < 2; i++) { - err = cb(data, dir2.pair[i]); - if (err) { - return err; - } - } - } - - cwd[0] = dir.d.tail[0]; - cwd[1] = dir.d.tail[1]; - - if (lfs_pair_isnull(cwd)) { - break; - } - } - - return 0; -} - -static int lfs1_moved(lfs_t *lfs, const void *e) { - if (lfs_pair_isnull(lfs->lfs1->root)) { - return 0; - } - - // skip superblock - lfs1_dir_t cwd; - int err = lfs1_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); - if (err) { - return err; - } - - // iterate over all directory directory entries - lfs1_entry_t entry; - while (!lfs_pair_isnull(cwd.d.tail)) { - err = lfs1_dir_fetch(lfs, &cwd, cwd.d.tail); - if (err) { - return err; - } - - while (true) { - err = lfs1_dir_next(lfs, &cwd, &entry); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - if (!(0x80 & entry.d.type) && - memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { - return true; - } - } - } - - return false; -} - -/// Filesystem operations /// -static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, - const struct lfs_config *cfg) { - int err = 0; - { - err = lfs_init(lfs, cfg); - if (err) { - return err; - } - - lfs->lfs1 = lfs1; - lfs->lfs1->root[0] = LFS_BLOCK_NULL; - lfs->lfs1->root[1] = LFS_BLOCK_NULL; - - // setup free lookahead - lfs->free.off = 0; - lfs->free.size = 0; - lfs->free.i = 0; - lfs_alloc_ack(lfs); - - // load superblock - lfs1_dir_t dir; - lfs1_superblock_t superblock; - err = lfs1_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); - if (err && err != LFS_ERR_CORRUPT) { - goto cleanup; - } - - if (!err) { - err = lfs1_bd_read(lfs, dir.pair[0], sizeof(dir.d), - &superblock.d, sizeof(superblock.d)); - lfs1_superblock_fromle32(&superblock.d); - if (err) { - goto cleanup; - } - - lfs->lfs1->root[0] = superblock.d.root[0]; - lfs->lfs1->root[1] = superblock.d.root[1]; - } - - if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { - LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}", - 0, 1); - err = LFS_ERR_CORRUPT; - goto cleanup; - } - - uint16_t major_version = (0xffff & (superblock.d.version >> 16)); - uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); - if ((major_version != LFS1_DISK_VERSION_MAJOR || - minor_version > LFS1_DISK_VERSION_MINOR)) { - LFS_ERROR("Invalid version v%d.%d", major_version, minor_version); - err = LFS_ERR_INVAL; - goto cleanup; - } - - return 0; - } - -cleanup: - lfs_deinit(lfs); - return err; -} - -static int lfs1_unmount(lfs_t *lfs) { - return lfs_deinit(lfs); -} - -/// v1 migration /// -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { - LFS_TRACE("lfs_migrate(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); - struct lfs1 lfs1; - int err = lfs1_mount(lfs, &lfs1, cfg); - if (err) { - LFS_TRACE("lfs_migrate -> %d", err); - return err; - } - - { - // iterate through each directory, copying over entries - // into new directory - lfs1_dir_t dir1; - lfs_mdir_t dir2; - dir1.d.tail[0] = lfs->lfs1->root[0]; - dir1.d.tail[1] = lfs->lfs1->root[1]; - while (!lfs_pair_isnull(dir1.d.tail)) { - // iterate old dir - err = lfs1_dir_fetch(lfs, &dir1, dir1.d.tail); - if (err) { - goto cleanup; - } - - // create new dir and bind as temporary pretend root - err = lfs_dir_alloc(lfs, &dir2); - if (err) { - goto cleanup; - } - - dir2.rev = dir1.d.rev; - dir1.head[0] = dir1.pair[0]; - dir1.head[1] = dir1.pair[1]; - lfs->root[0] = dir2.pair[0]; - lfs->root[1] = dir2.pair[1]; - - err = lfs_dir_commit(lfs, &dir2, NULL, 0); - if (err) { - goto cleanup; - } - - while (true) { - lfs1_entry_t entry1; - err = lfs1_dir_next(lfs, &dir1, &entry1); - if (err && err != LFS_ERR_NOENT) { - goto cleanup; - } - - if (err == LFS_ERR_NOENT) { - break; - } - - // check that entry has not been moved - if (entry1.d.type & 0x80) { - int moved = lfs1_moved(lfs, &entry1.d.u); - if (moved < 0) { - err = moved; - goto cleanup; - } - - if (moved) { - continue; - } - - entry1.d.type &= ~0x80; - } - - // also fetch name - char name[LFS_NAME_MAX+1]; - memset(name, 0, sizeof(name)); - err = lfs1_bd_read(lfs, dir1.pair[0], - entry1.off + 4+entry1.d.elen+entry1.d.alen, - name, entry1.d.nlen); - if (err) { - goto cleanup; - } - - bool isdir = (entry1.d.type == LFS1_TYPE_DIR); - - // create entry in new dir - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - uint16_t id; - err = lfs_dir_find(lfs, &dir2, &(const char*){name}, &id); - if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { - err = (err < 0) ? err : LFS_ERR_EXIST; - goto cleanup; - } - - lfs1_entry_tole32(&entry1.d); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, id, 0)}, - {LFS_MKTAG_IF_ELSE(isdir, - LFS_TYPE_DIR, id, entry1.d.nlen, - LFS_TYPE_REG, id, entry1.d.nlen), - name}, - {LFS_MKTAG_IF_ELSE(isdir, - LFS_TYPE_DIRSTRUCT, id, sizeof(entry1.d.u), - LFS_TYPE_CTZSTRUCT, id, sizeof(entry1.d.u)), - &entry1.d.u})); - lfs1_entry_fromle32(&entry1.d); - if (err) { - goto cleanup; - } - } - - if (!lfs_pair_isnull(dir1.d.tail)) { - // find last block and update tail to thread into fs - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - while (dir2.split) { - err = lfs_dir_fetch(lfs, &dir2, dir2.tail); - if (err) { - goto cleanup; - } - } - - lfs_pair_tole32(dir2.pair); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir1.d.tail})); - lfs_pair_fromle32(dir2.pair); - if (err) { - goto cleanup; - } - } - - // Copy over first block to thread into fs. Unfortunately - // if this fails there is not much we can do. - LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} " - "-> {0x%"PRIx32", 0x%"PRIx32"}", - lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); - - err = lfs_bd_erase(lfs, dir1.head[1]); - if (err) { - goto cleanup; - } - - err = lfs_dir_fetch(lfs, &dir2, lfs->root); - if (err) { - goto cleanup; - } - - for (lfs_off_t i = 0; i < dir2.off; i++) { - uint8_t dat; - err = lfs_bd_read(lfs, - NULL, &lfs->rcache, dir2.off, - dir2.pair[0], i, &dat, 1); - if (err) { - goto cleanup; - } - - err = lfs_bd_prog(lfs, - &lfs->pcache, &lfs->rcache, true, - dir1.head[1], i, &dat, 1); - if (err) { - goto cleanup; - } - } - - err = lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, true); - if (err) { - goto cleanup; - } - } - - // Create new superblock. This marks a successful migration! - err = lfs1_dir_fetch(lfs, &dir1, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - dir2.pair[0] = dir1.pair[0]; - dir2.pair[1] = dir1.pair[1]; - dir2.rev = dir1.d.rev; - dir2.off = sizeof(dir2.rev); - dir2.etag = 0xffffffff; - dir2.count = 0; - dir2.tail[0] = lfs->lfs1->root[0]; - dir2.tail[1] = lfs->lfs1->root[1]; - dir2.erased = false; - dir2.split = true; - - lfs_superblock_t superblock = { - .version = LFS_DISK_VERSION, - .block_size = lfs->cfg->block_size, - .block_count = lfs->cfg->block_count, - .name_max = lfs->name_max, - .file_max = lfs->file_max, - .attr_max = lfs->attr_max, - }; - - lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( - {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0)}, - {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, - {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock})); - if (err) { - goto cleanup; - } - - // sanity check that fetch works - err = lfs_dir_fetch(lfs, &dir2, (const lfs_block_t[2]){0, 1}); - if (err) { - goto cleanup; - } - - // force compaction to prevent accidentally mounting v1 - dir2.erased = false; - err = lfs_dir_commit(lfs, &dir2, NULL, 0); - if (err) { - goto cleanup; - } - } - -cleanup: - lfs1_unmount(lfs); - LFS_TRACE("lfs_migrate -> %d", err); - return err; -} - -#endif diff --git a/lib/LITTLEFS/src/lfs.h b/lib/LITTLEFS/src/lfs.h deleted file mode 100644 index 35bbbabf..00000000 --- a/lib/LITTLEFS/src/lfs.h +++ /dev/null @@ -1,655 +0,0 @@ -/* - * The little filesystem - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef LFS_H -#define LFS_H - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - -/// Version info /// - -// Software library version -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS_VERSION 0x00020002 -#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) -#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) - -// Version of On-disk data structures -// Major (top-nibble), incremented on backwards incompatible changes -// Minor (bottom-nibble), incremented on feature additions -#define LFS_DISK_VERSION 0x00020000 -#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) -#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) - - -/// Definitions /// - -// Type definitions -typedef uint32_t lfs_size_t; -typedef uint32_t lfs_off_t; - -typedef int32_t lfs_ssize_t; -typedef int32_t lfs_soff_t; - -typedef uint32_t lfs_block_t; - -// Maximum name size in bytes, may be redefined to reduce the size of the -// info struct. Limited to <= 1022. Stored in superblock and must be -// respected by other littlefs drivers. -#ifndef LFS_NAME_MAX -#define LFS_NAME_MAX 255 -#endif - -// Maximum size of a file in bytes, may be redefined to limit to support other -// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs_file_seek, lfs_file_size, and lfs_file_tell will return -// incorrect values due to using signed integers. Stored in superblock and -// must be respected by other littlefs drivers. -#ifndef LFS_FILE_MAX -#define LFS_FILE_MAX 2147483647 -#endif - -// Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. -#ifndef LFS_ATTR_MAX -#define LFS_ATTR_MAX 1022 -#endif - -// Possible error codes, these are negative to allow -// valid positive return values -enum lfs_error { - LFS_ERR_OK = 0, // No error - LFS_ERR_IO = -5, // Error during device operation - LFS_ERR_CORRUPT = -84, // Corrupted - LFS_ERR_NOENT = -2, // No directory entry - LFS_ERR_EXIST = -17, // Entry already exists - LFS_ERR_NOTDIR = -20, // Entry is not a dir - LFS_ERR_ISDIR = -21, // Entry is a dir - LFS_ERR_NOTEMPTY = -39, // Dir is not empty - LFS_ERR_BADF = -9, // Bad file number - LFS_ERR_FBIG = -27, // File too large - LFS_ERR_INVAL = -22, // Invalid parameter - LFS_ERR_NOSPC = -28, // No space left on device - LFS_ERR_NOMEM = -12, // No more memory available - LFS_ERR_NOATTR = -61, // No data/attr available - LFS_ERR_NAMETOOLONG = -36, // File name too long -}; - -// File types -enum lfs_type { - // file types - LFS_TYPE_REG = 0x001, - LFS_TYPE_DIR = 0x002, - - // internally used types - LFS_TYPE_SPLICE = 0x400, - LFS_TYPE_NAME = 0x000, - LFS_TYPE_STRUCT = 0x200, - LFS_TYPE_USERATTR = 0x300, - LFS_TYPE_FROM = 0x100, - LFS_TYPE_TAIL = 0x600, - LFS_TYPE_GLOBALS = 0x700, - LFS_TYPE_CRC = 0x500, - - // internally used type specializations - LFS_TYPE_CREATE = 0x401, - LFS_TYPE_DELETE = 0x4ff, - LFS_TYPE_SUPERBLOCK = 0x0ff, - LFS_TYPE_DIRSTRUCT = 0x200, - LFS_TYPE_CTZSTRUCT = 0x202, - LFS_TYPE_INLINESTRUCT = 0x201, - LFS_TYPE_SOFTTAIL = 0x600, - LFS_TYPE_HARDTAIL = 0x601, - LFS_TYPE_MOVESTATE = 0x7ff, - - // internal chip sources - LFS_FROM_NOOP = 0x000, - LFS_FROM_MOVE = 0x101, - LFS_FROM_USERATTRS = 0x102, -}; - -// File open flags -enum lfs_open_flags { - // open flags - LFS_O_RDONLY = 1, // Open a file as read only - LFS_O_WRONLY = 2, // Open a file as write only - LFS_O_RDWR = 3, // Open a file as read and write - LFS_O_CREAT = 0x0100, // Create a file if it does not exist - LFS_O_EXCL = 0x0200, // Fail if a file already exists - LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size - LFS_O_APPEND = 0x0800, // Move to end of file on every write - - // internally used flags - LFS_F_DIRTY = 0x010000, // File does not match storage - LFS_F_WRITING = 0x020000, // File has been written since last flush - LFS_F_READING = 0x040000, // File has been read since last flush - LFS_F_ERRED = 0x080000, // An error occured during write - LFS_F_INLINE = 0x100000, // Currently inlined in directory entry - LFS_F_OPENED = 0x200000, // File has been opened -}; - -// File seek flags -enum lfs_whence_flags { - LFS_SEEK_SET = 0, // Seek relative to an absolute position - LFS_SEEK_CUR = 1, // Seek relative to the current file position - LFS_SEEK_END = 2, // Seek relative to the end of the file -}; - - -// Configuration provided during initialization of the littlefs -struct lfs_config { - // Opaque user provided context that can be used to pass - // information to the block device operations - void *context; - - // Read a region in a block. Negative error codes are propogated - // to the user. - int (*read)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); - - // Program a region in a block. The block must have previously - // been erased. Negative error codes are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*prog)(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); - - // Erase a block. A block must be erased before being programmed. - // The state of an erased block is undefined. Negative error codes - // are propogated to the user. - // May return LFS_ERR_CORRUPT if the block should be considered bad. - int (*erase)(const struct lfs_config *c, lfs_block_t block); - - // Sync the state of the underlying block device. Negative error codes - // are propogated to the user. - int (*sync)(const struct lfs_config *c); - - // Minimum size of a block read. All read operations will be a - // multiple of this value. - lfs_size_t read_size; - - // Minimum size of a block program. All program operations will be a - // multiple of this value. - lfs_size_t prog_size; - - // Size of an erasable block. This does not impact ram consumption and - // may be larger than the physical erase size. However, non-inlined files - // take up at minimum one block. Must be a multiple of the read - // and program sizes. - lfs_size_t block_size; - - // Number of erasable blocks on the device. - lfs_size_t block_count; - - // Number of erase cycles before littlefs evicts metadata logs and moves - // the metadata to another block. Suggested values are in the - // range 100-1000, with large values having better performance at the cost - // of less consistent wear distribution. - // - // Set to -1 to disable block-level wear-leveling. - int32_t block_cycles; - - // Size of block caches. Each cache buffers a portion of a block in RAM. - // The littlefs needs a read cache, a program cache, and one additional - // cache per file. Larger caches can improve performance by storing more - // data and reducing the number of disk accesses. Must be a multiple of - // the read and program sizes, and a factor of the block size. - lfs_size_t cache_size; - - // Size of the lookahead buffer in bytes. A larger lookahead buffer - // increases the number of blocks found during an allocation pass. The - // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 8. - lfs_size_t lookahead_size; - - // Optional statically allocated read buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *read_buffer; - - // Optional statically allocated program buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *prog_buffer; - - // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 32-bit boundary. By default lfs_malloc is used to - // allocate this buffer. - void *lookahead_buffer; - - // Optional upper limit on length of file names in bytes. No downside for - // larger names except the size of the info struct which is controlled by - // the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in - // superblock and must be respected by other littlefs drivers. - lfs_size_t name_max; - - // Optional upper limit on files in bytes. No downside for larger files - // but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX when zero. Stored - // in superblock and must be respected by other littlefs drivers. - lfs_size_t file_max; - - // Optional upper limit on custom attributes in bytes. No downside for - // larger attributes size but must be <= LFS_ATTR_MAX. Defaults to - // LFS_ATTR_MAX when zero. - lfs_size_t attr_max; -}; - -// File info structure -struct lfs_info { - // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR - uint8_t type; - - // Size of the file, only valid for REG files. Limited to 32-bits. - lfs_size_t size; - - // Name of the file stored as a null-terminated string. Limited to - // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to - // reduce RAM. LFS_NAME_MAX is stored in superblock and must be - // respected by other littlefs drivers. - char name[LFS_NAME_MAX+1]; -}; - -// Custom attribute structure, used to describe custom attributes -// committed atomically during file writes. -struct lfs_attr { - // 8-bit type of attribute, provided by user and used to - // identify the attribute - uint8_t type; - - // Pointer to buffer containing the attribute - void *buffer; - - // Size of attribute in bytes, limited to LFS_ATTR_MAX - lfs_size_t size; -}; - -// Optional configuration provided during lfs_file_opencfg -struct lfs_file_config { - // Optional statically allocated file buffer. Must be cache_size. - // By default lfs_malloc is used to allocate this buffer. - void *buffer; - - // Optional list of custom attributes related to the file. If the file - // is opened with read access, these attributes will be read from disk - // during the open call. If the file is opened with write access, the - // attributes will be written to disk every file sync or close. This - // write occurs atomically with update to the file's contents. - // - // Custom attributes are uniquely identified by an 8-bit type and limited - // to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller - // than the buffer, it will be padded with zeros. If the stored attribute - // is larger, then it will be silently truncated. If the attribute is not - // found, it will be created implicitly. - struct lfs_attr *attrs; - - // Number of custom attributes in the list - lfs_size_t attr_count; -}; - - -/// internal littlefs data structures /// -typedef struct lfs_cache { - lfs_block_t block; - lfs_off_t off; - lfs_size_t size; - uint8_t *buffer; -} lfs_cache_t; - -typedef struct lfs_mdir { - lfs_block_t pair[2]; - uint32_t rev; - lfs_off_t off; - uint32_t etag; - uint16_t count; - bool erased; - bool split; - lfs_block_t tail[2]; -} lfs_mdir_t; - -// littlefs directory type -typedef struct lfs_dir { - struct lfs_dir *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - - lfs_off_t pos; - lfs_block_t head[2]; -} lfs_dir_t; - -// littlefs file type -typedef struct lfs_file { - struct lfs_file *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - - struct lfs_ctz { - lfs_block_t head; - lfs_size_t size; - } ctz; - - uint32_t flags; - lfs_off_t pos; - lfs_block_t block; - lfs_off_t off; - lfs_cache_t cache; - - const struct lfs_file_config *cfg; -} lfs_file_t; - -typedef struct lfs_superblock { - uint32_t version; - lfs_size_t block_size; - lfs_size_t block_count; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; -} lfs_superblock_t; - -typedef struct lfs_gstate { - uint32_t tag; - lfs_block_t pair[2]; -} lfs_gstate_t; - -// The littlefs filesystem type -typedef struct lfs { - lfs_cache_t rcache; - lfs_cache_t pcache; - - lfs_block_t root[2]; - struct lfs_mlist { - struct lfs_mlist *next; - uint16_t id; - uint8_t type; - lfs_mdir_t m; - } *mlist; - uint32_t seed; - - lfs_gstate_t gstate; - lfs_gstate_t gdisk; - lfs_gstate_t gdelta; - - struct lfs_free { - lfs_block_t off; - lfs_block_t size; - lfs_block_t i; - lfs_block_t ack; - uint32_t *buffer; - } free; - - const struct lfs_config *cfg; - lfs_size_t name_max; - lfs_size_t file_max; - lfs_size_t attr_max; - -#ifdef LFS_MIGRATE - struct lfs1 *lfs1; -#endif -} lfs_t; - - -/// Filesystem functions /// - -// Format a block device with the littlefs -// -// Requires a littlefs object and config struct. This clobbers the littlefs -// object, and does not leave the filesystem mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_format(lfs_t *lfs, const struct lfs_config *config); - -// Mounts a littlefs -// -// Requires a littlefs object and config struct. Multiple filesystems -// may be mounted simultaneously with multiple littlefs objects. Both -// lfs and config must be allocated while mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_mount(lfs_t *lfs, const struct lfs_config *config); - -// Unmounts a littlefs -// -// Does nothing besides releasing any allocated resources. -// Returns a negative error code on failure. -int lfs_unmount(lfs_t *lfs); - -/// General operations /// - -// Removes a file or directory -// -// If removing a directory, the directory must be empty. -// Returns a negative error code on failure. -int lfs_remove(lfs_t *lfs, const char *path); - -// Rename or move a file or directory -// -// If the destination exists, it must match the source in type. -// If the destination is a directory, the directory must be empty. -// -// Returns a negative error code on failure. -int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); - -// Find info about a file or directory -// -// Fills out the info structure, based on the specified file or directory. -// Returns a negative error code on failure. -int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); - -// Get a custom attribute -// -// Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than -// the buffer, it will be padded with zeros. If the stored attribute is larger, -// then it will be silently truncated. If no attribute is found, the error -// LFS_ERR_NOATTR is returned and the buffer is filled with zeros. -// -// Returns the size of the attribute, or a negative error code on failure. -// Note, the returned size is the size of the attribute on disk, irrespective -// of the size of the buffer. This can be used to dynamically allocate a buffer -// or check for existance. -lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, - uint8_t type, void *buffer, lfs_size_t size); - -// Set custom attributes -// -// Custom attributes are uniquely identified by an 8-bit type and limited -// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be -// implicitly created. -// -// Returns a negative error code on failure. -int lfs_setattr(lfs_t *lfs, const char *path, - uint8_t type, const void *buffer, lfs_size_t size); - -// Removes a custom attribute -// -// If an attribute is not found, nothing happens. -// -// Returns a negative error code on failure. -int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); - - -/// File operations /// - -// Open a file -// -// The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. -// -// Returns a negative error code on failure. -int lfs_file_open(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags); - -// Open a file with extra configuration -// -// The mode that the file is opened in is determined by the flags, which -// are values from the enum lfs_open_flags that are bitwise-ored together. -// -// The config struct provides additional config options per file as described -// above. The config struct must be allocated while the file is open, and the -// config struct must be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, - const char *path, int flags, - const struct lfs_file_config *config); - -// Close a file -// -// Any pending writes are written out to storage as though -// sync had been called and releases any allocated resources. -// -// Returns a negative error code on failure. -int lfs_file_close(lfs_t *lfs, lfs_file_t *file); - -// Synchronize a file on storage -// -// Any pending writes are written out to storage. -// Returns a negative error code on failure. -int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); - -// Read data from file -// -// Takes a buffer and size indicating where to store the read data. -// Returns the number of bytes read, or a negative error code on failure. -lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, - void *buffer, lfs_size_t size); - -// Write data to file -// -// Takes a buffer and size indicating the data to write. The file will not -// actually be updated on the storage until either sync or close is called. -// -// Returns the number of bytes written, or a negative error code on failure. -lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, - const void *buffer, lfs_size_t size); - -// Change the position of the file -// -// The change in position is determined by the offset and whence flag. -// Returns the new position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, - lfs_soff_t off, int whence); - -// Truncates the size of the file to the specified size -// -// Returns a negative error code on failure. -int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); - -// Return the position of the file -// -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) -// Returns the position of the file, or a negative error code on failure. -lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); - -// Change the position of the file to the beginning of the file -// -// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_SET) -// Returns a negative error code on failure. -int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); - -// Return the size of the file -// -// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END) -// Returns the size of the file, or a negative error code on failure. -lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); - - -/// Directory operations /// - -// Create a directory -// -// Returns a negative error code on failure. -int lfs_mkdir(lfs_t *lfs, const char *path); - -// Open a directory -// -// Once open a directory can be used with read to iterate over files. -// Returns a negative error code on failure. -int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); - -// Close a directory -// -// Releases any allocated resources. -// Returns a negative error code on failure. -int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); - -// Read an entry in the directory -// -// Fills out the info structure, based on the specified file or directory. -// Returns a positive value on success, 0 at the end of directory, -// or a negative error code on failure. -int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); - -// Change the position of the directory -// -// The new off must be a value previous returned from tell and specifies -// an absolute offset in the directory seek. -// -// Returns a negative error code on failure. -int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); - -// Return the position of the directory -// -// The returned offset is only meant to be consumed by seek and may not make -// sense, but does indicate the current position in the directory iteration. -// -// Returns the position of the directory, or a negative error code on failure. -lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); - -// Change the position of the directory to the beginning of the directory -// -// Returns a negative error code on failure. -int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); - - -/// Filesystem-level filesystem operations - -// Finds the current size of the filesystem -// -// Note: Result is best effort. If files share COW structures, the returned -// size may be larger than the filesystem actually is. -// -// Returns the number of allocated blocks, or a negative error code on failure. -lfs_ssize_t lfs_fs_size(lfs_t *lfs); - -// Traverse through all blocks in use by the filesystem -// -// The provided callback will be called with each block address that is -// currently in use by the filesystem. This can be used to determine which -// blocks are in use or how much of the storage is available. -// -// Returns a negative error code on failure. -int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); - -#ifdef LFS_MIGRATE -// Attempts to migrate a previous version of littlefs -// -// Behaves similarly to the lfs_format function. Attempts to mount -// the previous version of littlefs and update the filesystem so it can be -// mounted with the current version of littlefs. -// -// Requires a littlefs object and config struct. This clobbers the littlefs -// object, and does not leave the filesystem mounted. The config struct must -// be zeroed for defaults and backwards compatibility. -// -// Returns a negative error code on failure. -int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg); -#endif - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/lib/LITTLEFS/src/lfs_util.c b/lib/LITTLEFS/src/lfs_util.c deleted file mode 100644 index 0b60e3b4..00000000 --- a/lib/LITTLEFS/src/lfs_util.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * lfs util functions - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "lfs_util.h" - -// Only compile if user does not provide custom config -#ifndef LFS_CONFIG - - -// Software CRC implementation with small lookup table -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) { - static const uint32_t rtable[16] = { - 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, - 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, - 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, - }; - - const uint8_t *data = buffer; - - for (size_t i = 0; i < size; i++) { - crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; - crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; - } - - return crc; -} - - -#endif diff --git a/lib/LITTLEFS/src/lfs_util.h b/lib/LITTLEFS/src/lfs_util.h deleted file mode 100644 index dbb4c5ba..00000000 --- a/lib/LITTLEFS/src/lfs_util.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * lfs utility functions - * - * Copyright (c) 2017, Arm Limited. All rights reserved. - * SPDX-License-Identifier: BSD-3-Clause - */ -#ifndef LFS_UTIL_H -#define LFS_UTIL_H - -// Users can override lfs_util.h with their own configuration by defining -// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). -// -// If LFS_CONFIG is used, none of the default utils will be emitted and must be -// provided by the config file. To start, I would suggest copying lfs_util.h -// and modifying as needed. -#ifdef LFS_CONFIG -#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) -#define LFS_STRINGIZE2(x) #x -#include LFS_STRINGIZE(LFS_CONFIG) -#else - -// System includes -#include -#include -#include -#include - -#ifndef LFS_NO_MALLOC -#include -#endif -#ifndef LFS_NO_ASSERT -#include -#endif -#if !defined(LFS_NO_DEBUG) || \ - !defined(LFS_NO_WARN) || \ - !defined(LFS_NO_ERROR) || \ - defined(LFS_YES_TRACE) -#include -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - - -// Macros, may be replaced by system specific wrappers. Arguments to these -// macros must not have side-effects as the macros can be removed for a smaller -// code footprint - -// Logging functions -#ifdef LFS_YES_TRACE -#define LFS_TRACE_(fmt, ...) \ - printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "") -#else -#define LFS_TRACE(...) -#endif - -#ifndef LFS_NO_DEBUG -#define LFS_DEBUG_(fmt, ...) \ - printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "") -#else -#define LFS_DEBUG(...) -#endif - -#ifndef LFS_NO_WARN -#define LFS_WARN_(fmt, ...) \ - printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "") -#else -#define LFS_WARN(...) -#endif - -#ifndef LFS_NO_ERROR -#define LFS_ERROR_(fmt, ...) \ - printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__) -#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "") -#else -#define LFS_ERROR(...) -#endif - -// Runtime assertions -#ifndef LFS_NO_ASSERT -#define LFS_ASSERT(test) assert(test) -#else -#define LFS_ASSERT(test) -#endif - - -// Builtin functions, these may be replaced by more efficient -// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more -// expensive basic C implementation for debugging purposes - -// Min/max functions for unsigned 32-bit numbers -static inline uint32_t lfs_max(uint32_t a, uint32_t b) { - return (a > b) ? a : b; -} - -static inline uint32_t lfs_min(uint32_t a, uint32_t b) { - return (a < b) ? a : b; -} - -// Align to nearest multiple of a size -static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { - return a - (a % alignment); -} - -static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { - return lfs_aligndown(a + alignment-1, alignment); -} - -// Find the smallest power of 2 greater than or equal to a -static inline uint32_t lfs_npw2(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return 32 - __builtin_clz(a-1); -#else - uint32_t r = 0; - uint32_t s; - a -= 1; - s = (a > 0xffff) << 4; a >>= s; r |= s; - s = (a > 0xff ) << 3; a >>= s; r |= s; - s = (a > 0xf ) << 2; a >>= s; r |= s; - s = (a > 0x3 ) << 1; a >>= s; r |= s; - return (r | (a >> 1)) + 1; -#endif -} - -// Count the number of trailing binary zeros in a -// lfs_ctz(0) may be undefined -static inline uint32_t lfs_ctz(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) - return __builtin_ctz(a); -#else - return lfs_npw2((a & -a) + 1) - 1; -#endif -} - -// Count the number of binary ones in a -static inline uint32_t lfs_popc(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return __builtin_popcount(a); -#else - a = a - ((a >> 1) & 0x55555555); - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); - return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; -#endif -} - -// Find the sequence comparison of a and b, this is the distance -// between a and b ignoring overflow -static inline int lfs_scmp(uint32_t a, uint32_t b) { - return (int)(unsigned)(a - b); -} - -// Convert between 32-bit little-endian and native order -static inline uint32_t lfs_fromle32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return __builtin_bswap32(a); -#else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | - (((uint8_t*)&a)[3] << 24); -#endif -} - -static inline uint32_t lfs_tole32(uint32_t a) { - return lfs_fromle32(a); -} - -// Convert between 32-bit big-endian and native order -static inline uint32_t lfs_frombe32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) - return __builtin_bswap32(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) - return a; -#else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); -#endif -} - -static inline uint32_t lfs_tobe32(uint32_t a) { - return lfs_frombe32(a); -} - -// Calculate CRC-32 with polynomial = 0x04c11db7 -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); - -// Allocate memory, only used if buffers are not provided to littlefs -// Note, memory must be 64-bit aligned -static inline void *lfs_malloc(size_t size) { -#ifndef LFS_NO_MALLOC - return malloc(size); -#else - (void)size; - return NULL; -#endif -} - -// Deallocate memory, only used if buffers are not provided to littlefs -static inline void lfs_free(void *p) { -#ifndef LFS_NO_MALLOC - free(p); -#else - (void)p; -#endif -} - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif -#endif diff --git a/lib/LITTLEFS/src/littlefs_api.c b/lib/LITTLEFS/src/littlefs_api.c deleted file mode 100644 index 4098487c..00000000 --- a/lib/LITTLEFS/src/littlefs_api.c +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file littlefs_api.c - * @brief Maps the HAL of esp_partition <-> littlefs - * @author Brian Pugh - */ - -#define ESP_LOCAL_LOG_LEVEL ESP_LOG_INFO - -#include "esp_log.h" -#include "esp_partition.h" -#include "esp_vfs.h" -#include "lfs.h" -#include "esp_littlefs.h" -#include "littlefs_api.h" - -static const char TAG[] = "esp_littlefs_api"; - -int littlefs_api_read(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size) { - esp_littlefs_t * efs = c->context; - size_t part_off = (block * c->block_size) + off; - esp_err_t err = esp_partition_read(efs->partition, part_off, buffer, size); - if (err) { - ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", part_off, size, err); - return LFS_ERR_IO; - } - return 0; -} - -int littlefs_api_prog(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size) { - esp_littlefs_t * efs = c->context; - size_t part_off = (block * c->block_size) + off; - esp_err_t err = esp_partition_write(efs->partition, part_off, buffer, size); - if (err) { - ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", part_off, size, err); - return LFS_ERR_IO; - } - return 0; -} - -int littlefs_api_erase(const struct lfs_config *c, lfs_block_t block) { - esp_littlefs_t * efs = c->context; - size_t part_off = block * c->block_size; - esp_err_t err = esp_partition_erase_range(efs->partition, part_off, c->block_size); - if (err) { - ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", part_off, c->block_size, err); - return LFS_ERR_IO; - } - return 0; - -} - -int littlefs_api_sync(const struct lfs_config *c) { - /* Unnecessary for esp-idf */ - return 0; -} - diff --git a/lib/LITTLEFS/src/littlefs_api.h b/lib/LITTLEFS/src/littlefs_api.h deleted file mode 100644 index c1b2adc9..00000000 --- a/lib/LITTLEFS/src/littlefs_api.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef ESP_LITTLEFS_API_H__ -#define ESP_LITTLEFS_API_H__ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "esp_vfs.h" -#include "esp_partition.h" -#include "lfs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief a file descriptor - * That's also a singly linked list used for keeping tracks of all opened file descriptor - * - * Shortcomings/potential issues of 32-bit hash (when CONFIG_LITTLEFS_USE_ONLY_HASH) listed here: - * * unlink - If a different file is open that generates a hash collision, it will report an - * error that it cannot unlink an open file. - * * rename - If a different file is open that generates a hash collision with - * src or dst, it will report an error that it cannot rename an open file. - * Potential consequences: - * 1. A file cannot be deleted while a collision-geneating file is open. - * Worst-case, if the other file is always open during the lifecycle - * of your app, it's collision file cannot be deleted, which in the - * worst-case could cause storage-capacity issues. - * 2. Same as (1), but for renames - */ -typedef struct _vfs_littlefs_file_t { - lfs_file_t file; - uint32_t hash; - struct _vfs_littlefs_file_t * next; /*!< Pointer to next file in Singly Linked List */ -#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH - char * path; -#endif -} vfs_littlefs_file_t; - -/** - * @brief littlefs definition structure - */ -typedef struct { - lfs_t *fs; /*!< Handle to the underlying littlefs */ - SemaphoreHandle_t lock; /*!< FS lock */ - const esp_partition_t* partition; /*!< The partition on which littlefs is located */ - char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */ - - struct lfs_config cfg; /*!< littlefs Mount configuration */ - - vfs_littlefs_file_t *file; /*!< Singly Linked List of files */ - - vfs_littlefs_file_t **cache; /*!< A cache of pointers to the opened files */ - uint16_t cache_size; /*!< The cache allocated size (in pointers) */ - uint16_t fd_count; /*!< The count of opened file descriptor used to speed up computation */ -} esp_littlefs_t; - -/** - * @brief Read a region in a block. - * - * Negative error codes are propogated to the user. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_read(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, void *buffer, lfs_size_t size); - -/** - * @brief Program a region in a block. - * - * The block must have previously been erased. - * Negative error codes are propogated to the user. - * May return LFS_ERR_CORRUPT if the block should be considered bad. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_prog(const struct lfs_config *c, lfs_block_t block, - lfs_off_t off, const void *buffer, lfs_size_t size); - -/** - * @brief Erase a block. - * - * A block must be erased before being programmed. - * The state of an erased block is undefined. - * Negative error codes are propogated to the user. - * May return LFS_ERR_CORRUPT if the block should be considered bad. - * @return errorcode. 0 on success. - */ -int littlefs_api_erase(const struct lfs_config *c, lfs_block_t block); - -/** - * @brief Sync the state of the underlying block device. - * - * Negative error codes are propogated to the user. - * - * @return errorcode. 0 on success. - */ -int littlefs_api_sync(const struct lfs_config *c); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/README b/lib/README deleted file mode 100644 index 6debab1e..00000000 --- a/lib/README +++ /dev/null @@ -1,46 +0,0 @@ - -This directory is intended for project specific (private) libraries. -PlatformIO will compile them to static libraries and link into executable file. - -The source code of each library should be placed in a an own separate directory -("lib/your_library_name/[here are source files]"). - -For example, see a structure of the following two libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -and a contents of `src/main.c`: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -PlatformIO Library Dependency Finder will find automatically dependent -libraries scanning project source files. - -More information about PlatformIO Library Dependency Finder -- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/TickerScheduler/README.md b/lib/TickerScheduler/README.md deleted file mode 100644 index 33f0182e..00000000 --- a/lib/TickerScheduler/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# TickerScheduler -Simple scheduler for ESP8266 Arduino based on Ticker - -### Initialization - -`TickerScheduler(uint size);` - -| Param | Description | -| --- | --- | -| size | Amount of task Tickers to initialize | - -**Example**: - -`TickerScheduler ts(5)` - - -### Add task - -`boolean add(uint i, uint32_t period, tscallback_t f, boolean shouldFireNow = false); ` - -| Param | Description | -| --- | --- | -| i | Task ID | -| period | Task execution period in ms | -| f | Task callback | -| shouldFireNow| `true` if you want to execute task right after first scheduler update or wait next scheduled start | - -**Returns**: - -`true` - task added sucessfully - -`false` - task was not added - -**Example**: - -`ts.add(0, 3000, sendData)` - -### Execute scheduler in `loop()` - -`ts.update()` - -### Remove task - -`boolean remove(uint i)` - -**Returns**: - -`true` - task removed sucessfully - -`false` - task was not removed - -| Param | Description | -| --- | --- | -| i | Task ID | - -### Enable/Disable task - -`boolean enable(uint i)` - -`boolean disable(uint i)` - -**Returns**: - -`true` - task enabled/disabled sucessfully - -`false` - task was not enabled/disabled - -| Param | Description | -| --- | --- | -| i | Task ID | - -### Enable / disable all tasks - -`void enableAll()` - -`void disableAll()` - -### TODO - -* Task callback parameters support -* ... diff --git a/lib/TickerScheduler/TickerScheduler.cpp b/lib/TickerScheduler/TickerScheduler.cpp deleted file mode 100644 index af47ffa1..00000000 --- a/lib/TickerScheduler/TickerScheduler.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "TickerScheduler.h" - -TickerScheduler::TickerScheduler(uint8_t size) -{ - this->items = new TickerSchedulerItem[size]; - this->size = size; -} - -TickerScheduler::~TickerScheduler() -{ - for (uint8_t i = 0; i < this->size; i++) - { - this->remove(i); - yield(); - } - - delete[] this->items; - this->items = NULL; - this->size = 0; -} - -void TickerScheduler::handleTickerFlag(bool * flag) -{ - if (!*flag) - *flag = true; -} - -void TickerScheduler::handleTicker(tscallback_t f, void * arg, bool * flag) -{ - if (*flag) - { - yield(); - *flag = false; - yield(); - f(arg); - yield(); - } -} - -bool TickerScheduler::add(uint8_t i, uint32_t period, tscallback_t f, void* arg, boolean shouldFireNow) -{ - if (i >= this->size || this->items[i].is_used) - return false; - - this->items[i].cb = f; - this->items[i].cb_arg = arg; - this->items[i].flag = shouldFireNow; - this->items[i].period = period; - this->items[i].is_used = true; - - enable(i); - - return true; -} - -bool TickerScheduler::remove(uint8_t i) -{ - if (i >= this->size || !this->items[i].is_used) - return false; - - this->items[i].is_used = false; - this->items[i].t.detach(); - this->items[i].flag = false; - this->items[i].cb = NULL; - - return true; -} - -bool TickerScheduler::disable(uint8_t i) -{ - if (i >= this->size || !this->items[i].is_used) - return false; - - this->items[i].t.detach(); - - return true; -} - -bool TickerScheduler::enable(uint8_t i) -{ - if (i >= this->size || !this->items[i].is_used) - return false; - - bool * flag = &this->items[i].flag; - this->items[i].t.attach_ms(this->items[i].period, TickerScheduler::handleTickerFlag, flag); - - return true; -} - -void TickerScheduler::disableAll() -{ - for (uint8_t i = 0; i < this->size; i++) - disable(i); -} - -void TickerScheduler::enableAll() -{ - for (uint8_t i = 0; i < this->size; i++) - enable(i); -} - -void TickerScheduler::update() -{ - for (uint8_t i = 0; i < this->size; i++) - { - if (this->items[i].is_used) - { - #ifdef ARDUINO_ARCH_AVR - this->items[i].t.Tick(); - #endif - - handleTicker(this->items[i].cb, this->items[i].cb_arg, &this->items[i].flag); - } - yield(); - } -} diff --git a/lib/TickerScheduler/TickerScheduler.h b/lib/TickerScheduler/TickerScheduler.h deleted file mode 100644 index 98ae1f05..00000000 --- a/lib/TickerScheduler/TickerScheduler.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef TICKERSCHEDULER_H -#define TICKERSCHEDULER_H - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "Arduino.h" -#endif -#include - - -#ifdef ARDUINO_ARCH_AVR -class Ticker -{ - typedef void(*ticker_callback_t)(bool*); - -private: - bool is_attached = false; - uint32_t period = 0; - uint32_t last_tick = 0; - ticker_callback_t callback; - bool *callback_argument; -public: - void Tick() - { - if (is_attached && millis() - last_tick >= period) - { - callback(callback_argument); - last_tick = millis(); - } - } - - void detach() - { - this->is_attached = true; - } - - template void attach_ms(uint32_t milliseconds, void(*callback)(TArg), TArg arg) - { - this->period = milliseconds; - this->callback = callback; - this->callback_argument = arg; - this->is_attached = true; - } -}; -#endif - -//#ifdef ARDUINO_ARCH_ESP8266 -#include -#include -//#endif - -void tickerFlagHandle(volatile bool * flag); - -#ifdef _GLIBCXX_FUNCTIONAL -typedef std::function tscallback_t; -#else -typedef void(*tscallback_t)(void*); -#endif - -struct TickerSchedulerItem -{ - Ticker t; - bool flag = false; - tscallback_t cb; - void * cb_arg; - uint32_t period; - volatile bool is_used = false; -}; - -class TickerScheduler -{ -private: - uint8_t size; - TickerSchedulerItem *items = NULL; - - void handleTicker(tscallback_t, void *, bool * flag); - static void handleTickerFlag(bool * flag); - -public: - TickerScheduler(uint8_t size); - ~TickerScheduler(); - - bool add(uint8_t i, uint32_t period, tscallback_t, void *, boolean shouldFireNow = false); - bool remove(uint8_t i); - bool enable(uint8_t i); - bool disable(uint8_t i); - void enableAll(); - void disableAll(); - void update(); -}; - -#endif diff --git a/lib/TickerScheduler/example/blink/blink/blink.ino b/lib/TickerScheduler/example/blink/blink/blink.ino deleted file mode 100644 index 49afc470..00000000 --- a/lib/TickerScheduler/example/blink/blink/blink.ino +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#define LED1 4 -#define LED2 5 - -TickerScheduler ts(2); - -void blink1() { - digitalWrite(LED1, !digitalRead(LED1)); -} - -void blink2() { - digitalWrite(LED2, !digitalRead(LED2)); -} - -void setup() { - pinMode(LED1, OUTPUT); - digitalWrite(LED1, LOW); - - pinMode(LED2, OUTPUT); - digitalWrite(LED2, LOW); - - ts.add(0, 1000, [&](void *) { blink1(); }, nullptr, true); - ts.add(1, 3000, [&](void *) { blink2(); }, nullptr, true); -} - -void loop() { - ts.update(); -} diff --git a/partitions_custom.csv b/partitions_custom.csv deleted file mode 100644 index 86202b53..00000000 --- a/partitions_custom.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -ota_0, app, ota_0, 0x10000, 0x1A0000 -ota_1, app, ota_1, , 0x1A0000 -otadata, data, ota, 0x350000, 0x2000 -nvs, data, nvs, , 0x6000 -data, data, spiffs, , 0xA8000 \ No newline at end of file diff --git a/platformio.ini b/platformio.ini deleted file mode 100644 index 7e349997..00000000 --- a/platformio.ini +++ /dev/null @@ -1,85 +0,0 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - -[platformio] -default_envs = esp8266 - -[common_env_data] -lib_deps_external = - ArduinoJson@5.* - Bounce2 - PubSubClient - DHT sensor library for ESPx - Adafruit BMP280 Library - Adafruit BME280 Library - DallasTemperature -lib_deps_internal = - ESP Async WebServer - GyverFilters - OneWire - -[env:esp8266_01_1m] -framework = arduino -board = esp01_1m -board_build.ldscript = eagle.flash.1m512.ld -platform = https://github.com/platformio/platform-espressif8266.git -lib_deps = - ${common_env_data.lib_deps_external} - ${common_env_data.lib_deps_internal} - ESPAsyncTCP - ESPAsyncUDP - EspSoftwareSerial -monitor_filters = esp8266_exception_decoder -upload_speed = 921600 -monitor_speed = 115200 -board_build.filesystem = littlefs -board_build.f_cpu = 160000000L - -[env:esp8266] -framework = arduino -board = nodemcuv2 -platform = https://github.com/platformio/platform-espressif8266.git -lib_deps = - ${common_env_data.lib_deps_external} - ${common_env_data.lib_deps_internal} - ESPAsyncTCP - ESPAsyncUDP - EspSoftwareSerial -monitor_filters = esp8266_exception_decoder -upload_speed = 921600 -monitor_speed = 115200 -board_build.filesystem = littlefs -board_build.f_cpu = 160000000L - -[env:esp32] -framework = arduino -board = esp32dev -platform = https://github.com/platformio/platform-espressif32.git -build_flags = ${env.build_flags} -D=${PIOENV} -extra_scripts = ./tools/littlefsbuilder.py -lib_deps = - ${common_env_data.lib_deps_external} - ${common_env_data.lib_deps_internal} - AsyncTCP - ESP32Servo - LITTLEFS -monitor_filters = esp32_exception_decoder -upload_speed = 921600 -monitor_speed = 115200 -board_build.filesystem = littlefs - -; build_type = debug -; build_flags = -; -DDEBUG_ESP_CORE -; -DDEBUG_ESP_WIFI -; -DDEBUG_ESP_HTTP_UPDATE -; -DDEBUG_ESP_UPDATER -; -DDEBUG_ESP_OTA -; -DDEBUG_ESP_OOM diff --git a/src/Bus/I2CScanner.cpp b/src/Bus/I2CScanner.cpp deleted file mode 100644 index 669b2903..00000000 --- a/src/Bus/I2CScanner.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "Bus/I2CScanner.h" -#include "Utils/PrintMessage.h" - -#include - -static const char* MODULE = "I2C"; - -I2CScanner::I2CScanner(String& out) : BusScanner(TAG_I2C, out, 2){}; - -void I2CScanner::init() { - Wire.begin(); -} - -boolean I2CScanner::syncScan() { - pm.info("scanning..."); - size_t cnt = 0; - for (uint8_t i = 8; i < 120; i++) { - Wire.beginTransmission(i); - if (Wire.endTransmission() == 0) { - pm.info("found: " + i); - addResult(i, i < 119); - cnt++; - } - } - return cnt; -} \ No newline at end of file diff --git a/src/Class/Button.cpp b/src/Class/Button.cpp deleted file mode 100644 index 04afbd77..00000000 --- a/src/Class/Button.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/Button.h" -Button1* myButton; \ No newline at end of file diff --git a/src/Class/CallBackTest.cpp b/src/Class/CallBackTest.cpp deleted file mode 100644 index 55252e5e..00000000 --- a/src/Class/CallBackTest.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "Class/CallBackTest.h" - -CallBackTest::CallBackTest(){}; - -void CallBackTest::loop() { - count++; - if (count > 5000) { - // Проверяем что переменная содержит указатель - не пустая не null - // и непосредственно вызываем то, на что это указывает - // просто пишем имя - без () - это указатель на фунецию. - // () - вызываем функцию - с пустым набором параметров - - if (_cb != NULL) { - _cb(); - } - //или ровно тоже самое - //if (_cb) _cb(); - - if (_pcb) { - if (_pcb("SomeTextValue")) { - Serial.println("Got true!"); - } else { - Serial.println("Got false!"); - } - } - - count = 0; - } -} - -//передаем внутрь класса функцию любую void функцию без агрументов -void CallBackTest::setCallback(AsyncActionCb cb) { - _cb = cb; -} - -//передаем внутрь класса функцию любую void функцию с аргументами -void CallBackTest::setCallback(AsyncParamActionCb pcb) { - _pcb = pcb; -} -//CallBackTest* CB; - -//CB->setCallback([]() { -// Serial.println("123"); -//}); -// -//CB->setCallback([](const String str) { -// Serial.println(str); -// return true; -//}); \ No newline at end of file diff --git a/src/Class/Input.cpp b/src/Class/Input.cpp deleted file mode 100644 index 43f4df0a..00000000 --- a/src/Class/Input.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/Input.h" -Input* myInput; \ No newline at end of file diff --git a/src/Class/LineParsing.cpp b/src/Class/LineParsing.cpp deleted file mode 100644 index d44476ef..00000000 --- a/src/Class/LineParsing.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Class/LineParsing.h" \ No newline at end of file diff --git a/src/Class/NotAsinc.cpp b/src/Class/NotAsinc.cpp deleted file mode 100644 index f1c8f5f9..00000000 --- a/src/Class/NotAsinc.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "Class/NotAsinc.h" - -NotAsinc::NotAsinc(uint8_t size) { - this->items = new NotAsincItem[size]; - this->size = size; -} - -NotAsinc::~NotAsinc() {} - -void NotAsinc::add(uint8_t i, NotAsincCb f, void* arg) { - this->items[i].cb = f; - this->items[i].cb_arg = arg; - this->items[i].is_used = true; -} - -void NotAsinc::loop() { - if (this->items[task].is_used) { - handle(this->items[task].cb, this->items[task].cb_arg); - task = 0; - } -} - -void NotAsinc::make(uint8_t task) { - this->task = task; -} - -void NotAsinc::handle(NotAsincCb f, void* arg) { - f(arg); -} -NotAsinc* myNotAsincActions; \ No newline at end of file diff --git a/src/Class/OutputModule.cpp b/src/Class/OutputModule.cpp deleted file mode 100644 index d0496426..00000000 --- a/src/Class/OutputModule.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/OutputModule.h" -OutputModule* myText; \ No newline at end of file diff --git a/src/Class/Pwm.cpp b/src/Class/Pwm.cpp deleted file mode 100644 index 5e2d6ce9..00000000 --- a/src/Class/Pwm.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/Pwm.h" -Pwm* myPwm; \ No newline at end of file diff --git a/src/Class/ScenarioClass.cpp b/src/Class/ScenarioClass.cpp deleted file mode 100644 index d066cc62..00000000 --- a/src/Class/ScenarioClass.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/ScenarioClass.h" -Scenario* myScenario; \ No newline at end of file diff --git a/src/Class/SensorAnalog.cpp b/src/Class/SensorAnalog.cpp deleted file mode 100644 index e1963f5a..00000000 --- a/src/Class/SensorAnalog.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/SensorAnalog.h" -SensorAnalog* mySensorAnalog; \ No newline at end of file diff --git a/src/Class/Switch.cpp b/src/Class/Switch.cpp deleted file mode 100644 index 9bbf7c4e..00000000 --- a/src/Class/Switch.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Class/Switch.h" -Switch* mySwitch; \ No newline at end of file diff --git a/src/Cmd.cpp b/src/Cmd.cpp deleted file mode 100644 index d631e1e8..00000000 --- a/src/Cmd.cpp +++ /dev/null @@ -1,524 +0,0 @@ -#include "Cmd.h" - -#include "Class/Button.h" -#include "Class/Input.h" -#include "Class/LineParsing.h" -#include "Class/OutputModule.h" -#include "Class/Pwm.h" -#include "Class/Switch.h" -//----------------------------- -#include "Class/NotAsinc.h" -#include "Global.h" -#include "Module/Terminal.h" -#include "Servo/Servos.h" - -static const char *MODULE = "Cmd"; - -Terminal *term = nullptr; - -boolean but[NUM_BUTTONS]; -Bounce *buttons = new Bounce[NUM_BUTTONS]; - -#ifdef ESP8266 -SoftwareSerial *mySerial = nullptr; -#else -HardwareSerial *mySerial = nullptr; -#endif - -void getData(); - -void cmd_init() { - sCmd.addCommand("button-out", buttonOut); - sCmd.addCommand("pwm-out", pwmOut); - sCmd.addCommand("button-in", buttonIn); - - sCmd.addCommand("input-digit", inputDigit); - sCmd.addCommand("input-time", inputTime); - sCmd.addCommand("output-text", textOut); - -#ifdef ANALOG_ENABLED - sCmd.addCommand("analog-adc", analogAdc); -#endif - - - - - - - - - sCmd.addCommand("timerStart", timerStart_); - sCmd.addCommand("timerStop", timerStop_); - -#ifdef LEVEL_ENABLED - sCmd.addCommand("levelPr", levelPr); - sCmd.addCommand("ultrasonicCm", ultrasonicCm); -#endif -#ifdef DALLAS_ENABLED - sCmd.addCommand("dallas", dallas); -#endif -#ifdef DHT_ENABLED - sCmd.addCommand("dhtT", dhtT); - sCmd.addCommand("dhtH", dhtH); - sCmd.addCommand("dhtPerception", dhtP); - sCmd.addCommand("dhtComfort", dhtC); - sCmd.addCommand("dhtDewpoint", dhtD); -#endif - -#ifdef BMP_ENABLED - sCmd.addCommand("bmp280T", bmp280T); - sCmd.addCommand("bmp280P", bmp280P); -#endif - -#ifdef BME_ENABLED - sCmd.addCommand("bme280T", bme280T); - sCmd.addCommand("bme280P", bme280P); - sCmd.addCommand("bme280H", bme280H); - sCmd.addCommand("bme280A", bme280A); -#endif - -#ifdef STEPPER_ENABLED - sCmd.addCommand("stepper", stepper); - sCmd.addCommand("stepperSet", stepperSet); -#endif - -#ifdef SERVO_ENABLED - sCmd.addCommand("servo", servo_); - sCmd.addCommand("servoSet", servoSet); -#endif - -#ifdef SERIAL_ENABLED - sCmd.addCommand("serialBegin", serialBegin); - sCmd.addCommand("serialWrite", serialWrite); - sCmd.addCommand("getData", getData); -#endif - -#ifdef LOGGING_ENABLED - sCmd.addCommand("logging", logging); -#endif - - sCmd.addCommand("mqtt", mqttOrderSend); - sCmd.addCommand("http", httpOrderSend); - -#ifdef PUSH_ENABLED - sCmd.addCommand("push", pushControl); -#endif - - sCmd.addCommand("firmwareUpdate", firmwareUpdate); - sCmd.addCommand("firmwareVersion", firmwareVersion); - - handle_time_init(); -} - -//==========================================Модуль кнопок=================================================== -//button-out light toggle Кнопки Свет 1 pin[12] inv[1] st[1] -//========================================================================================================== -void buttonOut() { - myButton = new Button1(); - myButton->update(); - String key = myButton->gkey(); - String pin = myButton->gpin(); - String inv = myButton->ginv(); - sCmd.addCommand(key.c_str(), buttonOutSet); - jsonWriteStr(configOptionJson, key + "_pin", pin); - jsonWriteStr(configOptionJson, key + "_inv", inv); - myButton->pinModeSet(); - myButton->pinStateSetDefault(); - myButton->pinStateSetInvDefault(); - myButton->clear(); -} - -void buttonOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - String pin = jsonReadStr(configOptionJson, key + "_pin"); - String inv = jsonReadStr(configOptionJson, key + "_inv"); - if (inv == "") { - myButton->pinChange(key, pin, state, true); - } else { - myButton->pinChange(key, pin, state, false); - } -} - -//==========================================Модуль управления ШИМ=================================================== -//pwm-out volume range Кнопки Свет 1 pin[12] st[500] -//================================================================================================================== -void pwmOut() { - myPwm = new Pwm(); - myPwm->update(); - String key = myPwm->gkey(); - String pin = myPwm->gpin(); - String inv = myPwm->ginv(); - sCmd.addCommand(key.c_str(), pwmOutSet); - jsonWriteStr(configOptionJson, key + "_pin", pin); - myPwm->pwmModeSet(); - myPwm->pwmStateSetDefault(); - myPwm->clear(); -} - -void pwmOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - String pin = jsonReadStr(configOptionJson, key + "_pin"); - myPwm->pwmChange(key, pin, state); -} - -//==========================================Модуль физических кнопок======================================== -//button-in switch1 toggle Кнопки Свет 1 pin[2] db[20] -//========================================================================================================== -void buttonIn() { - mySwitch = new Switch(); - mySwitch->update(); - String key = mySwitch->gkey(); - String pin = mySwitch->gpin(); - sCmd.addCommand(key.c_str(), buttonInSet); - mySwitch->init(); - mySwitch->switchStateSetDefault(); - mySwitch->clear(); -} - -void buttonInSet() { - String key = sCmd.order(); - String state = sCmd.next(); - mySwitch->switchChangeVirtual(key, state); -} - -//==========================================Модуль ввода цифровых значений================================== -//input-digit digit1 inputDigit Ввод Введите.цифру 4 st[60] -//========================================================================================================== -void inputDigit() { - myInput = new Input(); - myInput->update(); - String key = myInput->gkey(); - sCmd.addCommand(key.c_str(), inputDigitSet); - myInput->inputSetDefaultFloat(); - myInput->clear(); -} - -void inputDigitSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myInput->inputSetFloat(key, state); -} - -//==========================================Модуль ввода времени============================================ -//input-time time1 inputTime Ввод Введите.время 4 st[10-00-00] -//========================================================================================================== -void inputTime() { - myInput = new Input(); - myInput->update(); - String key = myInput->gkey(); - sCmd.addCommand(key.c_str(), inputTimeSet); - myInput->inputSetDefaultStr(); - myInput->clear(); -} - -void inputTimeSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myInput->inputSetStr(key, state); -} - -void handle_time_init() { - ts.add( - TIME, 1000, [&](void *) { - jsonWriteStr(configLiveJson, "time", timeNow->getTime()); - jsonWriteStr(configLiveJson, "timenow", timeNow->getTimeJson()); - //eventGen("timenow", ""); - }, - nullptr, true); -} - -//===============================================Модуль вывода текста============================================ -//output-text;id;anydata;Вывод;Сигнализация;order;st[Обнаружено.движение] -//=============================================================================================================== -void textOut() { - myText = new OutputModule(); - myText->update(); - String key = myText->gkey(); - sCmd.addCommand(key.c_str(), textOutSet); - myText->OutputModuleStateSetDefault(); - myText->clear(); -} - -void textOutSet() { - String key = sCmd.order(); - String state = sCmd.next(); - myText->OutputModuleChange(key, state); -} - -//void text() { -// String number = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String page_number = sCmd.next(); -// -// createWidget(widget_name, page_name, page_number, "anydata", "text" + number); -//} -// -//void textSet() { -// String number = sCmd.next(); -// String text = sCmd.next(); -// text.replace("_", " "); -// -// if (text.indexOf("-time") >= 0) { -// text.replace("-time", ""); -// text.replace("#", " "); -// text = text + " " + timeNow->getDateTimeDotFormated(); -// } -// -// jsonWriteStr(configLiveJson, "text" + number, text); -// MqttClient::publishStatus("text" + number, text); -//} -//===================================================================================================================================== -//=========================================Модуль шагового мотора====================================================================== -#ifdef STEPPER_ENABLED -//stepper 1 12 13 -void stepper() { - String stepper_number = sCmd.next(); - String pin_step = sCmd.next(); - String pin_dir = sCmd.next(); - - jsonWriteStr(configOptionJson, "stepper" + stepper_number, pin_step + " " + pin_dir); - pinMode(pin_step.toInt(), OUTPUT); - pinMode(pin_dir.toInt(), OUTPUT); -} - -//stepperSet 1 100 5 -void stepperSet() { - String stepper_number = sCmd.next(); - String steps = sCmd.next(); - jsonWriteStr(configOptionJson, "steps" + stepper_number, steps); - String stepper_speed = sCmd.next(); - String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper" + stepper_number), " "); - String pin_dir = deleteBeforeDelimiter(jsonReadStr(configOptionJson, "stepper" + stepper_number), " "); - Serial.println(pin_step); - Serial.println(pin_dir); - if (steps.toInt() > 0) digitalWrite(pin_dir.toInt(), HIGH); - if (steps.toInt() < 0) digitalWrite(pin_dir.toInt(), LOW); - if (stepper_number == "1") { - ts.add( - STEPPER1, stepper_speed.toInt(), [&](void *) { - int steps_int = abs(jsonReadInt(configOptionJson, "steps1") * 2); - static int count; - count++; - String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper1"), " "); - digitalWrite(pin_step.toInt(), !digitalRead(pin_step.toInt())); - yield(); - if (count > steps_int) { - digitalWrite(pin_step.toInt(), LOW); - ts.remove(STEPPER1); - count = 0; - } - }, - nullptr, true); - } - if (stepper_number == "2") { - ts.add( - STEPPER2, stepper_speed.toInt(), [&](void *) { - int steps_int = abs(jsonReadInt(configOptionJson, "steps2") * 2); - static int count; - count++; - String pin_step = selectToMarker(jsonReadStr(configOptionJson, "stepper2"), " "); - digitalWrite(pin_step.toInt(), !digitalRead(pin_step.toInt())); - yield(); - if (count > steps_int) { - digitalWrite(pin_step.toInt(), LOW); - ts.remove(STEPPER2); - count = 0; - } - }, - nullptr, true); - } -} -#endif -//==================================================================================================================================================== -//=================================================================Сервоприводы======================================================================= -#ifdef SERVO_ENABLED -//servo 1 13 50 Мой#сервопривод Сервоприводы 0 100 0 180 2 -void servo_() { - String number = sCmd.next(); - uint8_t pin = String(sCmd.next()).toInt(); - int value = String(sCmd.next()).toInt(); - - String widget = sCmd.next(); - String page = sCmd.next(); - - int min_value = String(sCmd.next()).toInt(); - int max_value = String(sCmd.next()).toInt(); - int min_deg = String(sCmd.next()).toInt(); - int max_deg = String(sCmd.next()).toInt(); - - String pageNumber = sCmd.next(); - - jsonWriteStr(configOptionJson, "servo_pin" + number, String(pin, DEC)); - - value = map(value, min_value, max_value, min_deg, max_deg); - - Servo *servo = myServo.create(number.toInt(), pin); - servo->write(value); -#ifdef ESP32 - myServo1.attach(servo_pin.toInt(), 500, 2400); - myServo1.write(start_state_int); -#endif - - jsonWriteInt(configOptionJson, "s_min_val" + number, min_value); - jsonWriteInt(configOptionJson, "s_max_val" + number, max_value); - jsonWriteInt(configOptionJson, "s_min_deg" + number, min_deg); - jsonWriteInt(configOptionJson, "s_max_deg" + number, max_deg); - - jsonWriteInt(configLiveJson, "servo" + number, value); - - createWidgetParam(widget, page, pageNumber, "range", "servo" + number, "min", String(min_value), "max", String(max_value), "k", "1"); -} - -void servoSet() { - String number = sCmd.next(); - int value = String(sCmd.next()).toInt(); - - value = map(value, - jsonReadInt(configOptionJson, "s_min_val" + number), - jsonReadInt(configOptionJson, "s_max_val" + number), - jsonReadInt(configOptionJson, "s_min_deg" + number), - jsonReadInt(configOptionJson, "s_max_deg" + number)); - - Servo *servo = myServo.get(number.toInt()); - if (servo) { - servo->write(value); - } - - eventGen("servo", number); - jsonWriteInt(configLiveJson, "servo" + number, value); - MqttClient::publishStatus("servo" + number, String(value, DEC)); -} -#endif -//==================================================================================================================================================== -//=============================================================Модуль сериал порта======================================================================= - -#ifdef SERIAL_ENABLED -void serialBegin() { - String s_speed = sCmd.next(); - String rxPin = sCmd.next(); - String txPin = sCmd.next(); - - if (mySerial) { - delete mySerial; - } - -#ifdef ESP8266 - mySerial = new SoftwareSerial(rxPin.toInt(), txPin.toInt()); - mySerial->begin(s_speed.toInt()); -#else - mySerial = new HardwareSerial(2); - mySerial->begin(rxPin.toInt(), txPin.toInt()); -#endif - - term = new Terminal(mySerial); - term->setEOL(LF); - term->enableColors(false); - term->enableControlCodes(false); - term->enableEcho(false); - term->setOnReadLine([](const char *str) { - String line = String(str); - addCommandLoop(line); - }); -} - -void getData() { - String param = sCmd.next(); - String res = param.length() ? jsonReadStr(configLiveJson, param) : configLiveJson; - if (term) { - term->println(res.c_str()); - } -} - -void serialWrite() { - String payload = sCmd.next(); - if (term) { - term->println(payload.c_str()); - } -} -#endif -//==================================================================================================================================================== -//=================================================Глобальные команды удаленного управления=========================================================== - -void mqttOrderSend() { - String id = sCmd.next(); - String order = sCmd.next(); - - String all_line = jsonReadStr(configSetupJson, "mqttPrefix") + "/" + id + "/order"; - mqtt.publish(all_line.c_str(), order.c_str(), false); -} - -void httpOrderSend() { - String ip = sCmd.next(); - String order = sCmd.next(); - order.replace("_", "%20"); - String url = "http://" + ip + "/cmd?command=" + order; - getURL(url); -} - -void firmwareUpdate() { - myNotAsincActions->make(do_UPGRADE); -} - -void firmwareVersion() { - String widget = sCmd.next(); - String page = sCmd.next(); - String pageNumber = sCmd.next(); - - jsonWriteStr(configLiveJson, "firmver", FIRMWARE_VERSION); - createWidget(widget, page, pageNumber, "anydata", "firmver"); -} - -void addCommandLoop(const String &cmdStr) { - orderBuf += cmdStr; - if (!cmdStr.endsWith(",")) { - orderBuf += ","; - } -} - -void fileExecute(const String &filename) { - String cmdStr = readFile(filename, 2048); - csvExecute(cmdStr); -} - -void csvExecute(String &cmdStr) { - cmdStr.replace(";", " "); - cmdStr += "\r\n"; - cmdStr.replace("\r\n", "\n"); - cmdStr.replace("\r", "\n"); - int count = 0; - while (cmdStr.length()) { - String buf = selectToMarker(cmdStr, "\n"); - count++; - if (count > 1) sCmd.readStr(buf); - cmdStr = deleteBeforeDelimiter(cmdStr, "\n"); - } -} - -void spaceExecute(String &cmdStr) { - cmdStr += "\r\n"; - cmdStr.replace("\r\n", "\n"); - cmdStr.replace("\r", "\n"); - while (cmdStr.length()) { - String buf = selectToMarker(cmdStr, "\n"); - sCmd.readStr(buf); - cmdStr = deleteBeforeDelimiter(cmdStr, "\n"); - } -} - -void loopCmd() { - if (orderBuf.length()) { - String tmp = selectToMarker(orderBuf, ","); //выделяем первую команду rel 5 1, - pm.info("do: " + tmp); - sCmd.readStr(tmp); //выполняем - orderBuf = deleteBeforeDelimiter(orderBuf, ","); //осекаем - } -} - -void loopSerial() { - if (term) { - term->loop(); - } -} diff --git a/src/FSEditor.cpp b/src/FSEditor.cpp deleted file mode 100644 index bdaa5129..00000000 --- a/src/FSEditor.cpp +++ /dev/null @@ -1,326 +0,0 @@ -#include "FSEditor.h" - -#ifdef ESP32 -#include "LITTLEFS.h" -#define LittleFS LITTLEFS -#endif -#ifdef ESP8266 -#include -#endif - -#define FS_MAXLENGTH_FILEPATH 32 - -const char *excludeListFile = "/.exclude.files"; - -typedef struct ExcludeListS { - char *item; - ExcludeListS *next; -} ExcludeList; - -static ExcludeList *excludes = NULL; - -static bool matchWild(const char *pattern, const char *testee) { - const char *nxPat = NULL, *nxTst = NULL; - - while (*testee) { - if ((*pattern == '?') || (*pattern == *testee)) { - pattern++; - testee++; - continue; - } - if (*pattern == '*') { - nxPat = pattern++; - nxTst = testee; - continue; - } - if (nxPat) { - pattern = nxPat + 1; - testee = ++nxTst; - continue; - } - return false; - } - while (*pattern == '*') { - pattern++; - } - return (*pattern == 0); -} - -static bool addExclude(const char *item) { - size_t len = strlen(item); - if (!len) { - return false; - } - ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList)); - if (!e) { - return false; - } - e->item = (char *)malloc(len + 1); - if (!e->item) { - free(e); - return false; - } - memcpy(e->item, item, len + 1); - e->next = excludes; - excludes = e; - return true; -} - -static void loadExcludeList(fs::FS &_fs, const char *filename) { - static char linebuf[FS_MAXLENGTH_FILEPATH]; - fs::File excludeFile = _fs.open(filename, "r"); - if (!excludeFile) { - return; - } - if (excludeFile.isDirectory()) { - excludeFile.close(); - return; - } - if (excludeFile.size() > 0) { - uint8_t idx; - bool isOverflowed = false; - while (excludeFile.available()) { - linebuf[0] = '\0'; - idx = 0; - int lastChar; - do { - lastChar = excludeFile.read(); - if (lastChar != '\r') { - linebuf[idx++] = (char)lastChar; - } - } while ((lastChar >= 0) && (lastChar != '\n') && (idx < FS_MAXLENGTH_FILEPATH)); - - if (isOverflowed) { - isOverflowed = (lastChar != '\n'); - continue; - } - isOverflowed = (idx >= FS_MAXLENGTH_FILEPATH); - linebuf[idx - 1] = '\0'; - if (!addExclude(linebuf)) { - excludeFile.close(); - return; - } - } - } - excludeFile.close(); -} - -static bool isExcluded(fs::FS &_fs, const char *filename) { - if (excludes == NULL) { - loadExcludeList(_fs, excludeListFile); - } - if (strcmp(excludeListFile, filename) == 0) return true; - ExcludeList *e = excludes; - while (e) { - if (matchWild(e->item, filename)) { - return true; - } - e = e->next; - } - return false; -} - -// WEB HANDLER IMPLEMENTATION - -#ifdef ESP32 -FSEditor::FSEditor(const fs::FS &fs, const String &username, const String &password) -#else -FSEditor::FSEditor(const String &username, const String &password, const fs::FS &fs) -#endif - : _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) { -} - -bool FSEditor::canHandle(AsyncWebServerRequest *request) { - if (request->url().equalsIgnoreCase("/edit")) { - if (request->method() == HTTP_GET) { - if (request->hasParam("list")) - return true; - if (request->hasParam("edit")) { - request->_tempFile = _fs.open(request->arg("edit"), "r"); - if (!request->_tempFile) { - return false; - } - if (request->_tempFile.isDirectory()) { - request->_tempFile.close(); - return false; - } - } - if (request->hasParam("download")) { - request->_tempFile = _fs.open(request->arg("download"), "r"); - if (!request->_tempFile) { - return false; - } - if (request->_tempFile.isDirectory()) { - request->_tempFile.close(); - return false; - } - } - request->addInterestingHeader("If-Modified-Since"); - return true; - } else if (request->method() == HTTP_POST) - return true; - else if (request->method() == HTTP_DELETE) - return true; - else if (request->method() == HTTP_PUT) - return true; - } - return false; -} - -void FSEditor::handleRequest(AsyncWebServerRequest *request) { - if (_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); - - if (request->method() == HTTP_GET) { - if (request->hasParam("list")) { - String path = request->getParam("list")->value(); -#ifdef ESP32 - File dir = _fs.open(path); -#else - fs::Dir dir = _fs.openDir(path); -#endif - path = String(); - String output = "["; -#ifdef ESP32 - File entry = dir.openNextFile(); - while (entry) { -#else - while (dir.next()) { - fs::File entry = dir.openFile("r"); -#endif - String fname = entry.fullName(); - if (fname.charAt(0) != '/') fname = "/" + fname; - - if (isExcluded(_fs, fname.c_str())) { -#ifdef ESP32 - entry = dir.openNextFile(); -#endif - continue; - } - if (output != "[") output += ','; - output += "{\"type\":\""; - output += "file"; - output += "\",\"name\":\""; - output += String(fname); - output += "\",\"size\":"; - output += String(entry.size()); - output += "}"; -#ifdef ESP32 - entry = dir.openNextFile(); -#else - entry.close(); -#endif - } -#ifdef ESP32 - dir.close(); -#endif - output += "]"; - request->send(200, "application/json", output); - output = String(); - } else if (request->hasParam("edit") || request->hasParam("download")) { - request->send(request->_tempFile, request->_tempFile.fullName(), String(), request->hasParam("download")); - } else { - const char *buildTime = __DATE__ " " __TIME__ " GMT"; - if (request->header("If-Modified-Since").equals(buildTime)) { - request->send(304); - } else { - AsyncWebServerResponse *response = request->beginResponse(LittleFS, "/edit.htm", "text/html"); - // response->addHeader("Content-Encoding", "gzip"); - response->addHeader("Last-Modified", buildTime); - request->send(response); - } - } - } else if (request->method() == HTTP_DELETE) { - if (request->hasParam("path", true)) { - if (!(_fs.remove(request->getParam("path", true)->value()))) { -#ifdef ESP32 - _fs.rmdir(request->getParam("path", true)->value()); // try rmdir for littlefs -#endif - } - - request->send(200, "", "DELETE: " + request->getParam("path", true)->value()); - } else - request->send(404); - } else if (request->method() == HTTP_POST) { - if (request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value())) - request->send(200, "", "UPLOADED: " + request->getParam("data", true, true)->value()); - - else if (request->hasParam("rawname", true) && request->hasParam("raw0", true)) { - String rawnam = request->getParam("rawname", true)->value(); - - if (_fs.exists(rawnam)) _fs.remove(rawnam); // delete it to allow a mode - - int k = 0; - uint16_t i = 0; - fs::File f = _fs.open(rawnam, "a"); - - while (request->hasParam("raw" + String(k), true)) { //raw0 .. raw1 - if (f) { - i += f.print(request->getParam("raw" + String(k), true)->value()); - } - k++; - } - f.close(); - request->send(200, "", "IPADWRITE: " + rawnam + ":" + String(i)); - - } else { - request->send(500); - } - - } else if (request->method() == HTTP_PUT) { - if (request->hasParam("path", true)) { - String filename = request->getParam("path", true)->value(); - if (_fs.exists(filename)) { - request->send(200); - } else { -/*******************************************************/ -#ifdef ESP32 - if (strchr(filename.c_str(), '/')) { - // For file creation, silently make subdirs as needed. If any fail, - // it will be caught by the real file open later on - char *pathStr = strdup(filename.c_str()); - if (pathStr) { - // Make dirs up to the final fnamepart - char *ptr = strchr(pathStr, '/'); - while (ptr) { - *ptr = 0; - _fs.mkdir(pathStr); - *ptr = '/'; - ptr = strchr(ptr + 1, '/'); - } - } - free(pathStr); - } -#endif - /*******************************************************/ - fs::File f = _fs.open(filename, "w"); - if (f) { - f.write((uint8_t)0x00); - f.close(); - request->send(200, "", "CREATE: " + filename); - } else { - request->send(500); - } - } - } else - request->send(400); - } -} - -void FSEditor::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { - if (!index) { - if (!_username.length() || request->authenticate(_username.c_str(), _password.c_str())) { - _authenticated = true; - request->_tempFile = _fs.open(filename, "w"); - _startTime = millis(); - } - } - if (_authenticated && request->_tempFile) { - if (len) { - request->_tempFile.write(data, len); - } - if (final) { - request->_tempFile.close(); - } - } -} diff --git a/src/Global.cpp b/src/Global.cpp deleted file mode 100644 index 5298c961..00000000 --- a/src/Global.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "Global.h" - -#ifdef WS_enable -AsyncWebSocket ws; -//AsyncEventSource events; -#endif - -Clock* timeNow; - -TickerScheduler ts(TEST + 1); - -WiFiClient espClient; - -PubSubClient mqtt(espClient); - -StringCommand sCmd; - -AsyncWebServer server(80); - -OneWire *oneWire; - -DallasTemperature sensors; - -/* -* Global vars -*/ - -boolean just_load = true; - -// Json -String configSetupJson = "{}"; -String configLiveJson = "{}"; -String configOptionJson = "{}"; - -// Mqtt -String chipId = ""; -String prex = ""; - -String all_widgets = ""; - -String scenario = ""; - -//orders and events -String orderBuf = ""; -String eventBuf = ""; - -String itemsFile = ""; -String itemsLine = ""; - -// Sensors -String sensorReadingMap; - - - - - - -String analog_value_names_list; -int enter_to_analog_counter; - -String dallas_value_name; -int enter_to_dallas_counter; - -String levelPr_value_name; -String ultrasonicCm_value_name; - -String dhtT_value_name; -String dhtH_value_name; - -String bmp280T_value_name; -String bmp280P_value_name; - -String bme280T_value_name; -String bme280P_value_name; -String bme280H_value_name; -String bme280A_value_name; - -int sensors_reading_map[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// Logging -String logging_value_names_list; -int enter_to_logging_counter; - -// Scenario -int scenario_line_status[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - -String lastVersion = ""; - -// Async actions -boolean checkUpdatesFlag = false; -boolean updateFlag = false; - -boolean mqttParamsChanged = false; -boolean udp_data_parse = false; -boolean mqtt_send_settings_to_udp = false; - -BusScanner_t busToScan; -boolean busScanFlag = false; -boolean fsCheckFlag = false; -boolean delElementFlag = false; -boolean getJsonListFromCsvFlag = false; - -String csvFile = ""; -int colum; \ No newline at end of file diff --git a/src/Init.cpp b/src/Init.cpp deleted file mode 100644 index bbaa6f60..00000000 --- a/src/Init.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "Global.h" -#include "Init.h" -#include "Cmd.h" - - -void loadConfig() { - configSetupJson = readFile("config.json", 4096); - configSetupJson.replace(" ", ""); - configSetupJson.replace("\r\n", ""); - - jsonWriteStr(configSetupJson, "chipID", chipId); - jsonWriteStr(configSetupJson, "firmware_version", FIRMWARE_VERSION); - - prex = jsonReadStr(configSetupJson, "mqttPrefix") + "/" + chipId; - - Serial.println(configSetupJson); -} - -void all_init() { - Device_init(); - loadScenario(); - Timer_countdown_init(); -} - -void Device_init() { - - sensorReadingMap = ""; - - - - - - logging_value_names_list = ""; - - - - - - enter_to_logging_counter = LOG1 - 1; - analog_value_names_list = ""; - enter_to_analog_counter = 0; - dallas_value_name = ""; - enter_to_dallas_counter = 0; - levelPr_value_name = ""; - ultrasonicCm_value_name = ""; - dhtT_value_name = ""; - dhtH_value_name = ""; - bmp280T_value_name = ""; - bmp280P_value_name = ""; - bme280T_value_name = ""; - bme280P_value_name = ""; - bme280H_value_name = ""; - bme280A_value_name = ""; - - int array_sz = sizeof(sensors_reading_map) / sizeof(sensors_reading_map[0]); - for (int i = 0; i < array_sz; i++) { - sensors_reading_map[i] = 0; - } - - for (int i = LOG1; i <= LOG5; i++) { - ts.remove(i); - } - -#ifdef LAYOUT_IN_RAM - all_widgets = ""; -#else - removeFile(String("layout.txt")); -#endif - - - fileExecute(String(DEVICE_CONFIG_FILE)); - //outcoming_date(); -} -//-------------------------------сценарии----------------------------------------------------- - -void loadScenario() { - if (jsonReadStr(configSetupJson, "scen") == "1") { - scenario = readFile(String(DEVICE_SCENARIO_FILE), 2048); - } -} - -void uptime_init() { - ts.add( - UPTIME, 5000, [&](void*) { - handle_uptime(); - }, - nullptr, true); -} - -void telemetry_init() { - if (TELEMETRY_UPDATE_INTERVAL) { - ts.add( - STATISTICS, TELEMETRY_UPDATE_INTERVAL, [&](void*) { - handle_statistics(); - }, - nullptr, true); - } -} - -void handle_uptime() { - jsonWriteStr(configSetupJson, "uptime", timeNow->getUptime()); -} - -void handle_statistics() { - if (isNetworkActive()) { - String urls = "http://backup.privet.lv/visitors/?"; - //----------------------------------------------------------------- - urls += WiFi.macAddress().c_str(); - urls += "&"; - //----------------------------------------------------------------- -#ifdef ESP8266 - urls += "iot-manager_esp8266"; -#endif -#ifdef ESP32 - urls += "iot-manager_esp32"; -#endif - urls += "&"; -#ifdef ESP8266 - urls += ESP.getResetReason(); -#endif -#ifdef ESP32 - urls += "Power on"; -#endif - urls += "&"; - urls += String(FIRMWARE_VERSION); - String stat = getURL(urls); - } -} \ No newline at end of file diff --git a/src/ItemsList.cpp b/src/ItemsList.cpp deleted file mode 100644 index dcb194f7..00000000 --- a/src/ItemsList.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "ItemsList.h" - -#include "Utils\StringUtils.h" - -static const char* firstLine PROGMEM = "Тип элемента;Id;Виджет;Имя вкладки;Имя виджета;Позиция виджета"; - -void addItem(String name) { - String item = readFile("items/" + name + ".txt", 1024); - name = deleteToMarkerLast(name, "-"); - item.replace("id", name + String(getNewElementNumber("id.txt"))); - item.replace("order", String(getNewElementNumber("order.txt"))); - if (item.indexOf("pin-adc") != -1) { - item.replace("pin-adc", "pin[" + String(getFreePinAnalog()) + "]"); - } else if (item.indexOf("pin") != -1) { - item.replace("pin", "pin[" + String(getFreePinAll()) + "]"); - } - item.replace("\r\n", ""); - item.replace("\r", ""); - item.replace("\n", ""); - addFile(DEVICE_CONFIG_FILE, "\n" + item); -} - -void delAllItems() { - removeFile(DEVICE_CONFIG_FILE); - addFile(DEVICE_CONFIG_FILE, String(firstLine)); - removeFile("id.txt"); - removeFile("order.txt"); - removeFile("pins.txt"); -} - -uint8_t getNewElementNumber(String file) { - uint8_t number = readFile(file, 100).toInt(); - number++; - removeFile(file); - addFile(file, String(number)); - return number; -} - -uint8_t getFreePinAll() { -#ifdef ESP8266 - uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; -#endif -#ifdef ESP32 - uint8_t pins[] = {0, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5}; -#endif - uint8_t array_sz = sizeof(pins) / sizeof(pins[0]); - uint8_t i = getNewElementNumber("pins.txt"); - if (i < array_sz) { - return pins[i]; - } else { - return 0; - } -} - -uint8_t getFreePinAnalog() { -#ifdef ESP8266 - return 0; -#endif -} - -//void do_getJsonListFromCsv() { -// if (getJsonListFromCsvFlag) { -// getJsonListFromCsvFlag = false; -// removeFile("items/items.json"); -// addFile("items/items.json", getJsonListFromCsv(DEVICE_CONFIG_FILE, 1)); -// } -//} -// -//String getJsonListFromCsv(String csvFile, int colum) { -// File configFile = LittleFS.open("/" + csvFile, "r"); -// if (!configFile) { -// return "error"; -// } -// configFile.seek(0, SeekSet); -// -// String outJson = "{}"; -// -// int count = -1; -// -// while (configFile.position() != configFile.size()) { -// count++; -// String item = configFile.readStringUntil('\n'); -// if (count > 0) { -// String line = selectFromMarkerToMarker(item, ";", colum); -// jsonWriteStr(outJson, line, line); -// } -// } -// configFile.close(); -// csvFile = ""; -// return outJson; -//} -// -//void do_delElement() { -// if (delElementFlag) { -// delElementFlag = false; -// delElement(itemsFile, itemsLine); -// } -//} -// -//void delElement(String _itemsFile, String _itemsLine) { -// File configFile = LittleFS.open("/" + _itemsFile, "r"); -// if (!configFile) { -// return; -// } -// configFile.seek(0, SeekSet); -// String finalConf; -// int count = -1; -// while (configFile.position() != configFile.size()) { -// count++; -// String item = configFile.readStringUntil('\n'); -// Serial.print(_itemsLine); -// Serial.print(" "); -// Serial.println(count); -// if (count != _itemsLine.toInt()) { -// if (count == 0) { -// finalConf += item; -// } else { -// finalConf += "\n" + item; -// } -// } -// } -// removeFile(_itemsFile); -// addFile(_itemsFile, finalConf); -// Serial.println(finalConf); -// itemsFile = ""; -// itemsLine = ""; -// configFile.close(); -//} \ No newline at end of file diff --git a/src/Logging.cpp b/src/Logging.cpp deleted file mode 100644 index ab44bba6..00000000 --- a/src/Logging.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "Global.h" - -// -#include - -void sendLogData(String file, String topic); - -static const char* MODULE = "Log"; - -#ifdef LOGGING_ENABLED -//===============================================Логирование============================================================ -//logging temp1 1 10 Температура Датчики 2 -void logging() { - String value_name = sCmd.next(); - String period_min = sCmd.next(); - String maxCount = sCmd.next(); - String widget_name = sCmd.next(); - widget_name.replace("#", " "); - String page_name = sCmd.next(); - String page_number = sCmd.next(); - logging_value_names_list += value_name + ","; - enter_to_logging_counter++; //считаем количество входов в эту функцию - jsonWriteStr(configOptionJson, value_name + "_c", maxCount); //создаем в файловой системе переменную количества точек на графике с отметкой _c что значит count - - //создаем график в приложении с топиком _ch /prefix/3234045-1589487/value_name_ch - createChart(widget_name, page_name, page_number, "chart", value_name + "_ch", maxCount); - - if (enter_to_logging_counter == LOG1) { - ts.add( - LOG1, period_min.toInt() * 1000 * 60, [&](void*) { - String tmp_buf_1 = selectFromMarkerToMarker(logging_value_names_list, ",", 0); - deleteOldDate("log." + tmp_buf_1 + ".txt", jsonReadInt(configOptionJson, tmp_buf_1 + "_c"), jsonReadStr(configLiveJson, tmp_buf_1)); - pm.info("logging for " + tmp_buf_1 + " done"); - }, - nullptr, false); - } - if (enter_to_logging_counter == LOG2) { - ts.add( - LOG2, period_min.toInt() * 1000 * 60, [&](void*) { - String tmp_buf_2 = selectFromMarkerToMarker(logging_value_names_list, ",", 1); - deleteOldDate("log." + tmp_buf_2 + ".txt", jsonReadInt(configOptionJson, tmp_buf_2 + "_c"), jsonReadStr(configLiveJson, tmp_buf_2)); - pm.info("logging for " + tmp_buf_2 + " done"); - }, - nullptr, false); - } - if (enter_to_logging_counter == LOG3) { - ts.add( - LOG3, period_min.toInt() * 1000 * 60, [&](void*) { - String tmp_buf_3 = selectFromMarkerToMarker(logging_value_names_list, ",", 2); - deleteOldDate("log." + tmp_buf_3 + ".txt", jsonReadInt(configOptionJson, tmp_buf_3 + "_c"), jsonReadStr(configLiveJson, tmp_buf_3)); - pm.info("logging for " + tmp_buf_3 + " done"); - }, - nullptr, false); - } - if (enter_to_logging_counter == LOG4) { - ts.add( - LOG4, period_min.toInt() * 1000 * 60, [&](void*) { - String tmp_buf_4 = selectFromMarkerToMarker(logging_value_names_list, ",", 3); - deleteOldDate("log." + tmp_buf_4 + ".txt", jsonReadInt(configOptionJson, tmp_buf_4 + "_c"), jsonReadStr(configLiveJson, tmp_buf_4)); - pm.info("logging for " + tmp_buf_4 + " done"); - }, - nullptr, false); - } - if (enter_to_logging_counter == LOG5) { - ts.add( - LOG5, period_min.toInt() * 1000 * 60, [&](void*) { - String tmp_buf_5 = selectFromMarkerToMarker(logging_value_names_list, ",", 4); - deleteOldDate("log." + tmp_buf_5 + ".txt", jsonReadInt(configOptionJson, tmp_buf_5 + "_c"), jsonReadStr(configLiveJson, tmp_buf_5)); - pm.info("logging for " + tmp_buf_5 + " done"); - }, - nullptr, false); - } -} - -/* -* Удаление стрых данных и запись новых -*/ -void deleteOldDate(const String filename, size_t max_lines_cnt, String payload) { - String log_date = readFile(filename, 5120); - size_t lines_cnt = itemsCount(log_date, "\r\n"); - - pm.info("log " + filename + " (" + String(lines_cnt, DEC) + ")"); - - if ((lines_cnt > max_lines_cnt + 1) || !lines_cnt) { - removeFile(filename); - lines_cnt = 0; - } - - if (lines_cnt > max_lines_cnt) { - log_date = deleteBeforeDelimiter(log_date, "\r\n"); - if (timeNow->hasTimeSynced()) { - log_date += timeNow->getTimeUnix() + " " + payload + "\r\n"; - writeFile(filename, log_date); - } - } else { - if (timeNow->hasTimeSynced()) { - addFileLn(filename, timeNow->getTimeUnix() + " " + payload); - } - } -} - -//=========================================Выбор какие данные отправлять================================================================== -void choose_log_date_and_send() { - String all_line = logging_value_names_list; - while (all_line.length() != 0) { - String tmp = selectToMarker(all_line, ","); - sendLogData("log." + tmp + ".txt", tmp + "_ch"); - all_line = deleteBeforeDelimiter(all_line, ","); - } -} -//=========================================Отправка данных=================================================================================== -void sendLogData(String file, String topic) { - String log_date = readFile(file, 5120); - if (log_date != "failed") { - log_date.replace("\r\n", "\n"); - log_date.replace("\r", "\n"); - String buf = "{}"; - String json_array; - String unix_time; - String value; - while (log_date.length()) { - String tmp = selectToMarker(log_date, "\n"); - log_date = deleteBeforeDelimiter(log_date, "\n"); - unix_time = selectToMarker(tmp, " "); - jsonWriteInt(buf, "x", unix_time.toInt()); - value = deleteBeforeDelimiter(tmp, " "); - jsonWriteFloat(buf, "y1", value.toFloat()); - if (log_date.length() < 3) { - json_array += buf; - } else { - json_array += buf + ","; - } - buf = "{}"; - } - unix_time = ""; - value = ""; - log_date = ""; - json_array = "{\"status\":[" + json_array + "]}"; - pm.info(json_array); - - MqttClient::publishChart(topic, json_array); - } -} - -void clean_log_date() { - String all_line = logging_value_names_list; - while (all_line.length()) { - String tmp = selectToMarker(all_line, ","); - removeFile("log." + tmp + ".txt"); - all_line = deleteBeforeDelimiter(all_line, ","); - } -} -#endif \ No newline at end of file diff --git a/src/Module/Telnet.cpp b/src/Module/Telnet.cpp deleted file mode 100644 index d8bec643..00000000 --- a/src/Module/Telnet.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "Module/Telnet.h" - -bool Telnet::onInit() { - _server = new WiFiServer(_port); - _term = new Terminal(); - _term->enableControlCodes(); - _term->enableEcho(false); - _term->setStream(&_client); - return true; -} - -void Telnet::onEnd() { - delete _server; -} - -bool Telnet::onStart() { - _server->begin(); - _server->setNoDelay(true); - return true; -} - -void Telnet::onStop() { - if (hasClient()) { - _client.stop(); - } - _server->stop(); -} - -bool Telnet::hasClient() { return _client.connected(); } - -void Telnet::sendData(const String& data) { - if (hasClient()) { - _client.write(data.c_str()); - } -} - -void Telnet::setCommandShell(CommandShell* shell) { - _shell = shell; - _shell->setTerm(_term); -} - -void Telnet::setEventHandler(TelnetEventHandler h) { _eventHandler = h; } - -void Telnet::onLoop() { - if (_server->hasClient()) { - if (!_client) { - _client = _server->available(); - } else { - if (!_client.connected()) { - _server->stop(); - _client = _server->available(); - } else { - WiFiClient rejected; - rejected = _server->available(); - rejected.stop(); - } - } - } - - if (_lastConnected != hasClient()) { - _lastConnected = hasClient(); - if (_lastConnected) { - onConnect(); - } else { - onDisconnect(); - } - } - - if (hasClient() && _shell != nullptr) _shell->loop(); -} - -bool Telnet::isShellActive() { - return _shell->active(); -} - -void Telnet::onConnect() { - if (_eventHandler) { - _eventHandler(TE_CONNECTED, &_client); - } -} - -void Telnet::onDisconnect() { - if (_eventHandler) { - _eventHandler(TE_DISCONNECTED, nullptr); - } -} diff --git a/src/Module/Terminal.cpp b/src/Module/Terminal.cpp deleted file mode 100644 index 38c03b97..00000000 --- a/src/Module/Terminal.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "Module/Terminal.h" - -#include "Utils/TimeUtils.h" - -#define INPUT_MAX_LENGHT 255 - -Terminal::Terminal(Stream *stream) : _stream{stream}, - _line(INPUT_MAX_LENGHT), - _cc_pos(0), - _color(false), - _controlCodes(false), - _echo(false), - _eol(CRLF) { state = ST_NORMAL; }; - -void Terminal::setStream(Stream *stream) { - _stream = stream; -} - -void Terminal::setOnReadLine(TerminalInputEventHandler h) { inputHandler_ = h; } - -void Terminal::setOnEvent(TerminalEventHandler h) { eventHandler_ = h; } - -bool Terminal::available() { - return _stream != nullptr ? _stream->available() : false; -} - -void Terminal::setEOL(EOLType_t eol) { - _eol = eol; -} - -void Terminal::enableEcho(bool enabled) { - _echo = enabled; -} - -void Terminal::enableColors(bool enabled) { - _color = enabled; -} - -void Terminal::enableControlCodes(bool enabled) { - _controlCodes = enabled; -} - -void Terminal::quit() {} - -void Terminal::loop() { - if (_stream == nullptr || !_stream->available()) return; - - byte moveX = 0; - byte moveY = 0; - - char c = _stream->read(); - - _lastReceived = millis(); - - if (state == ST_INACTIVE) { - // wait for CR - if (c == CHAR_CR) { - if (eventHandler_) { - eventHandler_(EVENT_OPEN, _stream); - state = ST_NORMAL; - } - } - // or ignore all other - return; - } - - if (c == CHAR_LF || c == CHAR_NULL || c == CHAR_BIN) - return; - - // Esc - if (c == CHAR_ESC || c == 195) { - state = ST_ESC_SEQ; - _cc_pos = 0; - for (size_t i = 0; i < 2; ++i) { - bool timeout = false; - while (!_stream->available() && - !(timeout = millis_since(_lastReceived) > 10)) { - delay(0); - } - if (timeout) { - state = ST_NORMAL; - break; - } - _lastReceived = millis(); - c = _stream->read(); - _cc_buf[_cc_pos] = c; - if ((c == '[') || ((c >= 'A' && c <= 'Z') || c == '~')) { - _cc_pos++; - _cc_buf[++_cc_pos] = '\x00'; - } - } - uint8_t i; - for (i = 0; i < 10; ++i) { - if (strcmp(_cc_buf, keyMap[i].cc) == 0) { - c = keyMap[i].ch; - state = ST_NORMAL; - } - } - } - - if (state == ST_ESC_SEQ) { - state = ST_NORMAL; - return; - } - - // WHEN NORMAL - if (state == ST_NORMAL) { - if (c == CHAR_ESC) { - if (!_line.available()) { - // QUIT - state = ST_INACTIVE; - if (eventHandler_) - eventHandler_(EVENT_CLOSE, _stream); - } else { - // CLEAR - _line.clear(); - if (_controlCodes) { - clear_line(); - } else { - println(); - } - } - return; - } - - switch (c) { - case CHAR_CR: - println(); - if (inputHandler_) - inputHandler_(_line.c_str()); - _line.clear(); - moveY++; - break; - case CHAR_TAB: - if (eventHandler_) - eventHandler_(EVENT_TAB, _stream); - return; - case KEY_LEFT: - if (_line.prev()) - moveX--; - break; - case KEY_RIGHT: - if (_line.next()) - moveX++; - break; - case KEY_HOME: - moveX = -1 * _line.home(); - break; - case KEY_END: - moveX = _line.end(); - break; - case CHAR_BS: - case KEY_DEL: - if (_line.backspace()) { - backsp(); - moveX--; - } - break; - default: - // printable ascii 7bit or printable 8bit ISO8859 - if ((c & '\x7F') >= 32 && (c & '\x7F') < 127) - if (_line.write(c)) { - if (_echo) write(c); - moveX++; - } - break; - } - - // if (controlCodesEnabled) - // move(startY + moveY, startX + moveX); - } -} - -bool Terminal::setLine(const uint8_t *ptr, size_t size) { - _line.clear(); - if (_line.write(ptr, size)) - print(_line.c_str()); - return true; -} - -CharBuffer &Terminal::getLine() { return _line; } - -void Terminal::start() { - if (_controlCodes) initscr(); - println(); -} - -void Terminal::initscr() { - write_P(SEQ_LOAD_G1); - attrset(A_NORMAL); - move(0, 0); - clear(); -} - -void Terminal::attrset(const uint16_t attr) { - uint8_t i; - - if (attr != this->attr) { - this->write_P(SEQ_ATTRSET); - - i = (attr & F_COLOR) >> 8; - - if (i >= 1 && i <= 8) { - this->write_P(SEQ_ATTRSET_FCOLOR); - this->write(i - 1 + '0'); - } - - i = (attr & B_COLOR) >> 12; - - if (i >= 1 && i <= 8) { - this->write_P(SEQ_ATTRSET_BCOLOR); - this->write(i - 1 + '0'); - } - - if (attr & A_REVERSE) - this->write_P(SEQ_ATTRSET_REVERSE); - if (attr & A_UNDERLINE) - this->write_P(SEQ_ATTRSET_UNDERLINE); - if (attr & A_BLINK) - this->write_P(SEQ_ATTRSET_BLINK); - if (attr & A_BOLD) - this->write_P(SEQ_ATTRSET_BOLD); - if (attr & A_DIM) - this->write_P(SEQ_ATTRSET_DIM); - this->write('m'); - this->attr = attr; - } -} - -void Terminal::clear() { write_P(SEQ_CLEAR); } - -void Terminal::clear_line() { - write(CHAR_CR); - write_P(ESC_CLEAR_EOL); -} - -void Terminal::move(uint8_t y, uint8_t x) { - write_P(SEQ_CSI); - writeByDigit(y + 1); - write(';'); - writeByDigit(x + 1); - write('H'); - curY = y; - curX = x; -} - -void Terminal::writeByDigit(uint8_t i) { - uint8_t ii; - if (i >= 10) { - if (i >= 100) { - ii = i / 100; - write(ii + '0'); - i -= 100 * ii; - } - ii = i / 10; - write(ii + '0'); - i -= 10 * ii; - } - write(i + '0'); -} - -void Terminal::backsp() { - write(CHAR_BS); - write(CHAR_SPACE); - write(CHAR_BS); -} - -size_t Terminal::println(const char *str) { - size_t n = print(str); - return n += println(); -} - -size_t Terminal::println(void) { - size_t n = 0; - switch (_eol) { - case CRLF: - n += write(CHAR_CR); - n += write(CHAR_LF); - break; - case LF: - n += write(CHAR_LF); - break; - case LFCR: - n += write(CHAR_LF); - n += write(CHAR_CR); - break; - case CR: - n += write(CHAR_CR); - break; - } - return n; -} - -size_t Terminal::write(uint8_t ch) { - size_t n = 0; - if (_stream) - n = _stream->write(ch); - return n; -} - -size_t Terminal::write_P(PGM_P str) { - uint8_t ch; - size_t n = 0; - while ((ch = pgm_read_byte(str + n)) != '\x0') { - _stream->write(ch); - n++; - } - return n; -} - -size_t Terminal::write(const uint8_t *buf, size_t size) { - size_t n = 0; - while (size--) { - if (_stream->write(*buf++)) - n++; - else - break; - } - return n; -} \ No newline at end of file diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp deleted file mode 100644 index e9258605..00000000 --- a/src/MqttClient.cpp +++ /dev/null @@ -1,323 +0,0 @@ -#include "MqttClient.h" - -#include - -#include "Class/NotAsinc.h" -#include "Global.h" -#include "Init.h" - -static const char* MODULE = "Mqtt"; - -namespace MqttClient { - -// Errors -int wifi_lost_error = 0; -int mqtt_lost_error = 0; -bool connected = false; - -// Session params -String mqttPrefix; -String mqttRootDevice; - -void init() { - - myNotAsincActions->add( - do_MQTTPARAMSCHANGED, [&](void*) { - reconnect(); - }, - nullptr); - - mqtt.setCallback(handleSubscribedUpdates); - - ts.add( - WIFI_MQTT_CONNECTION_CHECK, MQTT_RECONNECT_INTERVAL, - [&](void*) { - if (isNetworkActive()) { - if (mqtt.connected()) { - if (!connected) { - pm.info("OK"); - setLedStatus(LED_OFF); - connected = true; - } - } else { - connect(); - if (!just_load) mqtt_lost_error++; - } - } else { - if (connected) { - pm.error("connection lost"); - connected = false; - } - ts.remove(WIFI_MQTT_CONNECTION_CHECK); - wifi_lost_error++; - startAPMode(); - } - }, - nullptr, true); -} - -void disconnect() { - pm.info("disconnect"); - mqtt.disconnect(); -} - -void reconnect() { - disconnect(); - connect(); -} - -void loop() { - if (!isNetworkActive() || !mqtt.connected()) { - return; - } - mqtt.loop(); -} - -void subscribe() { - pm.info("subscribe"); - mqtt.subscribe(mqttPrefix.c_str()); - mqtt.subscribe((mqttRootDevice + "/+/control").c_str()); - mqtt.subscribe((mqttRootDevice + "/order").c_str()); - mqtt.subscribe((mqttRootDevice + "/update").c_str()); - mqtt.subscribe((mqttRootDevice + "/devc").c_str()); - mqtt.subscribe((mqttRootDevice + "/devs").c_str()); -} - -boolean connect() { - pm.info("connect"); - - String addr = jsonReadStr(configSetupJson, "mqttServer"); - if (!addr) { - pm.error("no broker address"); - return false; - } - - int port = jsonReadInt(configSetupJson, "mqttPort"); - String user = jsonReadStr(configSetupJson, "mqttUser"); - String pass = jsonReadStr(configSetupJson, "mqttPass"); - - //Session params - mqttPrefix = jsonReadStr(configSetupJson, "mqttPrefix"); - mqttRootDevice = mqttPrefix + "/" + chipId; - - pm.info("broker " + addr + ":" + String(port, DEC)); - pm.info("topic " + mqttRootDevice); - - setLedStatus(LED_FAST); - mqtt.setServer(addr.c_str(), port); - bool res = false; - if (!mqtt.connected()) { - if (mqtt.connect(chipId.c_str(), user.c_str(), pass.c_str())) { - pm.info("connected"); - setLedStatus(LED_OFF); - subscribe(); - res = true; - } else { - pm.error("could't connect, retry in " + String(MQTT_RECONNECT_INTERVAL / 1000) + "s"); - setLedStatus(LED_FAST); - } - } - return res; -} - -void handleSubscribedUpdates(char* topic, uint8_t* payload, size_t length) { - String topicStr = String(topic); - - pm.info(topicStr); - - String payloadStr; - - payloadStr.reserve(length + 1); - for (size_t i = 0; i < length; i++) { - payloadStr += (char)payload[i]; - } - - pm.info(payloadStr); - - if (payloadStr.startsWith("HELLO")) { - pm.info("Full update"); - publishWidgets(); - publishState(); -#ifdef LOGGING_ENABLED - choose_log_date_and_send(); -#endif - - } else if (topicStr.indexOf("control")) { - - //iotTeam/12882830-1458415/light 1 - - String key = selectFromMarkerToMarker(topicStr, "/", 3); - - orderBuf += key; - orderBuf += " "; - orderBuf += payloadStr; - orderBuf += ","; - - } else if (topicStr.indexOf("order")) { - payloadStr.replace("_", " "); - orderBuf += payloadStr; - orderBuf += ","; - - } else if (topicStr.indexOf("update")) { - if (payloadStr == "1") { - myNotAsincActions->make(do_UPGRADE); - } - - } else if (topicStr.indexOf("devc")) { - writeFile(String(DEVICE_CONFIG_FILE), payloadStr); - Device_init(); - - } else if (topicStr.indexOf("devs")) { - writeFile(String(DEVICE_SCENARIO_FILE), payloadStr); - loadScenario(); - } -} - -boolean publish(const String& topic, const String& data) { - if (mqtt.beginPublish(topic.c_str(), data.length(), false)) { - mqtt.print(data); - return mqtt.endPublish(); - } - return false; -} - -boolean publishData(const String& topic, const String& data) { - String path = mqttRootDevice + "/" + topic; - if (!publish(path, data)) { - pm.error("on publish data"); - return false; - } - return true; -} - -boolean publishChart(const String& topic, const String& data) { - String path = mqttRootDevice + "/" + topic + "/status"; - if (!publish(path, data)) { - pm.error("on publish chart"); - return false; - } - return true; -} - -boolean publishControl(String id, String topic, String state) { - String path = mqttPrefix + "/" + id + "/" + topic + "/control"; - return mqtt.publish(path.c_str(), state.c_str(), false); -} - -boolean publishChart_test(const String& topic, const String& data) { - String path = mqttRootDevice + "/" + topic + "/status"; - return mqtt.publish(path.c_str(), data.c_str(), false); -} - -boolean publishStatus(const String& topic, const String& data) { - String path = mqttRootDevice + "/" + topic + "/status"; - String json = "{}"; - jsonWriteStr(json, "status", data); - return mqtt.publish(path.c_str(), json.c_str(), false); -} - -#ifdef LAYOUT_IN_RAM -void publishWidgets() { - if (all_widgets != "") { - int counter = 0; - String line; - int psn_1 = 0; - int psn_2; - do { - psn_2 = all_widgets.indexOf("\r\n", psn_1); //\r\n - line = all_widgets.substring(psn_1, psn_2); - line.replace("\n", ""); - line.replace("\r\n", ""); - //jsonWriteStr(line, "id", String(counter)); - //jsonWriteStr(line, "pageId", String(counter)); - counter++; - sendMQTT("config", line); - Serial.println("[V] " + line); - psn_1 = psn_2 + 1; - } while (psn_2 + 2 < all_widgets.length()); - getMemoryLoad("[I] after send all widgets"); - } -} -#endif - -#ifndef LAYOUT_IN_RAM -void publishWidgets() { - auto file = seekFile("layout.txt"); - if (!file) { - pm.error("no file layout.txt"); - return; - } - while (file.available()) { - String payload = file.readStringUntil('\n'); - pm.info("widgets: " + payload); - publishData("config", payload); - } - file.close(); -} -#endif - -void publishState() { - // берет строку json и ключи превращает в топики а значения колючей в них посылает - // {"name":"MODULES","lang":"","ip":"192.168.43.60","DS":"34.00","rel1":"1","rel2":"1"} - // "name":"MODULES","lang":"","ip":"192.168.43.60","DS":"34.00","rel1":"1","rel2":"1" - // "name":"MODULES","lang":"","ip":"192.168.43.60","DS":"34.00","rel1":"1","rel2":"1", - String str = configLiveJson; - str.replace("{", ""); - str.replace("}", ""); - str += ","; - - while (str.length()) { - String tmp = selectToMarker(str, ","); - - String topic = selectToMarker(tmp, ":"); - topic.replace("\"", ""); - - String state = selectToMarkerLast(tmp, ":"); - state.replace("\"", ""); - - if ((topic != "time") && (topic != "name") && (topic != "lang") && (topic != "ip") && (topic.indexOf("_in") < 0)) { - publishStatus(topic, state); - } - str = deleteBeforeDelimiter(str, ","); - } -} - -const String getStateStr() { - switch (mqtt.state()) { - case -4: - return F("no respond"); - break; - case -3: - return F("connection was broken"); - break; - case -2: - return F("connection failed"); - break; - case -1: - return F("client disconnected"); - break; - case 0: - return F("client connected"); - break; - case 1: - return F("doesn't support the requested version"); - break; - case 2: - return F("rejected the client identifier"); - break; - case 3: - return F("unable to accept the connection"); - break; - case 4: - return F("wrong username/password"); - break; - case 5: - return F("not authorized to connect"); - break; - default: - return F("unspecified"); - break; - } -} - -} // namespace MqttClient \ No newline at end of file diff --git a/src/MqttDiscovery.cpp b/src/MqttDiscovery.cpp deleted file mode 100644 index 7ccd0bc1..00000000 --- a/src/MqttDiscovery.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "MqttDiscovery.h" - -namespace Discovery { - -static const char json_is_defined[] = "is_defined"; -static const char jsonId[] = "id"; -static const char jsonBatt[] = "batt"; -static const char jsonLux[] = "lux"; -static const char jsonPres[] = "pres"; -static const char jsonFer[] = "fer"; -static const char jsonMoi[] = "moi"; -static const char jsonHum[] = "hum"; -static const char jsonTemp[] = "tem"; -static const char jsonStep[] = "steps"; -static const char jsonWeight[] = "weight"; -static const char jsonPresence[] = "presence"; -static const char jsonAltim[] = "altim"; -static const char jsonAltif[] = "altift"; -static const char jsonTempf[] = "tempf"; -static const char jsonMsg[] = "message"; -static const char jsonVal[] = "value"; -static const char jsonVolt[] = "volt"; -static const char jsonCurrent[] = "current"; -static const char jsonPower[] = "power"; -static const char jsonGpio[] = "gpio"; -static const char jsonFtcd[] = "ftcd"; -static const char jsonWm2[] = "wattsm2"; -static const char jsonAdc[] = "adc"; -static const char jsonPa[] = "pa"; - -const String getValueJson(const char* str) { - char buf[32]; - sprintf(buf, "{{ value_json.%s }}", str); - return buf; -} - -void createDiscovery( - const char* type, const char* name, const char* clazz, - const char* value_template, const char* payload_on, const char* payload_off, - const char* maasure_unit, int off_delay, const char* has_payload, const char* no_payload, - const char* avail_topi, const char* cmd_topic, const char* state_topic, bool child) { - //const char* unique_id = getUniqueId(name).c_str(); -} - -void createADC(const char* name) { - createDiscovery( - "Type", "Name", "Clazz", - "Value", "Payload", "NoPayload", - "Measure", 0, "HasPayload", "NoPayload", - "", "", "", false); -} - -void createSwitch(const char* name) { - createDiscovery( - "Type", "Name", "Clazz", - "Value", "Payload", "NoPayload", - "Measure", 0, "HasPayload", "NoPayload", - "", "", "", false); -} -// component, -// type, -// name, -// availability topic, -// device class, -// value template, payload on, payload off, unit of measurement -const char* BMEsensor[6][8] = { - {"sensor", "tempc", "bme", "temperature", "", "", "°C"}, //jsonTemp - {"sensor", "tempf", "bme", "temperature", "", "", "°F"}, //jsonTempf - {"sensor", "pa", "bme", "", "", "", "hPa"}, //jsonPa - {"sensor", "hum", "bme", "humidity", "", "", "%"}, // jsonHum - {"sensor", "altim", "bme", "", "", "", "m"}, //jsonAltim - {"sensor", "altift", "bme", "", "", "", "ft"} // jsonAltif -}; - -} // namespace Discovery diff --git a/src/PushingBox.cpp b/src/PushingBox.cpp deleted file mode 100644 index c5c37a79..00000000 --- a/src/PushingBox.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Global.h" - -void Push_init() { - server.on("/pushingboxDate", HTTP_GET, [](AsyncWebServerRequest* request) { - if (request->hasArg("pushingbox_id")) { - jsonWriteStr(configSetupJson, "pushingbox_id", request->getParam("pushingbox_id")->value()); - } - - saveConfig(); - - request->send(200, "text/text", "ok"); // отправляем ответ о выполнении - }); -} - -void pushControl() { - String title = sCmd.next(); - title.replace("#", " "); - String body = sCmd.next(); - body.replace("#", " "); - - static String body_old; - - const char* logServer = "api.pushingbox.com"; - String deviceId = jsonReadStr(configSetupJson, "pushingbox_id"); - - Serial.println("- starting client"); - - WiFiClient client_push; - - Serial.println("- connecting to pushing server: " + String(logServer)); - if (!client_push.connect(logServer, 80)) { - Serial.println("- not connected"); - } else { - Serial.println("- succesfully connected"); - - String postStr = "devid="; - postStr += String(deviceId); - - postStr += "&title="; - postStr += String(title); - - postStr += "&body="; - postStr += String(body); - - postStr += "\r\n\r\n"; - - Serial.println("- sending data..."); - - client_push.print("POST /pushingbox HTTP/1.1\n"); - client_push.print("Host: api.pushingbox.com\n"); - client_push.print("Connection: close\n"); - client_push.print("Content-Type: application/x-www-form-urlencoded\n"); - client_push.print("Content-Length: "); - client_push.print(postStr.length()); - client_push.print("\n\n"); - client_push.print(postStr); - } - client_push.stop(); - Serial.println("- stopping the client"); -} diff --git a/src/SSDP.cpp b/src/SSDP.cpp deleted file mode 100644 index 3f0d4544..00000000 --- a/src/SSDP.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifdef SSDP -#ifdef ESP8266 - #include -#endif -#ifdef ESP32 - #include -#endif - -#include -#include "Global.h" - -void SsdpInit() { - server.on("/description.xml", HTTP_GET, [](AsyncWebServerRequest* request) { - String ssdpSend = ""; - String ssdpHeder = xmlNode("major", "1"); - ssdpHeder += xmlNode("minor", "0"); - ssdpHeder = xmlNode("specVersion", ssdpHeder); - ssdpHeder += xmlNode("URLBase", "http://" + WiFi.localIP().toString()); - String ssdpDescription = xmlNode("deviceType", "upnp:rootdevice"); - ssdpDescription += xmlNode("friendlyName", jsonReadStr(configSetupJson, "name")); - ssdpDescription += xmlNode("presentationURL", "/"); - ssdpDescription += xmlNode("serialNumber", getChipId()); -#ifdef ESP8266 - ssdpDescription += xmlNode("modelName", "ESP8266"); -#endif -#ifdef ESP32 - ssdpDescription += xmlNode("modelName", "ESP32"); -#endif - ssdpDescription += xmlNode("modelNumber", getChipId()); - ssdpDescription += xmlNode("modelURL", "https://github.com/IoTManagerProject/IoTManager/wiki"); - ssdpDescription += xmlNode("manufacturer", "Borisenko Dmitry"); - ssdpDescription += xmlNode("manufacturerURL", "https://github.com/IoTManagerProject/IoTManager"); - ssdpDescription += xmlNode("UDN", "uuid:38323636-4558-4dda-9188-cda0e6" + decToHex(ESP.getChipId(), 6)); - ssdpDescription = xmlNode("device", ssdpDescription); - ssdpHeder += ssdpDescription; - ssdpSend += ssdpHeder; - ssdpSend += ""; - Serial.println("->!!!SSDP Get request received"); - request->send(200, "text/xml", ssdpSend); - }); - //Если версия 2.0.0 закаментируйте следующую строчку - SSDP.setDeviceType("upnp:rootdevice"); - SSDP.setSchemaURL("description.xml"); - SSDP.begin(); -} - -String xmlNode(String tags, String data) { - String temp = "<" + tags + ">" + data + ""; - return temp; -} - -String decToHex(uint32_t decValue, byte desiredStringLength) { - String hexString = String(decValue, HEX); - while (hexString.length() < desiredStringLength) hexString = "0" + hexString; - return hexString; -} -#endif \ No newline at end of file diff --git a/src/Scenario.cpp b/src/Scenario.cpp deleted file mode 100644 index 06bf5fa8..00000000 --- a/src/Scenario.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "Cmd.h" -#include "Global.h" -#include "Class/ScenarioClass.h" - -static const char* MODULE = "Scen"; - -boolean isScenarioEnabled() { - return jsonReadBool(configSetupJson, "scen") && eventBuf != ""; -} - -void loopScenario() { - if (!isScenarioEnabled()) { - return; - } - String scenarioTmp = scenario; - scenarioTmp += "\n"; - scenarioTmp.replace("\r\n", "\n"); - scenarioTmp.replace("\r", "\n"); - - while (scenarioTmp.length()) { - String scenBlok = selectToMarker(scenarioTmp, "end"); //выделяем первый сценарий - if (!scenBlok.length()) { - return; - } - - size_t i = 0; - i++; - if (scenario_line_status[i] == 1) { - String condition = selectToMarker(scenBlok, "\n"); //выделяем условие - - String conditionParam = selectFromMarkerToMarker(condition, " ", 0); //выделяем параметр условия - String order = eventBuf; - String eventParam = selectToMarker(order, ","); //выделяем параметр события - - if (conditionParam == eventParam) { //если поступившее событие равно событию заданному buttonSet1 в файле начинаем его обработку - - String conditionSign = selectFromMarkerToMarker(condition, " ", 1); //выделяем знак (=) - - String conditionValue = selectFromMarkerToMarker(condition, " ", 2); //выделяем значение (1) - - boolean flag = false; - - String eventParam = jsonReadStr(configLiveJson, conditionParam); //получаем значение этого параметра события из json - - if (conditionSign == "=") { - flag = eventParam == conditionValue; - } else if (conditionSign == "!=") { - flag = eventParam != conditionValue; - } else if (conditionSign == "<") { - flag = eventParam.toInt() < conditionValue.toInt(); - } else if (conditionSign == ">") { - flag = eventParam.toInt() > conditionValue.toInt(); - } else if (conditionSign == ">=") { - flag = eventParam.toInt() >= conditionValue.toInt(); - } else if (conditionSign == "<=") { - flag = eventParam.toInt() <= conditionValue.toInt(); - } - - if (flag) { - scenBlok = deleteBeforeDelimiter(scenBlok, "\n"); // удаляем строку самого сценария оставляя только команды - pm.info("do: " + scenBlok); - spaceExecute(scenBlok); // выполняем все команды - } - } - } - scenarioTmp = deleteBeforeDelimiter(scenarioTmp, "end\n"); //удаляем первый сценарий - } - - String eventBufTmp = eventBuf; //читаем файл событий - eventBufTmp = deleteBeforeDelimiter(eventBufTmp, ","); //удаляем выполненное событие - eventBuf = eventBufTmp; //записываем обновленный файл событий -} - -void eventGen(String event_name, String number) { - if (!jsonReadBool(configSetupJson, "scen")) { - return; - } - eventBuf = event_name + number + ","; -} - -String add_set(String str) { - String num1 = str.substring(str.length() - 1); - String num2 = str.substring(str.length() - 2, str.length() - 1); - if (isDigitStr(num1) && isDigitStr(num2)) { - str = str.substring(0, str.length() - 2) + "Set" + num2 + num1; - } else { - if (isDigitStr(num1)) { - str = str.substring(0, str.length() - 1) + "Set" + num1; - } - } - return str; -} - -//button-out1 = 1 -//button-out2 1 -//button-out3 1 -//end \ No newline at end of file diff --git a/src/Sensors.cpp b/src/Sensors.cpp deleted file mode 100644 index d49c5f93..00000000 --- a/src/Sensors.cpp +++ /dev/null @@ -1,571 +0,0 @@ -#include "Class/SensorAnalog.h" -#include "Cmd.h" -#include "Global.h" - -GMedian<10, int> medianFilter; -DHTesp dht; - -Adafruit_BMP280 bmp; -Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor(); -Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor(); - -Adafruit_BME280 bme; -Adafruit_Sensor *bme_temp = bme.getTemperatureSensor(); -Adafruit_Sensor *bme_pressure = bme.getPressureSensor(); -Adafruit_Sensor *bme_humidity = bme.getHumiditySensor(); - -const String perceptionStr(byte value); -const String comfortStr(ComfortState value); - -void bmp280T_reading(); - -void sensorsInit() { - ts.add( - SENSORS, 15000, [&](void *) { - String buf = sensorReadingMap; - while (buf.length()) { - String tmp = selectToMarker(buf, ","); - sCmd.readStr(tmp); - buf = deleteBeforeDelimiter(buf, ","); - } - }, - nullptr, true); -} - -#ifdef ANALOG_ENABLED -//==============================================Модуль аналогового сенсора=========================================================================================== -//analog-adc;id;anydata;Сенсоры;Аналоговый;order;pin-adc;map[1,1024,1,100];c[1] -//=================================================================================================================================================================== -void analogAdc() { - mySensorAnalog = new SensorAnalog(); - mySensorAnalog->update(); - String key = mySensorAnalog->gkey(); - String pin = mySensorAnalog->gpin(); - sCmd.addCommand(key.c_str(), analogReading); - sensorReadingMap += key + ","; - jsonWriteStr(configOptionJson, key + "_pin", pin); - jsonWriteStr(configOptionJson, key + "_map", mySensorAnalog->gmap()); - jsonWriteStr(configOptionJson, key + "_с", mySensorAnalog->gc()); - mySensorAnalog->clear(); -} - -void analogReading() { - String key = sCmd.order(); - String pin = jsonReadStr(configOptionJson, key + "_pin"); - mySensorAnalog->SensorAnalogRead(key, pin); -} -#endif - -#ifdef LEVEL_ENABLED -//=========================================Модуль ультрозвукового дальномера================================================================== -//ultrasonic-cm;id;anydata;Сенсоры;Расстояние;order;pin;map[1,100,1,100];c[1] -//========================================================================================================================================= -void ultrasonicCm() { - - -} - -void ultrasonicReading() { - - -} - -void levelPr() { -// String value_name = sCmd.next(); -// String trig = sCmd.next(); -// String echo = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String empty_level = sCmd.next(); -// String full_level = sCmd.next(); -// String page_number = sCmd.next(); -// levelPr_value_name = value_name; -// jsonWriteStr(configOptionJson, "e_lev", empty_level); -// jsonWriteStr(configOptionJson, "f_lev", full_level); -// jsonWriteStr(configOptionJson, "trig", trig); -// jsonWriteStr(configOptionJson, "echo", echo); -// pinMode(trig.toInt(), OUTPUT); -// pinMode(echo.toInt(), INPUT); -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// sensors_reading_map[0] = 1; -} -////ultrasonicCm cm 14 12 Дистанция,#см Датчики fillgauge 1 -//void ultrasonicCm() { -// String value_name = sCmd.next(); -// String trig = sCmd.next(); -// String echo = sCmd.next(); -// String widget_name = sCmd.next(); -// String page_name = sCmd.next(); -// String type = sCmd.next(); -// String empty_level = sCmd.next(); -// String full_level = sCmd.next(); -// String page_number = sCmd.next(); -// ultrasonicCm_value_name = value_name; -// jsonWriteStr(configOptionJson, "trig", trig); -// jsonWriteStr(configOptionJson, "echo", echo); -// pinMode(trig.toInt(), OUTPUT); -// pinMode(echo.toInt(), INPUT); -// createWidgetByType(widget_name, page_name, page_number, type, value_name); -// sensors_reading_map[0] = 1; -//} -// -void ultrasonic_reading() { -// long duration_; -// int distance_cm; -// int level; -// static int counter; -// int trig = jsonReadInt(configOptionJson, "trig"); -// int echo = jsonReadInt(configOptionJson, "echo"); -// digitalWrite(trig, LOW); -// delayMicroseconds(2); -// digitalWrite(trig, HIGH); -// delayMicroseconds(10); -// digitalWrite(trig, LOW); -// duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m -// distance_cm = duration_ / 29 / 2; -// distance_cm = medianFilter.filtered(distance_cm); //отсечение промахов медианным фильтром -// counter++; -// if (counter > TANK_LEVEL_SAMPLES) { -// counter = 0; -// level = map(distance_cm, -// jsonReadInt(configOptionJson, "e_lev"), -// jsonReadInt(configOptionJson, "f_lev"), 0, 100); -// -// jsonWriteInt(configLiveJson, levelPr_value_name, level); -// eventGen(levelPr_value_name, ""); -// -// MqttClient::publishStatus(levelPr_value_name, String(level)); -// -// Serial.println("[I] sensor '" + levelPr_value_name + "' data: " + String(level)); -// -// jsonWriteInt(configLiveJson, ultrasonicCm_value_name, distance_cm); -// eventGen(ultrasonicCm_value_name, ""); -// -// MqttClient::publishStatus(ultrasonicCm_value_name, String(distance_cm)); -// -// Serial.println("[I] sensor '" + ultrasonicCm_value_name + "' data: " + String(distance_cm)); -// } -} -#endif -//========================================================================================================================================= -//=========================================Модуль температурного сенсора ds18b20=========================================================== -#ifdef DALLAS_ENABLED -//dallas temp1 2 1 Температура Датчики anydata 1 -//dallas temp2 2 2 Температура Датчики anydata 2 -void dallas() { - String value_name = sCmd.next(); - String pin = sCmd.next(); - String address = sCmd.next(); - jsonWriteStr(configOptionJson, value_name + "_ds", address); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - oneWire = new OneWire((uint8_t)pin.toInt()); - sensors.setOneWire(oneWire); - sensors.begin(); - sensors.setResolution(12); - dallas_value_name += value_name + ";"; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - sensors_reading_map[3] = 1; -} - -void dallas_reading() { - float temp = 0; - byte num = sensors.getDS18Count(); - String dallas_value_name_tmp_buf = dallas_value_name; - sensors.requestTemperatures(); - for (byte i = 0; i < num; i++) { - temp = sensors.getTempCByIndex(i); - String buf = selectToMarker(dallas_value_name_tmp_buf, ";"); - dallas_value_name_tmp_buf = deleteBeforeDelimiter(dallas_value_name_tmp_buf, ";"); - jsonWriteStr(configLiveJson, buf, String(temp)); - eventGen(buf, ""); - MqttClient::publishStatus(buf, String(temp)); - Serial.println("[I] sensor '" + buf + "' send date " + String(temp)); - } -} -#endif -//========================================================================================================================================= -//=========================================Модуль сенсоров DHT============================================================================= -#ifdef DHT_ENABLED -//dhtT t 2 dht11 Температура#DHT,#t°C Датчики any-data 1 -void dhtT() { - String value_name = sCmd.next(); - String pin = sCmd.next(); - String sensor_type = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - dhtT_value_name = value_name; - if (sensor_type == "dht11") { - dht.setup(pin.toInt(), DHTesp::DHT11); - } - if (sensor_type == "dht22") { - dht.setup(pin.toInt(), DHTesp::DHT22); - } - createWidgetByType(widget_name, page_name, page_number, type, value_name); - sensors_reading_map[4] = 1; -} - -void dhtT_reading() { - float value = 0; - static int counter; - if (dht.getStatus() != 0 && counter < 5) { - MqttClient::publishStatus(dhtT_value_name, String(dht.getStatusString())); - counter++; - } else { - counter = 0; - value = dht.getTemperature(); - if (String(value) != "nan") { - eventGen(dhtT_value_name, ""); - jsonWriteStr(configLiveJson, dhtT_value_name, String(value)); - MqttClient::publishStatus(dhtT_value_name, String(value)); - Serial.println("[I] sensor '" + dhtT_value_name + "' data: " + String(value)); - } - } -} - -//dhtH h 2 dht11 Влажность#DHT,#t°C Датчики any-data 1 -void dhtH() { - String value_name = sCmd.next(); - String pin = sCmd.next(); - String sensor_type = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - dhtH_value_name = value_name; - if (sensor_type == "dht11") { - dht.setup(pin.toInt(), DHTesp::DHT11); - } - if (sensor_type == "dht22") { - dht.setup(pin.toInt(), DHTesp::DHT22); - } - createWidgetByType(widget_name, page_name, page_number, type, value_name); - sensors_reading_map[5] = 1; -} - -void dhtH_reading() { - float value = 0; - static int counter; - if (dht.getStatus() != 0 && counter < 5) { - MqttClient::publishStatus(dhtH_value_name, String(dht.getStatusString())); - counter++; - } else { - counter = 0; - value = dht.getHumidity(); - if (String(value) != "nan") { - eventGen(dhtH_value_name, ""); - jsonWriteStr(configLiveJson, dhtH_value_name, String(value)); - MqttClient::publishStatus(dhtH_value_name, String(value)); - Serial.println("[I] sensor '" + dhtH_value_name + "' data: " + String(value)); - } - } -} - -//dhtPerception Восприятие: Датчики 4 -void dhtP() { - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String page_number = sCmd.next(); - createWidgetByType(widget_name, page_name, page_number, "any-data", "dhtPerception"); - sensors_reading_map[6] = 1; -} - -void dhtP_reading() { - byte value; - if (dht.getStatus() != 0) { - MqttClient::publishStatus("dhtPerception", String(dht.getStatusString())); - } else { - value = dht.computePerception(jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); - String final_line = perceptionStr(value); - jsonWriteStr(configLiveJson, "dhtPerception", final_line); - eventGen("dhtPerception", ""); - MqttClient::publishStatus("dhtPerception", final_line); - if (mqtt.connected()) { - Serial.println("[I] sensor 'dhtPerception' data: " + final_line); - } - } -} - -//dhtComfort Степень#комфорта: Датчики 3 -void dhtC() { - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String page_number = sCmd.next(); - createWidgetByType(widget_name, page_name, page_number, "anydata", "dhtComfort"); - sensors_reading_map[7] = 1; -} - -void dhtC_reading() { - ComfortState cf; - if (dht.getStatus() != 0) { - MqttClient::publishStatus("dhtComfort", String(dht.getStatusString())); - } else { - dht.getComfortRatio(cf, jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); - String final_line = comfortStr(cf); - jsonWriteStr(configLiveJson, "dhtComfort", final_line); - eventGen("dhtComfort", ""); - MqttClient::publishStatus("dhtComfort", final_line); - Serial.println("[I] sensor 'dhtComfort' send date " + final_line); - } -} - -const String perceptionStr(byte value) { - String res; - switch (value) { - case 0: - res = F("Сухой воздух"); - break; - case 1: - res = F("Комфортно"); - break; - case 2: - res = F("Уютно"); - break; - case 3: - res = F("Хорошо"); - break; - case 4: - res = F("Неудобно"); - break; - case 5: - res = F("Довольно неудобно"); - break; - case 6: - res = F("Очень неудобно"); - break; - case 7: - res = F("Невыносимо"); - default: - res = F("Unknown"); - break; - } - return res; -} - -const String comfortStr(ComfortState value) { - String res; - switch (value) { - case Comfort_OK: - res = F("Отлично"); - break; - case Comfort_TooHot: - res = F("Очень жарко"); - break; - case Comfort_TooCold: - res = F("Очень холодно"); - break; - case Comfort_TooDry: - res = F("Очень сухо"); - break; - case Comfort_TooHumid: - res = F("Очень влажно"); - break; - case Comfort_HotAndHumid: - res = F("Жарко и влажно"); - break; - case Comfort_HotAndDry: - res = F("Жарко и сухо"); - break; - case Comfort_ColdAndHumid: - res = F("Холодно и влажно"); - break; - case Comfort_ColdAndDry: - res = F("Холодно и сухо"); - break; - default: - res = F("Неизвестно"); - break; - }; - return res; -} - -//dhtDewpoint Точка#росы: Датчики 5 -void dhtD() { - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String page_number = sCmd.next(); - createWidgetByType(widget_name, page_name, page_number, "anydata", "dhtDewpoint"); - sensors_reading_map[8] = 1; -} - -void dhtD_reading() { - float value; - if (dht.getStatus() != 0) { - MqttClient::publishStatus("dhtDewpoint", String(dht.getStatusString())); - } else { - value = dht.computeDewPoint(jsonReadStr(configLiveJson, dhtT_value_name).toFloat(), jsonReadStr(configLiveJson, dhtH_value_name).toFloat(), false); - jsonWriteInt(configLiveJson, "dhtDewpoint", value); - eventGen("dhtDewpoint", ""); - MqttClient::publishStatus("dhtDewpoint", String(value)); - Serial.println("[I] sensor 'dhtDewpoint' data: " + String(value)); - } -} -#endif -//=========================================i2c bus esp8266 scl-4 sda-5 ==================================================================== -//========================================================================================================================================= -//=========================================Модуль сенсоров bmp280========================================================================== - -//bmp280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -void bmp280T() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bmp280T_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bmp.begin(hexStringToUint8(address)); - bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ - Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */ - Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ - Adafruit_BMP280::FILTER_X16, /* Filtering. */ - Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ - //bmp_temp->printSensorDetails(); - sensors_reading_map[9] = 1; -} - -void bmp280T_reading() { - float value = 0; - sensors_event_t temp_event; - bmp_temp->getEvent(&temp_event); - value = temp_event.temperature; - jsonWriteStr(configLiveJson, bmp280T_value_name, String(value)); - eventGen(bmp280T_value_name, ""); - MqttClient::publishStatus(bmp280T_value_name, String(value)); - Serial.println("[I] sensor '" + bmp280T_value_name + "' data: " + String(value)); -} - -//bmp280P press1 0x76 Давление#bmp280 Датчики any-data 2 -void bmp280P() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bmp280P_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bmp.begin(hexStringToUint8(address)); - bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ - Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */ - Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */ - Adafruit_BMP280::FILTER_X16, /* Filtering. */ - Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ - //bmp_temp->printSensorDetails(); - sensors_reading_map[10] = 1; -} - -void bmp280P_reading() { - float value = 0; - sensors_event_t pressure_event; - bmp_pressure->getEvent(&pressure_event); - value = pressure_event.pressure; - value = value / 1.333224; - jsonWriteStr(configLiveJson, bmp280P_value_name, String(value)); - eventGen(bmp280P_value_name, ""); - MqttClient::publishStatus(bmp280P_value_name, String(value)); - Serial.println("[I] sensor '" + bmp280P_value_name + "' data: " + String(value)); -} - -//========================================================================================================================================= -//=============================================Модуль сенсоров bme280====================================================================== -//bme280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -void bme280T() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bme280T_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bme.begin(hexStringToUint8(address)); - sensors_reading_map[11] = 1; -} - -void bme280T_reading() { - float value = 0; - value = bme.readTemperature(); - jsonWriteStr(configLiveJson, bme280T_value_name, String(value)); - eventGen(bme280T_value_name, ""); - MqttClient::publishStatus(bme280T_value_name, String(value)); - Serial.println("[I] sensor '" + bme280T_value_name + "' data: " + String(value)); -} - -//bme280P pres1 0x76 Давление#bmp280 Датчики any-data 1 -void bme280P() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bme280P_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bme.begin(hexStringToUint8(address)); - sensors_reading_map[12] = 1; -} - -void bme280P_reading() { - float value = 0; - value = bme.readPressure(); - value = value / 1.333224 / 100; - jsonWriteStr(configLiveJson, bme280P_value_name, String(value)); - eventGen(bme280P_value_name, ""); - MqttClient::publishStatus(bme280P_value_name, String(value)); - Serial.println("[I] sensor '" + bme280P_value_name + "' data: " + String(value)); -} - -//bme280H hum1 0x76 Влажность#bmp280 Датчики any-data 1 -void bme280H() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bme280H_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bme.begin(hexStringToUint8(address)); - sensors_reading_map[13] = 1; -} - -void bme280H_reading() { - float value = 0; - value = bme.readHumidity(); - jsonWriteStr(configLiveJson, bme280H_value_name, String(value)); - eventGen(bme280H_value_name, ""); - MqttClient::publishStatus(bme280H_value_name, String(value)); - Serial.println("[I] sensor '" + bme280H_value_name + "' data: " + String(value)); -} - -//bme280A altit1 0x76 Высота#bmp280 Датчики any-data 1 -void bme280A() { - String value_name = sCmd.next(); - String address = sCmd.next(); - String widget_name = sCmd.next(); - String page_name = sCmd.next(); - String type = sCmd.next(); - String page_number = sCmd.next(); - bme280A_value_name = value_name; - createWidgetByType(widget_name, page_name, page_number, type, value_name); - bme.begin(hexStringToUint8(address)); - sensors_reading_map[14] = 1; -} - -void bme280A_reading() { - float value = bme.readAltitude(1013.25); - jsonWriteStr(configLiveJson, bme280A_value_name, String(value, 2)); - - eventGen(bme280A_value_name, ""); - - MqttClient::publishStatus(bme280A_value_name, String(value)); - - Serial.println("[I] sensor '" + bme280A_value_name + "' data: " + String(value)); -} diff --git a/src/Servo/Servs.cpp b/src/Servo/Servs.cpp deleted file mode 100644 index 8667baa0..00000000 --- a/src/Servo/Servs.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifdef SERVO_ENABLED -#include "Servo/Servos.h" -Servos myServo; - -Servos::Servos(){}; - -Servo *Servos::create(uint8_t num, uint8_t pin) { - // Ищем среди ранее созданных - for (size_t i = 0; i < _items.size(); i++) { - auto item = _items.at(i); - if (item.num == num) { - if (item.pin != pin) { - item.obj->attach(pin); - item.pin = pin; - }; - return item.obj; - } - } - // Добавляем новый - Servo_t newItem{num, pin}; - newItem.obj = new Servo(); - newItem.obj->attach(pin); - _items.push_back(newItem); - return newItem.obj; -} - -Servo *Servos::get(uint8_t num) { - // Ищем среди ранее созданных - for (size_t i = 0; i < _items.size(); i++) { - auto item = _items.at(i); - if (item.num == num) { - return item.obj; - } - } - return nullptr; -} - -size_t Servos::count() { - return _items.size(); -} -#endif diff --git a/src/Timers.cpp b/src/Timers.cpp deleted file mode 100644 index aa6bd002..00000000 --- a/src/Timers.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "Global.h" - -//================================================================================================================ -//=========================================Таймеры================================================================= -void Timer_countdown_init() { - ts.add( - TIMER_COUNTDOWN, 1000, [&](void*) { - String old_line = jsonReadStr(configOptionJson, "timers"); - if (old_line != "") { - //Serial.println(old_line); - int i = 0; - do { - String timer = selectFromMarkerToMarker(old_line, ",", i); - Serial.print("timer no " + String(i) + ": "); - Serial.println(timer); - if (timer == "not found" || timer == "") return; - int number = selectToMarker(timer, ":").toInt(); - int time = readTimer(number); - if (time == 0) { - delTimer(String(number)); - jsonWriteStr(configLiveJson, "timer" + String(number), "0"); - eventGen("timer", String(number)); - } else { - time--; - addTimer(String(number), String(time)); - } - i++; - } while (i <= 9); - } - }, - nullptr, true); -} - -void timerStart_() { - String number = sCmd.next(); - String period_of_time = sCmd.next(); - String type = sCmd.next(); - if (period_of_time.indexOf("digit") != -1) { - //period_of_time = add_set(period_of_time); - period_of_time = jsonReadStr(configLiveJson, period_of_time); - } - if (type == "sec") period_of_time = period_of_time; - if (type == "min") period_of_time = String(period_of_time.toInt() * 60); - if (type == "hours") period_of_time = String(period_of_time.toInt() * 60 * 60); - addTimer(number, period_of_time); - jsonWriteStr(configLiveJson, "timer" + number, "1"); -} -void addTimer(String number, String time) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - String new_timer = number + ":" + time; - int psn1 = tmp.indexOf(number + ":"); //0 ищем позицию таймера который надо заменить - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - String timer = tmp.substring(psn1, psn2); //1:60 выделяем таймер который надо заменить - ///tmp.replace(timer, new_timer); //заменяем таймер на новый (во всей стороке) - tmp.replace(timer + ",", ""); - tmp += new_timer + ","; - } else { //если его нет - tmp += new_timer + ","; - } - jsonWriteStr(configOptionJson, "timers", tmp); - //Serial.println("ura"); -} - -void timerStop_() { - String number = sCmd.next(); - delTimer(number); -} - -void delTimer(String number) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - int psn1 = tmp.indexOf(number + ":"); //0 ищем позицию таймера который надо удалить - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - String timer = tmp.substring(psn1, psn2) + ","; //1:60, выделяем таймер который надо удалить - tmp.replace(timer, ""); //удаляем таймер - jsonWriteStr(configOptionJson, "timers", tmp); - } -} - -int readTimer(int number) { - String tmp = jsonReadStr(configOptionJson, "timers"); //1:60,2:120, - int psn1 = tmp.indexOf(String(number) + ":"); //0 ищем позицию таймера который надо прочитать - String timer; - if (psn1 != -1) { //если он есть - int psn2 = tmp.indexOf(",", psn1); //4 от этой позиции находим позицию запятой - timer = tmp.substring(psn1, psn2); //1:60 выделяем таймер который надо прочитать - timer = deleteBeforeDelimiter(timer, ":"); - } - return timer.toInt(); -} \ No newline at end of file diff --git a/src/Upgrade.cpp b/src/Upgrade.cpp deleted file mode 100644 index 5a38e5cf..00000000 --- a/src/Upgrade.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "Upgrade.h" - -#include "Class/NotAsinc.h" -#include "ESP8266.h" -#include "Global.h" - -static const char* MODULE = "Update"; -static const char* check_update_url PROGMEM = "http://91.204.228.124:1100/update/%s/version.txt"; - -void upgradeInit() { - myNotAsincActions->add( - do_UPGRADE, [&](void*) { - upgrade_firmware(); - }, - nullptr); - - myNotAsincActions->add( - do_GETLASTVERSION, [&](void*) { - getLastVersion(); - }, - nullptr); - - if (isNetworkActive()) { - getLastVersion(); - if (lastVersion.length()) { - pm.info("available: " + lastVersion); - } - }; -} - -const String getAvailableUrl(const char* mcu) { - char buf[128]; - sprintf_P(buf, check_update_url, mcu); - return buf; -} - -void getLastVersion() { - String url; -#ifdef ESP8266 - url = getAvailableUrl("esp8266"); -#else - url = getAvailableUrl("esp32"); -#endif - lastVersion = getURL(url); - jsonWriteStr(configSetupJson, "last_version", lastVersion); -} - -void upgrade_firmware() { - String scanerioBackup, configBackup, setupBackup; - - scanerioBackup = readFile(String(DEVICE_SCENARIO_FILE), 4096); - configBackup = readFile(String(DEVICE_CONFIG_FILE), 4096); - setupBackup = configSetupJson; - - pm.info("update data"); - WiFiClient wifiClient; -#ifdef ESP8266 - ESPhttpUpdate.rebootOnUpdate(false); - t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs(wifiClient, "http://91.204.228.124:1100/update/esp8266/esp32-esp8266_iot-manager_modules_firmware.spiffs.bin"); -#else - httpUpdate.rebootOnUpdate(false); - t_httpUpdate_return ret = httpUpdate.updateSpiffs(wifiClient, "http://91.204.228.124:1100/update/esp32/esp32-esp8266_iot-manager_modules_firmware.spiffs.bin"); -#endif - if (ret == HTTP_UPDATE_OK) { - writeFile(String(DEVICE_SCENARIO_FILE), scanerioBackup); - writeFile(String(DEVICE_CONFIG_FILE), configBackup); - writeFile("config.json", setupBackup); - - saveConfig(); - - pm.info("done!"); - } else { - pm.error("on data"); - return; - } - - Serial.println("update firmware"); -#ifdef ESP8266 - ret = ESPhttpUpdate.update(wifiClient, "http://91.204.228.124:1100/update/esp8266/esp32-esp8266_iot-manager_modules_firmware.ino.bin"); -#else - ret = httpUpdate.update(wifiClient, "http://91.204.228.124:1100/update/esp32/esp32-esp8266_iot-manager_modules_firmware.ino.bin"); -#endif - if (ret == HTTP_UPDATE_OK) { - pm.info("done! restart..."); - ESP.restart(); - } else { - pm.error("on firmware"); - } -} diff --git a/src/Utils/FileUtils.cpp b/src/Utils/FileUtils.cpp deleted file mode 100644 index 0ec732c3..00000000 --- a/src/Utils/FileUtils.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "Utils/FileUtils.h" -#include "Utils/PrintMessage.h" - -static const char* MODULE = "FS"; - -const String filepath(const String& filename) { - return filename.startsWith("/") ? filename : "/" + filename; -} - -bool fileSystemInit() { - if (!LittleFS.begin()) { - pm.error("init"); - return false; - } - return true; -} - -void removeFile(const String& filename) { - String path = filepath(filename); - if (LittleFS.exists(path)) { - if (!LittleFS.remove(path)) { - pm.error("remove " + path); - } - } else { - pm.info("not exist" + path); - } -} - -File seekFile(const String& filename, size_t position) { - String path = filepath(filename); - auto file = LittleFS.open(path, "r"); - if (!file) { - pm.error("open " + path); - } - // поставим курсор в начало файла - file.seek(position, SeekSet); - return file; -} - -const String readFileString(const String& filename, const String& to_find) { - String path = filepath(filename); - String res = "failed"; - auto file = LittleFS.open(path, "r"); - if (!file) { - return "failed"; - } - if (file.find(to_find.c_str())) { - res = file.readStringUntil('\n'); - } - file.close(); - return res; -} - -const String addFileLn(const String& filename, const String& str) { - String path = filepath(filename); - auto file = LittleFS.open(path, "a"); - if (!file) { - return "failed"; - } - file.println(str); - file.close(); - return "sucсess"; -} - -const String addFile(const String& filename, const String& str) { - String path = filepath(filename); - auto file = LittleFS.open(path, "a"); - if (!file) { - return "failed"; - } - file.print(str); - file.close(); - return "sucсess"; -} - -bool copyFile(const String& src, const String& dst, bool overwrite) { - String srcPath = filepath(src); - String dstPath = filepath(dst); - pm.info("copy " + srcPath + " to " + dstPath); - if (!LittleFS.exists(srcPath)) { - pm.error("not exist: " + srcPath); - return false; - } - if (LittleFS.exists(dstPath)) { - if (!overwrite) { - pm.error("already exist: " + dstPath); - return false; - } - LittleFS.remove(dstPath); - } - auto srcFile = LittleFS.open(srcPath, "r"); - auto dstFile = LittleFS.open(dstPath, "w"); - - uint8_t buf[512]; - while (srcFile.available()) { - size_t len = srcFile.read(buf, 512); - dstFile.write(buf, len); - } - srcFile.close(); - dstFile.close(); - return true; -} - -const String writeFile(const String& filename, const String& str) { - String path = filepath(filename); - auto file = LittleFS.open(path, "w"); - if (!file) { - return "failed"; - } - file.print(str); - file.close(); - return "sucсess"; -} - -const String readFile(const String& filename, size_t max_size) { - String path = filepath(filename); - auto file = LittleFS.open(path, "r"); - if (!file) { - return "failed"; - } - size_t size = file.size(); - if (size > max_size) { - file.close(); - return "large"; - } - String temp = file.readString(); - file.close(); - return temp; -} - -const String getFileSize(const String filename) { - String filepath(filename); - auto file = LittleFS.open(filepath, "r"); - if (!file) { - return "failed"; - } - size_t size = file.size(); - file.close(); - return String(size); -} - -const String getFSSizeInfo() { - String res; -#ifdef ESP8266 - FSInfo info; - if (LittleFS.info(info)) { - res = prettyBytes(info.usedBytes) + " of " + prettyBytes(info.totalBytes); - } else { - res = "error"; - } -#else - res = prettyBytes(LittleFS.usedBytes()) + " of " + prettyBytes(LittleFS.totalBytes()); -#endif - return res; -} - -const String getConfigFile(uint8_t preset, ConfigType_t type) { - char buf[64]; - sprintf(buf, "/conf/%s%03d.txt", (type == CT_CONFIG) ? "c" : "s", preset); - return String(buf); -} \ No newline at end of file diff --git a/src/Utils/JsonUtils.cpp b/src/Utils/JsonUtils.cpp deleted file mode 100644 index 04b4b926..00000000 --- a/src/Utils/JsonUtils.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "Utils\JsonUtils.h" -#include "Utils/FileUtils.h" -#include "Global.h" - -#include - -String jsonReadStr(String& json, String name) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - return root[name].as(); -} - -boolean jsonReadBool(String& json, String name) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - return root[name].as(); -} - -int jsonReadInt(String& json, String name) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - return root[name]; -} - -String jsonWriteStr(String& json, String name, String value) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - root[name] = value; - json = ""; - root.printTo(json); - return json; -} - -String jsonWriteBool(String& json, String name, boolean value) { - return jsonWriteStr(json, name, value ? "1" : "0"); -} - -String jsonWriteInt(String& json, String name, int value) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - root[name] = value; - json = ""; - root.printTo(json); - return json; -} - -String jsonWriteFloat(String& json, String name, float value) { - DynamicJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(json); - root[name] = value; - json = ""; - root.printTo(json); - return json; -} - -void saveConfig() { - writeFile(String("config.json"), configSetupJson); -} \ No newline at end of file diff --git a/src/Utils/StringUtils.cpp b/src/Utils/StringUtils.cpp deleted file mode 100644 index 7c043f0e..00000000 --- a/src/Utils/StringUtils.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "Utils\StringUtils.h" -#include "Consts.h" - -String selectToMarkerLast(String str, String found) { - int p = str.lastIndexOf(found); - return str.substring(p + found.length()); -} - -String selectToMarker(String str, String found) { - int p = str.indexOf(found); - return str.substring(0, p); -} - -String extractInner(String str) { - int p1 = str.indexOf("["); - int p2 = str.indexOf("]"); - return str.substring(p1 + 1, p2); -} - -String deleteAfterDelimiter(String str, String found) { - int p = str.indexOf(found); - return str.substring(0, p); -} - -String deleteBeforeDelimiter(String str, String found) { - int p = str.indexOf(found) + found.length(); - return str.substring(p); -} - -String deleteBeforeDelimiterTo(String str, String found) { - int p = str.indexOf(found); - return str.substring(p); -} - -String deleteToMarkerLast(String str, String found) { - int p = str.lastIndexOf(found); - return str.substring(0, p); -} - -String selectToMarkerPlus(String str, String found, int plus) { - int p = str.indexOf(found); - return str.substring(0, p + plus); -} - -String selectFromMarkerToMarker(String str, String tofind, int number) { - if (str.indexOf(tofind) == -1) { - return "not found"; - } - str += tofind; // добавим для корректного поиска - uint8_t i = 0; // Индекс перебора - do { - if (i == number) { - // если индекс совпал с позицией - return selectToMarker(str, tofind); - } - // отбросим проверенный блок до разделителя - str = deleteBeforeDelimiter(str, tofind); - i++; - } while (str.length() != 0); - - return "not found"; -} - -uint8_t hexStringToUint8(String hex) { - uint8_t tmp = strtol(hex.c_str(), NULL, 0); - if (tmp >= 0x00 && tmp <= 0xFF) { - return tmp; - } -} - -uint16_t hexStringToUint16(String hex) { - uint16_t tmp = strtol(hex.c_str(), NULL, 0); - if (tmp >= 0x0000 && tmp <= 0xFFFF) { - return tmp; - } -} - -size_t itemsCount(String str, const String &separator) { - // если строки поиск нет сразу выход - if (str.indexOf(separator) == -1) { - return 0; - } - // добавим для корректного поиска - str += separator; - size_t cnt = 0; - while (str.length()) { - // отбросим проверенный блок до разделителя - str = deleteBeforeDelimiter(str, separator); - cnt++; - } - return cnt; -} - -boolean isDigitStr(const String &str) { - for (size_t i = 0; i < str.length(); i++) { - if (!isDigit(str.charAt(i))) { - return false; - } - } - return str.length(); -} - -String prettyBytes(size_t size) { - if (size < 1024) - return String(size) + "b"; - else if (size < (1024 * 1024)) - return String(size / 1024.0) + "kB"; - else if (size < (1024 * 1024 * 1024)) - return String(size / 1024.0 / 1024.0) + "MB"; - else - return String(size / 1024.0 / 1024.0 / 1024.0) + "GB"; -} - -static const char *str_info = "I"; -static const char *str_warn = "W"; -static const char *str_error = "E"; -static const char *str_unknown = "?"; - -String getErrorLevelStr(ErrorLevel_t level) { - const char *ptr; - switch (level) { - case EL_INFO: - ptr = str_info; - break; - case EL_WARNING: - ptr = str_warn; - break; - case EL_ERROR: - ptr = str_error; - break; - default: - ptr = str_unknown; - break; - } - return String(ptr); -} \ No newline at end of file diff --git a/src/Utils/SysUtils.cpp b/src/Utils/SysUtils.cpp deleted file mode 100644 index d5c957d6..00000000 --- a/src/Utils/SysUtils.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "Utils/SysUtils.h" -#include "Utils/PrintMessage.h" -#include "Global.h" - -static const char* MODULE = "Main"; - -const String getUniqueId(const char* name) { - return String(name) + getMacAddress(); -} - -const String getChipId() { - String res; -#ifdef ESP32 - char buf[32] = {0}; - uint32_t mac = ESP.getEfuseMac(); - sprintf(buf, "%0X", mac); - res = String(buf); -#endif -#ifdef ESP8266 - res = String(ESP.getChipId()) + "-" + String(ESP.getFlashChipId()); -#endif - return res; -} - -void setChipId() { - chipId = getChipId(); - pm.info("id: " + chipId); -} - -#ifdef ESP8266 -static uint32_t total_memory = 52864; -#else -static uint32_t total_memory = ESP.getHeapSize(); -#endif - -const String printMemoryStatus() { - uint32_t free = ESP.getFreeHeap(); - uint32_t used = total_memory - free; - uint32_t memory_load = (used * 100) / total_memory; - char buf[64]; - sprintf(buf, "used: %d%% free: %s", memory_load, getHeapStats().c_str()); - return String(buf); -} - -#ifdef ESP8266 -const String getHeapStats() { - uint32_t free; - uint16_t max; - uint8_t frag; - ESP.getHeapStats(&free, &max, &frag); - String buf; - buf += prettyBytes(free); - buf += " frag: "; - buf += frag; - buf += '%'; - return buf; -} -#else -const String getHeapStats() { - String buf; - buf = prettyBytes(ESP.getFreeHeap()); - return buf; -} -#endif - -const String getMacAddress() { - uint8_t mac[6]; - char buf[13] = {0}; -#if defined(ESP8266) - WiFi.macAddress(mac); - sprintf(buf, MACSTR, MAC2STR(mac)); -#else - esp_read_mac(mac, ESP_MAC_WIFI_STA); - sprintf(buf, MACSTR, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); -#endif - return String(buf); -} - - -#ifdef ESP8266 -void setLedStatus(LedStatus_t status) { - pinMode(LED_PIN, OUTPUT); - switch (status) { - case LED_OFF: - noTone(LED_PIN); - digitalWrite(LED_PIN, HIGH); - break; - case LED_ON: - noTone(LED_PIN); - digitalWrite(LED_PIN, LOW); - break; - case LED_SLOW: - tone(LED_PIN, 1); - break; - case LED_FAST: - tone(LED_PIN, 20); - break; - default: - break; - } -} -#else -void setLedStatus(LedStatus_t status) { - pinMode(LED_PIN, OUTPUT); - switch (status) { - case LED_OFF: - digitalWrite(LED_PIN, HIGH); - break; - case LED_ON: - digitalWrite(LED_PIN, LOW); - break; - case LED_SLOW: - break; - case LED_FAST: - break; - default: - break; - } -} -#endif -//=================================================================== -/* - void web_print (String text) { - if (WiFi.status() == WL_CONNECTED) { - jsonWriteStr(json, "test1", jsonReadStr(json, "test2")); - jsonWriteStr(json, "test2", jsonReadStr(json, "test3")); - jsonWriteStr(json, "test3", jsonReadStr(json, "test4")); - jsonWriteStr(json, "test4", jsonReadStr(json, "test5")); - jsonWriteStr(json, "test5", jsonReadStr(json, "test6")); - - jsonWriteStr(json, "test6", GetTime() + " " + text); - - ws.textAll(json); - } - } -*/ -//=================================================================== -/* - "socket": [ - "ws://{{ip}}/ws" - ], -*/ -//=================================================================== -/* - { - "type": "h4", - "title": "('{{build2}}'=='{{firmware_version}}'?'NEW':'OLD')" - }, -*/ -//=================================================================== -/* - { - "type": "button", - "title": "Конфигурация устройства", - "socket": "test2", - "class": "btn btn-block btn-primary" - }, - { - "type": "hr" - }, - { - "type": "h6", - "title": "{{test1}}" - }, - { - "type": "h6", - "title": "{{test2}}" - }, - { - "type": "h6", - "title": "{{test3}}" - }, - { - "type": "h6", - "title": "{{test4}}" - }, - { - "type": "h6", - "title": "{{test5}}" - }, - { - "type": "h6", - "title": "{{test6}}" - }, - { - "type": "hr" - }, -*/ -//=================================================================== - -/* - String getResetReason(uint8_t core) { - int reason = rtc_get_reset_reason(core); - switch (reason) { - case 1 : return "Power on"; break; //Vbat power on reset - case 3 : return "Software reset digital core"; break; //Software reset digital core - case 4 : return "Legacy watch dog reset digital core"; break; //Legacy watch dog reset digital core - case 5 : return "Deep Sleep reset digital core"; break; //Deep Sleep reset digital core - case 6 : return "Reset by SLC module, reset digital core"; break; //Reset by SLC module, reset digital core - case 7 : return "Timer Group0 Watch dog reset digital core"; break; //Timer Group0 Watch dog reset digital core - case 8 : return "Timer Group1 Watch dog reset digital core"; break; //Timer Group1 Watch dog reset digital core - case 9 : return "RTC Watch dog Reset digital core"; break; // - case 10 : return "Instrusion tested to reset CPU"; break; - case 11 : return "Time Group reset CPU"; break; - case 12 : return "Software reset CPU"; break; - case 13 : return "RTC Watch dog Reset CPU"; break; - case 14 : return "for APP CPU, reseted by PRO CPU"; break; - case 15 : return "Reset when the vdd voltage is not stable"; break; - case 16 : return "RTC Watch dog reset digital core and rtc module"; break; - default : return "NO_MEAN"; - } - } - - - String EspClass::getResetReason(void) { - char buff[32]; - if (resetInfo.reason == REASON_DEFAULT_RST) { // normal startup by power on - strcpy_P(buff, PSTR("Power on")); - } else if (resetInfo.reason == REASON_WDT_RST) { // hardware watch dog reset - strcpy_P(buff, PSTR("Hardware Watchdog")); - } else if (resetInfo.reason == REASON_EXCEPTION_RST) { // exception reset, GPIO status won’t change - strcpy_P(buff, PSTR("Exception")); - } else if (resetInfo.reason == REASON_SOFT_WDT_RST) { // software watch dog reset, GPIO status won’t change - strcpy_P(buff, PSTR("Software Watchdog")); - } else if (resetInfo.reason == REASON_SOFT_RESTART) { // software restart ,system_restart , GPIO status won’t change - strcpy_P(buff, PSTR("Software/System restart")); - } else if (resetInfo.reason == REASON_DEEP_SLEEP_AWAKE) { // wake up from deep-sleep - strcpy_P(buff, PSTR("Deep-Sleep Wake")); - } else if (resetInfo.reason == REASON_EXT_SYS_RST) { // external system reset - strcpy_P(buff, PSTR("External System")); - } else { - strcpy_P(buff, PSTR("Unknown")); - } - return String(buff); - } -*/ diff --git a/src/Utils/TimeUtils.cpp b/src/Utils/TimeUtils.cpp deleted file mode 100644 index 04e53d1f..00000000 --- a/src/Utils/TimeUtils.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include "Utils\TimeUtils.h" -#include "Utils\StringUtils.h" - -static const uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -static const char* week_days[7] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"}; - -// String getTimeUnix() { -// time_t t; -// struct tm* tm; - -// t = time(NULL); -// tm = localtime(&t); -// Serial.printf("%04d/%02d/%02d(%s) %02d:%02d:%02d\n", -// tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, week_days[tm->tm_wday], -// tm->tm_hour, tm->tm_min, tm->tm_sec); -// delay(1000); -// time_t now = time(nullptr); -// if (now < 30000) { -// return "failed"; -// } -// return String(now); -// } - -// String getTime() { -// time_t now = time(nullptr); -// int zone = 3600 * jsonReadStr(configSetupJson, "timezone").toInt(); -// now = now + zone; -// String Time = ""; // Строка для результатов времени -// Time += ctime(&now); // Преобразуем время в строку формата Thu Jan 19 00:55:35 2017 -// int i = Time.indexOf(":"); //Ишем позицию первого символа : -// Time = Time.substring(i - 2, i + 6); // Выделяем из строки 2 символа перед символом : и 6 символов после -// return Time; // Возврашаем полученное время -// } - -// String getTimeWOsec() { -// time_t now = time(nullptr); -// int zone = 3600 * jsonReadStr(configSetupJson, "timezone").toInt(); -// now = now + zone; -// String Time = ""; // Строка для результатов времени -// Time += ctime(&now); // Преобразуем время в строку формата Thu Jan 19 00:55:35 2017 -// int i = Time.indexOf(":"); //Ишем позицию первого символа : -// Time = Time.substring(i - 2, i + 3); // Выделяем из строки 2 символа перед символом : и 6 символов после -// return Time; // Возврашаем полученное время -// } - -// String getDate() { -// time_t now = time(nullptr); -// int zone = 3600 * jsonReadStr(configSetupJson, "timezone").toInt(); -// now = now + zone; -// String Data = ""; // Строка для результатов времени -// Data += ctime(&now); // Преобразуем время в строку формата Thu Jan 19 00:55:35 2017 -// Data.replace("\n", ""); -// uint8_t i = Data.lastIndexOf(" "); //Ишем позицию последнего символа пробел -// String Time = Data.substring(i - 8, i + 1); // Выделяем время и пробел -// Data.replace(Time, ""); // Удаляем из строки 8 символов времени и пробел -// return Data; // Возврашаем полученную дату -// } - -// String getDateDigitalFormated() { -// String date = getDate(); - -// date = deleteBeforeDelimiter(date, " "); - -// date.replace("Jan", "01"); -// date.replace("Feb", "02"); -// date.replace("Mar", "03"); -// date.replace("Apr", "04"); -// date.replace("May", "05"); -// date.replace("Jun", "06"); -// date.replace("Jul", "07"); -// date.replace("Aug", "08"); -// date.replace("Sep", "09"); -// date.replace("Oct", "10"); -// date.replace("Nov", "11"); -// date.replace("Dec", "12"); - -// String month = date.substring(0, 2); -// String day = date.substring(3, 5); -// String year = date.substring(8, 10); - -// String out = day; -// out += "."; -// out += month; -// out += "."; -// out += year; - -// return out; -// } - -// int timeToMin(String Time) { -// //"00:00:00" время в секунды -// long min = selectToMarker(Time, ":").toInt() * 60; //общее количество секунд в полных часах -// Time = deleteBeforeDelimiter(Time, ":"); // Теперь здесь минуты секунды -// min += selectToMarker(Time, ":").toInt(); // Добавим секунды из полных минут -// return min; -// } - -static const char* TIME_FORMAT PROGMEM = "%02d:%02d:%02d"; -static const char* TIME_FORMAT_WITH_DAYS PROGMEM = "%dd %02d:%02d"; - -const String prettySeconds(unsigned long time_s) { - unsigned long tmp = time_s; - unsigned long seconds; - unsigned long minutes; - unsigned long hours; - unsigned long days; - seconds = tmp % 60; - tmp = tmp / 60; - - minutes = tmp % 60; - tmp = tmp / 60; - - hours = tmp % 24; - days = tmp / 24; - - char buf[32]; - - if (days) { - sprintf_P(buf, TIME_FORMAT_WITH_DAYS, days, hours, minutes, seconds); - } else { - sprintf_P(buf, TIME_FORMAT, hours, minutes, seconds); - } - return String(buf); -} - -const String prettyMillis(unsigned long time_ms) { - return prettySeconds(time_ms / 1000); -} - -unsigned long millis_since(unsigned long sinse) { - return millis_passed(sinse, millis()); -} - -unsigned long millis_passed(unsigned long start, unsigned long finish) { - unsigned long result = 0; - if (start <= finish) { - unsigned long passed = finish - start; - if (passed <= __LONG_MAX__) { - result = static_cast(passed); - } else { - result = static_cast((__LONG_MAX__ - finish) + start + 1u); - } - } else { - unsigned long passed = start - finish; - if (passed <= __LONG_MAX__) { - result = static_cast(passed); - result = -1 * result; - } else { - result = static_cast((__LONG_MAX__ - start) + finish + 1u); - result = -1 * result; - } - } - return result; -} - -int getOffsetInSeconds(int timezone) { - return getOffsetInMinutes(timezone) * ONE_MINUTE_s; -} - -int getOffsetInMinutes(int timezone) { - return timezone * ONE_HOUR_m; -} - -void breakEpochToTime(unsigned long epoch, Time_t& tm) { - // break the given time_input into time components - // this is a more compact version of the C library localtime function - - unsigned long time = epoch; - tm.second = time % 60; - time /= 60; // now it is minutes - tm.minute = time % 60; - time /= 60; // now it is hours - tm.hour = time % 24; - time /= 24; // now it is days - tm.days = time; - tm.day_of_week = ((time + 4) % 7) + 1; // Sunday is day 1 - - uint8_t year = 0; - unsigned long days = 0; - - while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { - year++; - } - tm.year = year - 30; - - days -= LEAP_YEAR(year) ? 366 : 365; - time -= days; // now it is days in this year, starting at 0 - tm.day_of_year = time; - - uint8_t month; - uint8_t month_length; - for (month = 0; month < 12; month++) { - if (1 == month) { // february - if (LEAP_YEAR(year)) { - month_length = 29; - } else { - month_length = 28; - } - } else { - month_length = days_in_month[month]; - } - - if (time >= month_length) { - time -= month_length; - } else { - break; - } - } - tm.month = month + 1; // jan is month 1 - tm.day_of_month = time + 1; // day of month - tm.valid = (epoch > MIN_DATETIME); -} diff --git a/src/Utils/WebUtils.cpp b/src/Utils/WebUtils.cpp deleted file mode 100644 index dc87a06c..00000000 --- a/src/Utils/WebUtils.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Utils\WebUtils.h" -#include "ESPAsyncWebServer.h" - -String getURL(const String& urls) { - String res = ""; - HTTPClient http; - http.begin(urls); - int httpCode = http.GET(); - if (httpCode == HTTP_CODE_OK) { - res = http.getString(); - } else { - res = "error"; - } - http.end(); - return res; -} - - - -const String getMethodName(AsyncWebServerRequest* request) { - String res = F("UNKNOWN"); - if (request->method() == HTTP_GET) - res = F("GET"); - else if (request->method() == HTTP_POST) - res = F("POST"); - else if (request->method() == HTTP_DELETE) - res = F("DELETE"); - else if (request->method() == HTTP_PUT) - res = F("PUT"); - else if (request->method() == HTTP_PATCH) - res = F("PATCH"); - else if (request->method() == HTTP_HEAD) - res = F("HEAD"); - else if (request->method() == HTTP_OPTIONS) - res = F("OPTIONS"); - return res; -} - -const String getRequestInfo(AsyncWebServerRequest* request) { - String res = getMethodName(request); - res += ' '; - res += "http://"; - res += request->host(); - res += request->url(); - res += '\n'; - if (request->contentLength()) { - res += "content-type: "; - res += request->contentType(); - res += " content-lenght: "; - res += prettyBytes(request->contentLength()); - res += '\n'; - } - - if (request->headers()) { - res += "headers:\n"; - for (size_t i = 0; i < request->headers(); i++) { - AsyncWebHeader* h = request->getHeader(i); - res += h->name(); - res += '='; - res += h->value(); - res += '\n'; - } - } - - if (request->params()) { - res += "params:\n"; - for (size_t i = 0; i < request->params(); i++) { - AsyncWebParameter* p = request->getParam(i); - if (p->isFile()) { - res += "FILE"; - } else if (p->isPost()) { - res += "POST"; - } else { - res += "GET"; - } - res += ' '; - res += p->name(); - res += ':'; - res += p->value(); - if (p->isFile()) { - res += " size:"; - res += p->size(); - } - res += '\n'; - } - } - return res; -} diff --git a/src/Utils/WiFiUtils.cpp b/src/Utils/WiFiUtils.cpp deleted file mode 100644 index d28a8933..00000000 --- a/src/Utils/WiFiUtils.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Utils/WiFiUtils.h" - -static const char* MODULE = "WiFi"; - -void startSTAMode() { - setLedStatus(LED_SLOW); - pm.info("STA Mode"); - - String ssid = jsonReadStr(configSetupJson, "routerssid"); - String passwd = jsonReadStr(configSetupJson, "routerpass"); - - WiFi.mode(WIFI_STA); - if (ssid == "" && passwd == "") { - WiFi.begin(); - } else { - if (WiFi.begin(ssid.c_str(), passwd.c_str()) == WL_CONNECT_FAILED) { - pm.error("failed on start"); - } - } - - bool keepConnecting = true; - uint8_t tries = 20; - int8_t connRes; - do { -#ifdef ESP8266 - connRes = WiFi.waitForConnectResult(1000); -#else - byte connRes = WiFi.waitForConnectResult(); -#endif - switch (connRes) { - case WL_NO_SSID_AVAIL: { - pm.error("no network"); - keepConnecting = false; - } break; - case WL_CONNECTED: { - String hostIpStr = WiFi.localIP().toString(); - pm.info("http://" + hostIpStr); - jsonWriteStr(configSetupJson, "ip", hostIpStr); - keepConnecting = false; - } break; - case WL_CONNECT_FAILED: { - pm.error("check credentials"); - jsonWriteInt(configOptionJson, "pass_status", 1); - keepConnecting = false; - } break; - default: - break; - } - } while (keepConnecting && tries--); - - if (isNetworkActive()) { - MqttClient::init(); - setLedStatus(LED_OFF); - } else { - pm.error("failed: " + String(connRes, DEC)); - startAPMode(); - }; -} - -bool startAPMode() { - setLedStatus(LED_ON); - pm.info("AP Mode"); - - String ssid = jsonReadStr(configSetupJson, "apssid"); - String passwd = jsonReadStr(configSetupJson, "appass"); - - WiFi.mode(WIFI_AP); - - WiFi.softAP(ssid.c_str(), passwd.c_str()); - String hostIpStr = WiFi.softAPIP().toString(); - pm.info("Host IP: " + hostIpStr); - jsonWriteStr(configSetupJson, "ip", hostIpStr); - - ts.add( - WIFI_SCAN, 10 * 1000, - [&](void*) { - String sta_ssid = jsonReadStr(configSetupJson, "routerssid"); - pm.info("scanning for " + sta_ssid); - if (scanWiFi(sta_ssid)) { - ts.remove(WIFI_SCAN); - startSTAMode(); - } - }, - nullptr, true); - - return true; -} - -boolean scanWiFi(String ssid) { - bool res = false; - int8_t n = WiFi.scanComplete(); - pm.info("scan result: " + String(n, DEC)); - if (n == -2) { - // не было запущено, запускаем - pm.info("start scanning"); - // async, show_hidden - WiFi.scanNetworks(true, false); - } else if (n == -1) { - // все еще выполняется - pm.info("scanning in progress"); - } else if (n == 0) { - // не найдена ни одна сеть - pm.info("no networks found"); - WiFi.scanNetworks(true, false); - } else if (n > 0) { - for (int8_t i = 0; i < n; i++) { - if (WiFi.SSID(i) == ssid) { - res = true; - } - pm.info((res ? "*" : "") + String(i, DEC) + ") " + WiFi.SSID(i)); - } - } - return res; -} - -boolean isNetworkActive() { - return WiFi.status() == WL_CONNECTED; -} diff --git a/src/Web.cpp b/src/Web.cpp deleted file mode 100644 index b312d64f..00000000 --- a/src/Web.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "Web.h" -#include "ItemsList.h" -#include "Global.h" -#include "Init.h" -#include "Class/NotAsinc.h" - -static const char* MODULE = "Web"; - -bool parseRequestForPreset(AsyncWebServerRequest* request, uint8_t& preset) { - if (request->hasArg("preset")) { - preset = request->getParam("preset")->value().toInt(); - return true; - } - return false; -} - -void web_init() { - // dnsServer.start(53, "*", WiFi.softAPIP()); - // server.addHandler(new CaptiveRequestHandler(jsonReadStr(configSetupJson, "name").c_str())).setFilter(ON_AP_FILTER); - - server.on("/restart", HTTP_GET, [](AsyncWebServerRequest* request) { - if (request->hasArg("device")) { - if (request->getParam("device")->value() == "ok") { - ESP.restart(); - } - request->send(200); - }; - }); - - server.on("/set", HTTP_GET, [](AsyncWebServerRequest* request) { - - //==============================presets=========================================================================================================== - //uint8_t preset; - //if (parseRequestForPreset(request, preset)) { - // pm.info("activate #" + String(preset, DEC)); - // String configFile = DEVICE_CONFIG_FILE; - // String scenarioFile = DEVICE_SCENARIO_FILE; - // copyFile(getConfigFile(preset, CT_CONFIG), configFile); - // copyFile(getConfigFile(preset, CT_SCENARIO), scenarioFile); - // Device_init(); - // loadScenario(); - // request->redirect("/?set.device"); - //} - - //==============================set.device.json==================================================================================================== - if (request->hasArg("addItem")) { - String name = request->getParam("addItem")->value(); - addItem(name); - Device_init(); - request->redirect("/?set.device"); - } - - if (request->hasArg("delAllItems")) { - delAllItems(); - Device_init(); - request->redirect("/?set.device"); - } - - if (request->hasArg("saveItems")) { - Device_init(); - request->redirect("/?set.device"); - } - - //==============================init==================================================================================================== - if (request->hasArg("devinit")) { - Device_init(); - request->send(200); - } - - if (request->hasArg("scen")) { - bool value = request->getParam("scen")->value().toInt(); - jsonWriteBool(configSetupJson, "scen", value); - saveConfig(); - loadScenario(); - request->send(200); - } - - if (request->hasArg("sceninit")) { - loadScenario(); - request->send(200); - } - -#ifdef LOGGING_ENABLED - if (request->hasArg("cleanlog")) { - clean_log_date(); - request->send(200); - } -#endif - //==============================udp settings============================================= - if (request->hasArg("udponoff")) { - bool value = request->getParam("udponoff")->value().toInt(); - jsonWriteBool(configSetupJson, "udponoff", value); - saveConfig(); - loadScenario(); - request->send(200); - } - - if (request->hasArg("updatelist")) { - removeFile("/dev.csv"); - addFileLn("dev.csv", "device id;device name;ip address"); - request->redirect("/?set.udp"); - } - - if (request->hasArg("updatepage")) { - request->redirect("/?set.udp"); - } - - if (request->hasArg("devname")) { - jsonWriteStr(configSetupJson, "name", request->getParam("devname")->value()); - saveConfig(); - request->send(200); - } - //==============================wifi settings============================================= - if (request->hasArg("routerssid")) { - jsonWriteStr(configSetupJson, "routerssid", request->getParam("routerssid")->value()); - saveConfig(); - request->send(200); - } - - if (request->hasArg("routerpass")) { - jsonWriteStr(configSetupJson, "routerpass", request->getParam("routerpass")->value()); - saveConfig(); - request->send(200); - } - - if (request->hasArg("apssid")) { - jsonWriteStr(configSetupJson, "apssid", request->getParam("apssid")->value()); - saveConfig(); - request->send(200, "text/text", "OK"); - } - - if (request->hasArg("appass")) { - jsonWriteStr(configSetupJson, "appass", request->getParam("appass")->value()); - saveConfig(); - request->send(200); - } - - if (request->hasArg("weblogin")) { - jsonWriteStr(configSetupJson, "weblogin", request->getParam("weblogin")->value()); - saveConfig(); - request->send(200); - } - - if (request->hasArg("webpass")) { - jsonWriteStr(configSetupJson, "webpass", request->getParam("webpass")->value()); - saveConfig(); - request->send(200); - } - - if (request->hasArg("timezone")) { - String timezoneStr = request->getParam("timezone")->value(); - jsonWriteStr(configSetupJson, "timezone", timezoneStr); - saveConfig(); - timeNow->setTimezone(timezoneStr.toInt()); - request->send(200); - } - - if (request->hasArg("ntp")) { - String ntpStr = request->getParam("ntp")->value(); - jsonWriteStr(configSetupJson, "ntp", ntpStr); - saveConfig(); - timeNow->setNtpPool(ntpStr); - request->send(200); - } - - if (request->hasArg("blink")) { - bool value = request->getParam("blink")->value().toInt(); - jsonWriteBool(configSetupJson, "blink", value); - saveConfig(); - request->send(200); - } - //==============================mqtt settings============================================= - if (request->hasArg("mqttServer")) { - jsonWriteStr(configSetupJson, "mqttServer", request->getParam("mqttServer")->value()); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - request->send(200); - } - if (request->hasArg("mqttPort")) { - int port = (request->getParam("mqttPort")->value()).toInt(); - jsonWriteInt(configSetupJson, "mqttPort", port); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - request->send(200); - } - if (request->hasArg("mqttPrefix")) { - jsonWriteStr(configSetupJson, "mqttPrefix", request->getParam("mqttPrefix")->value()); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - request->send(200); - } - if (request->hasArg("mqttUser")) { - jsonWriteStr(configSetupJson, "mqttUser", request->getParam("mqttUser")->value()); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - request->send(200); - } - if (request->hasArg("mqttPass")) { - jsonWriteStr(configSetupJson, "mqttPass", request->getParam("mqttPass")->value()); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - request->send(200); - } - - if (request->hasArg("mqttsend")) { - myNotAsincActions->make(do_MQTTUDP); - request->send(200); - } - - if (request->hasArg("mqttcheck")) { - String buf = "" + MqttClient::getStateStr(); - - String payload = "{}"; - jsonWriteStr(payload, "title", buf); - jsonWriteStr(payload, "class", "pop-up"); - - request->send(200, "text/html", payload); - } - - //==============================push settings============================================= -#ifdef PUSH_ENABLED - if (request->hasArg("pushingboxid")) { - jsonWriteStr(configSetupJson, "pushingboxid", request->getParam("pushingboxid")->value()); - saveConfig(); - request->send(200); - } -#endif - - //==============================utilities settings============================================= - if (request->hasArg(TAG_I2C)) { - busScanFlag = true; - busToScan = BS_I2C; - request->redirect("/?set.utilities"); - } else if (request->hasArg(TAG_ONE_WIRE)) { - busScanFlag = true; - busToScan = BS_ONE_WIRE; - if (request->hasParam(TAG_ONE_WIRE_PIN)) { - setConfigParam(TAG_ONE_WIRE_PIN, request->getParam(TAG_ONE_WIRE_PIN)->value()); - } - request->redirect("/?set.utilities"); - } else if (request->hasArg(TAG_ONE_WIRE_PIN)) { - setConfigParam(TAG_ONE_WIRE_PIN, request->getParam(TAG_ONE_WIRE_PIN)->value()); - request->send(200); - } - }); - - //==============================list of items===================================================== - //server.on("/del", HTTP_GET, [](AsyncWebServerRequest* request) { - // if (request->hasArg("file")) { - // itemsFile = request->getParam("file")->value(); - // } - // if (request->hasArg("line")) { - // itemsLine = request->getParam("line")->value(); - // } - // delElementFlag = true; - // Device_init(); - // request->redirect("/?setn.device"); - //}); - - /* - * Check - */ - server.on("/check", HTTP_GET, [](AsyncWebServerRequest* request) { - myNotAsincActions->make(do_GETLASTVERSION); - pm.info("firmware version: " + lastVersion); - - if (!FLASH_4MB) { - lastVersion = "less"; - } else if (isNetworkActive()) { - lastVersion = "nowifi"; - } - - String msg = ""; - if (lastVersion == FIRMWARE_VERSION) { - msg = F("Актуальная версия прошивки уже установлена."); - } else if (lastVersion != FIRMWARE_VERSION) { - msg = F("Новая версия прошивкиИдет обновление прошивки, после обновления страница перезагрузится автоматически...')\">Установить"); - } else if (lastVersion == "error") { - msg = F("Cервер не найден. Попробуйте повторить позже..."); - } else if (lastVersion == "") { - msg = F("Нажмите на кнопку \"обновить прошивку\" повторно..."); - } else if (lastVersion == "less") { - msg = F("Обновление \"по воздуху\" не поддерживается!"); - } else if (lastVersion == "nowifi") { - msg = F("Устройство не подключено к роутеру!"); - } else if (lastVersion == "notsupported") { - msg = F("Обновление возможно только через usb!"); - } - String tmp = "{}"; - jsonWriteStr(tmp, "title", "" + msg); - jsonWriteStr(tmp, "class", "pop-up"); - request->send(200, "text/html", tmp); - }); - - /* - * Upgrade - */ - server.on("/upgrade", HTTP_GET, [](AsyncWebServerRequest* request) { - myNotAsincActions->make(do_UPGRADE);; - request->send(200, "text/html"); - }); -} - -void setConfigParam(const char* param, const String& value) { - pm.info("set " + String(param) + ": " + value); - jsonWriteStr(configSetupJson, param, value); - saveConfig(); -} \ No newline at end of file diff --git a/src/WebServer.cpp b/src/WebServer.cpp deleted file mode 100644 index 7b31056d..00000000 --- a/src/WebServer.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "HttpServer.h" - -#include "Utils/FileUtils.h" -#include "Utils/WebUtils.h" -#include "FSEditor.h" - -namespace HttpServer { - -static const char *MODULE = "Http"; -/* Forward declaration */ -void initOta(); -void initMDNS(); -void initWS(); - -void init() { - String login = jsonReadStr(configSetupJson, "weblogin"); - String pass = jsonReadStr(configSetupJson, "webpass"); -#ifdef FSEditor -#ifdef ESP32 - server.addHandler(new FSEditor(LittleFS, login, pass)); -#else - server.addHandler(new FSEditor(login, pass)); -#endif -#endif - - server.serveStatic("/css/", LittleFS, "/css/").setCacheControl("max-age=600"); - server.serveStatic("/js/", LittleFS, "/js/").setCacheControl("max-age=600"); - server.serveStatic("/favicon.ico", LittleFS, "/favicon.ico").setCacheControl("max-age=600"); - server.serveStatic("/icon.jpeg", LittleFS, "/icon.jpeg").setCacheControl("max-age=600"); - - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm").setAuthentication(login.c_str(), pass.c_str()); - - server.onNotFound([](AsyncWebServerRequest *request) { - pm.error("not found:\n" + getRequestInfo(request)); - request->send(404); - }); - - server.onFileUpload([](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { - // TODO - if (!index) { - pm.info("start upload " + filename); - } - if (final) { - pm.info("finish upload: " + prettyBytes(index + len)); - } - }); - - // динамические данные - server.on("/config.live.json", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200, "application/json", configLiveJson); - }); - - // данные не являющиеся событиями - server.on("/config.option.json", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200, "application/json", configOptionJson); - }); - - // для хранения постоянных данных - server.on("/config.setup.json", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200, "application/json", configSetupJson); - }); - - server.on("/cmd", HTTP_GET, [](AsyncWebServerRequest *request) { - String cmdStr = request->getParam("command")->value(); - pm.info("do: " + cmdStr); - addCommandLoop(cmdStr); - request->send(200, "text/html", "OK"); - }); - - server.begin(); - - initOta(); - initMDNS(); - initWS(); -} - -void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { -#ifdef WS_enable - if (type == WS_EVT_CONNECT) { - Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); - client->printf(json.c_str(), client->id()); - //client->ping(); - } else if (type == WS_EVT_DISCONNECT) { - Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); - } else if (type == WS_EVT_ERROR) { - Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data); - } else if (type == WS_EVT_PONG) { - Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : ""); - } else if (type == WS_EVT_DATA) { - AwsFrameInfo *info = (AwsFrameInfo *)arg; - String msg = ""; - if (info->final && info->index == 0 && info->len == len) { - //the whole message is in a single frame and we got all of it's data - Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len); - - if (info->opcode == WS_TEXT) { - for (size_t i = 0; i < info->len; i++) { - msg += (char)data[i]; - } - } else { - char buff[3]; - for (size_t i = 0; i < info->len; i++) { - sprintf(buff, "%02x ", (uint8_t)data[i]); - msg += buff; - } - } - Serial.printf("%s\n", msg.c_str()); - - if (info->opcode == WS_TEXT) - client->text("{}"); - else - client->binary("{}"); - } else { - //message is comprised of multiple frames or the frame is split into multiple packets - if (info->index == 0) { - if (info->num == 0) - Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); - } - - Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len); - - if (info->opcode == WS_TEXT) { - for (size_t i = 0; i < len; i++) { - msg += (char)data[i]; - } - } else { - char buff[3]; - for (size_t i = 0; i < len; i++) { - sprintf(buff, "%02x ", (uint8_t)data[i]); - msg += buff; - } - } - Serial.printf("%s\n", msg.c_str()); - - if ((info->index + len) == info->len) { - Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); - if (info->final) { - Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary"); - if (info->message_opcode == WS_TEXT) - client->text("I got your text message"); - else - client->binary("I got your binary message"); - } - } - } - } -#endif - ; -} - -void initMDNS() { -#ifdef MDNS_ENABLED - MDNS.addService("http", "tcp", 80); - // TODO Add Adduino OTA -#endif - ; -} - -void initOta() { -#ifdef OTA_UPDATES_ENABLED - ArduinoOTA.onStart([]() { - events.send("Update Start", "ota"); - }); - ArduinoOTA.onEnd([]() { - events.send("Update End", "ota"); - }); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - char p[32]; - sprintf(p, "Progress: %u%%\n", (progress / (total / 100))); - events.send(p, "ota"); - }); - ArduinoOTA.onError([](ota_error_t error) { - if (error == OTA_AUTH_ERROR) - events.send("Auth Failed", "ota"); - else if (error == OTA_BEGIN_ERROR) - events.send("Begin Failed", "ota"); - else if (error == OTA_CONNECT_ERROR) - events.send("Connect Failed", "ota"); - else if (error == OTA_RECEIVE_ERROR) - events.send("Recieve Failed", "ota"); - else if (error == OTA_END_ERROR) - events.send("End Failed", "ota"); - }); - ArduinoOTA.setHostname(hostName); - ArduinoOTA.begin(); -#endif - ; -} - -void initWS() { -#ifdef WS_enable - ws.onEvent(onWsEvent); - server.addHandler(&ws); - events.onConnect([](AsyncEventSourceClient *client) { - client->send("", NULL, millis(), 1000); - }); - server.addHandler(&events); -#endif - ; -} - -} // namespace HttpServer \ No newline at end of file diff --git a/src/Widgets.cpp b/src/Widgets.cpp deleted file mode 100644 index 1f8b07ca..00000000 --- a/src/Widgets.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "Global.h" - -static const char* MODULE = "Widget"; - -const String getWidgetFile(const String& name); - -bool loadWidget(const String& filename, String& buf) { - buf = readFile(getWidgetFile(filename), 2048); - bool res = !(buf == "Failed" || buf == "Large"); - if (!res) { - pm.error("on load" + filename); - } - return res; -} - -void createWidget(String descr, String page, String order, String filename, String topic) { - String buf = "{}"; - if (!loadWidget(filename, buf)) { - return; - } - descr.replace("#", " "); - page.replace("#", " "); - - jsonWriteStr(buf, "page", page); - jsonWriteStr(buf, "order", order); - jsonWriteStr(buf, "descr", descr); - jsonWriteStr(buf, "topic", prex + "/" + topic); - -#ifdef LAYOUT_IN_RAM - all_widgets += widget + "\r\n"; -#else - addFileLn("layout.txt", buf); -#endif -} - -//TODO Вот эта процедура, и несколько оберток -void createWidgetParam(String widget, String page, String pageNumber, String filename, String topic, - String name1, String param1, String name2, String param2, String name3, String param3) { - String buf = ""; - if (!loadWidget(filename, buf)) { - return; - } - - widget.replace("#", " "); - page.replace("#", " "); - - jsonWriteStr(buf, "page", page); - jsonWriteStr(buf, "order", pageNumber); - jsonWriteStr(buf, "descr", widget); - jsonWriteStr(buf, "topic", prex + "/" + topic); - - if (name1) jsonWriteStr(buf, name1, param1); - if (name2) jsonWriteStr(buf, name2, param2); - if (name3) jsonWriteStr(buf, name3, param3); - -#ifdef LAYOUT_IN_RAM - all_widgets += widget + "\r\n"; -#else - addFileLn("layout.txt", buf); -#endif -} - -void createChart(String widget, String page, String pageNumber, String filename, String topic, - String maxCount) { - String buf = ""; - if (!loadWidget(filename, buf)) { - return; - } - - widget.replace("#", " "); - page.replace("#", " "); - - jsonWriteStr(buf, "page", page); - jsonWriteStr(buf, "order", pageNumber); - //jsonWriteStr(widget, "descr", widget_name); - jsonWriteStr(buf, "series", widget); - jsonWriteStr(buf, "maxCount", maxCount); - jsonWriteStr(buf, "topic", prex + "/" + topic); - -#ifdef LAYOUT_IN_RAM - all_widgets += widget + "\r\n"; -#else - addFileLn("layout.txt", buf); -#endif -} - -void createWidgetByType(String widget, String page, String pageNumber, String type, String topic) { - createWidget(widget, page, pageNumber, getWidgetFile(type), topic); -} - -const String getWidgetFile(const String& name) { - return "/widgets/" + name + ".json"; -} diff --git a/src/bus.cpp b/src/bus.cpp deleted file mode 100644 index 8e210398..00000000 --- a/src/bus.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "Bus/BusScannerFactory.h" -#include "Class/NotAsinc.h" -#include "Global.h" - -void busInit() { - myNotAsincActions->add( - do_BUSSCAN, [&](void*) { - doBusScan(); - }, - nullptr); -} - -void doBusScan() { - String res = ""; - BusScanner* scanner = BusScannerFactory::get(configSetupJson, busToScan, res); - scanner->scan(); - jsonWriteStr(configLiveJson, String(scanner->tag()), res); -} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index f5ecc388..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -#include -<<<<<<< HEAD -#include "test.h" -======= - ->>>>>>> parent of 01248f4... esp32 -#include "Class/CallBackTest.h" -#include "Class/NotAsinc.h" -#include "Class/ScenarioClass.h" -#include "Class/Switch.h" -#include "Cmd.h" -#include "Global.h" -#include "Init.h" -#include "ItemsList.h" -#include "Utils/Timings.h" -#include "Utils\WebUtils.h" - -void not_async_actions(); - -static const char* MODULE = "Main"; - -Timings metric; -boolean initialized = false; - -void setup() { - WiFi.setAutoConnect(false); - WiFi.persistent(false); - - Serial.begin(115200); - Serial.flush(); - Serial.println(); - Serial.println("--------------started----------------"); - - setChipId(); - - myNotAsincActions = new NotAsinc(do_LAST); - myScenario = new Scenario(); - - pm.info("FS"); - fileSystemInit(); - - pm.info("Config"); - loadConfig(); - - pm.info("Clock"); - clock_init(); - - pm.info("Commands"); - cmd_init(); - - pm.info("Sensors"); - sensorsInit(); - - pm.info("Init"); - all_init(); - - pm.info("Network"); - startSTAMode(); - - pm.info("Uptime"); - uptime_init(); - - pm.info("telemetry"); - telemetry_init(); - - pm.info("Updater"); - upgradeInit(); - - pm.info("HttpServer"); - HttpServer::init(); - - pm.info("WebAdmin"); - web_init(); - -#ifdef UDP_ENABLED - pm.info("Broadcast UDP"); - udpInit(); -#endif -#ifdef SSDP - pm.info("Ssdp Init"); - SsdpInit(); -#endif - - ts.add( - TEST, 1000 * 60, [&](void*) { - pm.info(printMemoryStatus()); - }, - nullptr, true); - - just_load = false; - initialized = true; - - setupTest(); -} - -void loop() { - if (!initialized) { - return; - } -#ifdef OTA_UPDATES_ENABLED - ArduinoOTA.handle(); -#endif -#ifdef WS_enable - ws.cleanupClients(); -#endif -#ifdef UDP_ENABLED - loopUdp(); -#endif - timeNow->loop(); - MqttClient::loop(); - mySwitch->loop(); - myScenario->loop(); - loopTest(); - loopCmd(); - loopSerial(); - - myNotAsincActions->loop(); - ts.update(); -} - -void clock_init() { - timeNow = new Clock(); - timeNow->setNtpPool(jsonReadStr(configSetupJson, "ntp")); - timeNow->setTimezone(jsonReadStr(configSetupJson, "timezone").toInt()); - - ts.add( - TIME_SYNC, 30000, [&](void*) { - timeNow->hasSync(); - }, - nullptr, true); -} diff --git a/src/test.cpp b/src/test.cpp deleted file mode 100644 index e810dec3..00000000 --- a/src/test.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "test.h" - -#include "Global.h" - -void setupTest() { - //пример выделения подстрок - String buf = "Geeks-for-Geeks"; - int buf_len = buf.length() + 1; - char char_array[buf_len]; - buf.toCharArray(char_array, buf_len); - char* token = strtok(char_array, "-"); - while (token != NULL) { - printf("%s\n", token); - token = strtok(NULL, "-"); - } - - //char str[] = "Geeks for Geeks"; - //char* token; - //char* rest = str; - //while ((token = strtok_r(rest, " ", &rest))) { - // printf("%s\n", token); - //} -} - -void loopTest() { -} - -//char stringToCharArray(int& i, String in) { -// int in_len = in.length() + 1; -// i = in_len; -// char char_array[in_len]; -// in.toCharArray(char_array, in_len); -// return char_array[in_len]; -//} \ No newline at end of file diff --git a/src/udp_.cpp b/src/udp_.cpp deleted file mode 100644 index a94d49b0..00000000 --- a/src/udp_.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "Class/NotAsinc.h" -#include "udp.h" -#include "udp_.h" -#include "Global.h" - -static const char* MODULE = "Udp"; - -#ifdef ESP8266 -IPAddress udp_multicastIP(255, 255, 255, 255); -WiFiUDP udp; -#endif -#ifdef ESP32 -IPAddress udp_multicastIP(239, 255, 255, 255); -AsyncUDP udp; -#endif -String remote_ip; -String received; -int udp_period; -boolean udp_busy = false; -unsigned int udp_port = 4210; - - -#ifdef UDP_ENABLED -void udpInit() { - myNotAsincActions->add( - do_UDPDATAPARSE, [&](void*) { - do_udp_data_parse(); - }, - nullptr); - - myNotAsincActions->add( - do_MQTTUDP, [&](void*) { - send_mqtt_to_udp(); - }, - nullptr); - - removeFile("dev.csv"); - addFileLn("dev.csv", "device id;device name;ip address"); - -#ifdef ESP8266 - udp.begin(udp_port); -#endif - - handleUdp_esp32(); - - randomSeed(micros()); - udp_period = random(50000, 60000); - - ts.add( - UDP, udp_period, [&](void*) { - if (jsonReadBool(configSetupJson, "udponoff") && isNetworkActive() && !udp_busy) { - pm.info("send info"); - String payload = "iotm;"; - payload += chipId; - payload += ";"; - payload += jsonReadStr(configSetupJson, "name"); -#ifdef ESP8266 - udp.beginPacketMulticast(udp_multicastIP, udp_port, WiFi.localIP()); - udp.write(payload.c_str()); - udp.endPacket(); -#endif -#ifdef ESP32 - udp.broadcast(line_to_send.c_str()); -#endif - } - }, - nullptr, false); -} - -bool isUdpEnabled() { - return jsonReadBool(configSetupJson, "udponoff") && isNetworkActive(); -} - -void loopUdp() { -#ifdef ESP8266 - if (!isUdpEnabled()) { - return; - } - - int packetSize = udp.parsePacket(); - if (!packetSize) { - return; - } - - char udp_packet[255]; - remote_ip = udp.remoteIP().toString(); - - pm.info(prettyBytes(packetSize) + " from " + remote_ip + ":" + udp.remotePort()); - - int len = udp.read(udp_packet, 255); - if (len) { - udp_packet[len] = '\x00'; - } - received = String(udp_packet); - if (received.indexOf("iotm;") >= 0 || received.indexOf("mqttServer") >= 0) { - myNotAsincActions->make(do_UDPDATAPARSE); - } -#endif - ; -} - -void handleUdp_esp32() { -#ifdef ESP32 - if (udp.listenMulticast(udp_multicastIP, udp_port)) { - udp.onPacket([](AsyncUDPPacket packet) { - received = (char*)packet.data(); - remote_ip = packet.remoteIP().toString(); - if (jsonReadStr(configSetupJson, "udponoff") == "1") { - if (received.indexOf("iotm;") >= 0) { - myNotAsincActions->make(do_UDPDATAPARSE); - } - if (received.indexOf("mqttServer") >= 0) { - myNotAsincActions->make(do_UDPDATAPARSE); - } - } - }); - } -#endif -} - -void do_udp_data_parse() { - if (received.indexOf("mqttServer") >= 0) { - pm.info("received setting"); - jsonWriteStr(configSetupJson, "mqttServer", jsonReadStr(received, "mqttServer")); - jsonWriteInt(configSetupJson, "mqttPort", jsonReadInt(received, "mqttPort")); - jsonWriteStr(configSetupJson, "mqttPrefix", jsonReadStr(received, "mqttPrefix")); - jsonWriteStr(configSetupJson, "mqttUser", jsonReadStr(received, "mqttUser")); - jsonWriteStr(configSetupJson, "mqttPass", jsonReadStr(received, "mqttPass")); - saveConfig(); - myNotAsincActions->make(do_MQTTPARAMSCHANGED); - } - if (received.indexOf("iotm;") >= 0) { - add_dev_in_list("dev.csv", selectFromMarkerToMarker(received, ";", 1), selectFromMarkerToMarker(received, ";", 2), received); - } -} - -void add_dev_in_list(String filename, String id, String dev_name, String ip) { - auto file = seekFile("/" + filename); - if (!file.find(id.c_str())) { - addFileLn(filename, id + ";" + dev_name + "; " + ip + ""); - } -} - -void send_mqtt_to_udp() { - if (!isUdpEnabled()) { - return; - } - udp_busy = true; - String data = "{}"; - jsonWriteStr(data, "mqttServer", jsonReadStr(configSetupJson, "mqttServer")); - jsonWriteInt(data, "mqttPort", jsonReadInt(configSetupJson, "mqttPort")); - jsonWriteStr(data, "mqttPrefix", jsonReadStr(configSetupJson, "mqttPrefix")); - jsonWriteStr(data, "mqttUser", jsonReadStr(configSetupJson, "mqttUser")); - jsonWriteStr(data, "mqttPass", jsonReadStr(configSetupJson, "mqttPass")); -#ifdef ESP8266 - udp.beginPacketMulticast(udp_multicastIP, udp_port, WiFi.localIP()); - udp.write(data.c_str()); - udp.endPacket(); -#endif -#ifdef ESP32 - udp.broadcast(mqtt_data.c_str()); -#endif - pm.info("sent info"); - udp_busy = false; -} - -#endif \ No newline at end of file diff --git a/tools/littlefsbuilder.py b/tools/littlefsbuilder.py deleted file mode 100644 index df1fb52d..00000000 --- a/tools/littlefsbuilder.py +++ /dev/null @@ -1,2 +0,0 @@ -Import("env") -env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/tools/mklittlefs.exe' ) diff --git a/tools/mklittlefs.exe b/tools/mklittlefs.exe deleted file mode 100644 index 7de93ff2..00000000 Binary files a/tools/mklittlefs.exe and /dev/null differ