Библиотека для создания простого веб-интерфейса настроек на esp8266/esp32
- Веб-приложение весит около 15кб и вшивается в программу в бинарном gzip виде без возни с файлами
- Удобный билдер панели управления из скетча
- Десяток типовых виджетов с возможностью объединения в группы и вложенные меню
- Система авторизации с разными правами для авторизованных юзеров и гостей
- Файловый менеджер и OTA (обновление по воздуху)
- Интеграция с библиотекой GyverDB для полностью автоматического хранения данных
- Компактный бинарный протокол связи
- Легко адаптируется под любую библиотеку HTTP сервера, из коробки реализовано три версии: GyverHTTP, стандартный esp-WebServer, ESPAsyncWebserver
- Это GyverHub на минималках
- Исходник веб-приложения здесь
Есть Android-приложение для поиска устройств с библиотекой
ESP8266, ESP32
- GTL v1.1.14+
- GyverDB v1.1.8+
- StringUtils v1.4.28+
- GyverHTTP v1.0.23+
- BSON v2.0.0+
При установке из реестра PIO или Arduino IDE все зависимости установятся автоматически
platformio.ini
[env]
framework = arduino
lib_deps =
GyverLibs/Settings
;esphome/ESPAsyncWebServer-esphome ; для версии SettingsAsync
;esphome/ESPAsyncTCP-esphome ; для версии SettingsAsync
[env:d1_mini]
platform = espressif8266
board = d1_mini
upload_speed = 921600
monitor_speed = 115200
monitor_filters = esp8266_exception_decoder, default
build_type = debug
board_build.filesystem = littlefs
[env:esp32dev]
monitor_speed = 115200
platform = espressif32
board = esp32dev
upload_speed = 921600
board_build.filesystem = littlefs
[env:esp32-c3]
monitor_speed = 115200
platform = espressif32
board = esp32dev
board_build.mcu = esp32c3
upload_speed = 2000000
board_build.f_cpu = 80000000L
board_build.filesystem = littlefs
Вебморда является самостоятельным веб-приложением, написанным на html/css/js. Её файлы минифицированы, сжаты в gz и вшиты в код библиотеки как PROGMEM массив. В библиотеке настроен вебсервер, который отправляет файлы вебморды при заходе на IP платы в браузере. Лёгкий html файл подгружается каждый раз, а скрипты и стили кешируются браузером для ускорения загрузки. Вебморда общается с платой по http: например при загрузке запрашивает пакет с виджетами и прочей информацией, пакеты имеют формат бинарного json.
Во всех трёх реализациях сервера из коробки настроен DNS для работы как Captive portal - если ESP работает в режиме точки доступа (AP или AP_STA), то при подключении к точке автоматически откроется окно браузера со страницей настроек.
Позволяет находить устройства с библиотекой в локальной сети и заменяет браузер, вебморда открывается сразу в приложении, кнопка назад возвращает к списку устройств. Чтобы удалить устройство - долгое удержание на нём на смартфоне или правой кнопкой мыши на ПК. Для поиска смартфон/ПК должны быть в одной локальной сети с устройством. В приложении должна быть указана корректная маска подсети (настраивается в роутере). Если в роутере она не менялась - то она там стандартная 255.255.255.0, как и в приложении по умолчанию.
- Скачать .apk последней версии
- Скачать .html последней версии
Требуется версия библиотеки v1.0.13+
Пакет с виджетами собирается устройством в билдере - функция в программе, которая вызывается, когда приходит запрос от вебморды. Внутри билдера нужно вызвать методы виджетов в том порядке, в котором они должны находиться в вебморде.
// минимальный код
SettingsGyver sett;
void build(sets::Builder& b) {
// b.Input(...);
// b.Button(...);
}
void setup() {
// подключение к WiFi...
sett.begin();
sett.onBuild(build);
}
void loop() {
sett.tick();
}
У всех виджетов есть вариант функции с ID и без ID. ID виджета нужен для:
- Работа с подключенной базой данных на чтение и запись значений
- Отправка обновлений на виджет
- Разбор действий отдельно от вывода виджетов, чтобы разделить UI и обработку
ID в данной библиотеке задаётся числом, тип size_t
(на ESP это 32-бит беззнаковое целое). Можно задавать ID:
- Просто числом вручную
enum
- Хэш-строки из библиотеки StringUtils:
SH("my_input")
или"my_input"_h
- Хэш-ключи
DB_KEYS
из библиотеки GyverDB
Если ID не задан - он будет присваиваться библиотекой автоматически (только для активных виджетов). Автоматический ID - это число от
UINT32_MAX
, уменьшается на 1 с каждым вызовом. Если автоматический ID совпадёт с каким то из вручную заданных - в вебморде высветится ошибкаDuplicated ID
Не рекомендуется задавать ID числом
0
, т.к.b.build.id == 0
при запросе виджетов
enum keys : size_t {
my_inp = 1,
button,
};
DB_KEYS(
kk,
my_inp,
button
);
void build(sets::Builder& b) {
b.Input("My input"); // без ID
b.Input("my_inp"_h, "My input"); // хэш-строка
b.Input(SH("my_inp"), "My input"); // хэш-строка
b.Input(12, "My input"); // вручную числом
b.Input(keys::my_inp, "My input"); // enum
b.Input(kk::my_inp, "My input"); // GyverDB-хэш
}
Есть несколько способов взаимодействия с виджетами, т.е. отправки и получения значений:
- У виджета можно задать ID, по которому библиотека будет автоматически читать и писать данные в базу данных GyverDB
- К виджету можно подключить переменную, библиотека будет читать из неё значение и писать при изменении
- У виджета без ID и переменной будет значение по умолчанию (0 или пустая строка), но получить новое значение с вебморды можно из инфо о билде
- Активный виджет (значение можно менять из вебморды) возвращает
true
при изменении значения пользователем, также при клике по кнопке
String str;
char cstr[20];
void build(sets::Builder& b) {
// виджет без id и начального значения
// При установке с вебморды получаем значение напрямую
if (b.Input("My input")) {
Serial.println(b.build.value);
}
// виджет без id с привязанной String-строкой
// при установке с вебморды значение запишется в строку
b.Input("My input", &str);
b.Input("My input", AnyPtr(cstr, 20)); // для char-массивов
// виджет с id без привязанной переменной
// будет работать с базой данных по указанному ключу
b.Input("my_inp"_h, "My input");
// действие возвращается независимо от наличия id
if (b.Button()) Serial.println("btn 1");
if (b.Button("my_btn2"_h)) Serial.println("btn 2");
}
Здесь
AnyPtr
- тип данных, принимающий указатель на любой встроенный тип (числа, строки). Для передачи ему char-буфера нужно явно вызвать конструктор с указанием размера буфера
void build(sets::Builder& b) {
// можно узнать, было ли действие по виджету
if (b.build.isAction()) {
Serial.print("Set: 0x");
Serial.print(b.build.id, HEX);
Serial.print(" = ");
Serial.println(b.build.value);
}
}
// разделение UI и действий
void build(sets::Builder& b) {
// вывод UI
b.Input("my_inp"_h, "My input");
b.Button("my_btn"_h, "My button");
// обработка действий
switch (b.build.id) {
case "my_inp"_h:
Serial.print("input: ");
Serial.println(b.build.value);
break;
case "my_btn"_h:
Serial.println("btn click");
break;
}
}
Если ID виджета задан и переменная не привязана - будет использоваться БД. Если привязана переменная - будет использоваться она
Text
- это обёртка для текстовых данных из библиотеки StringUtils, см. документацию там. Text может конвертироваться в любые другие типы и сравниваться с ними, а также имеет множество инструментов для парсинга и поиска.
Serial.println(b.build.value); // печататься
b.build.value == 123; // сравниваться
b.build.value == "123"; // сравниваться
b.build.value.toFloat(); // конвертироваться
byte b = b.build.value; // авто-конвертироваться
Библиотека интегрирована с GyverDB - относительно быстрой базой данных для хранения данных любого типа. Settings автоматически читает и обновляет данные в БД, поэтому рекомендуется изучить как работать с БД на странице описания GyverDB. При использовании GyverDBFile база данных будет автоматически писаться в файл при изменениях, а файловая система позаботится об оптимальном износе flash памяти. При запуске рекомендуется инициализировать БД, указав ключи и соответствующие им начальные значения и типы. Эти значения будут записаны только в том случае, если запись в БД ещё не существует. В то же время автоматическое обновление БД работает только для существующих записей, т.е. Settings будет работать только с сущестующими ячейками и не создаст новых. Минимальный пример:
// Подключить библиотеки и создать БД и Settings
#include <GyverDBFile.h>
#include <LittleFS.h>
GyverDBFile db(&LittleFS, "/data.db");
#include <SettingsESP.h>
SettingsESP sett("My Settings", &db);
// Объявить хэш-ключи БД через макрос. Это удобнее, чем "строки" и enum,
// а также не боится изменения порядка или удаления ключей из середины списка
DB_KEYS(
keys,
input,
slider
);
// билдер
void build(sets::Builder& b) {
b.Input(kk::input);
b.Slider(kk::slider);
}
void setup() {
Serial.begin(115200);
Serial.println();
// WIFI
WiFi.mode(WIFI_STA);
WiFi.begin("WIFI_SSID", "WIFI_PASS");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println(WiFi.localIP());
// settings
sett.begin();
sett.onBuild(build);
// запуск файловой системы
#ifdef ESP32
LittleFS.begin(true); // format on fail
#else
LittleFS.begin();
#endif
// запуск БД и чтение из файла
db.begin();
// инициализация БД начальными значениями
db.init(keys::input, "text");
db.init(keys::slider, 30);
}
void loop() {
sett.tick();
}
Можно использовать несколько баз данных, например одна для сохраняемых в память настроек, вторая для "временных" настроек, которые не нужно сохранять при перезагрузке: GyverDBFile
сохраняет в файл, а обычная GyverDB
- нет, живёт чисто в оперативной памяти. Переключаться между БД нужно в билдере таким образом, чтобы после смены БД шли только виджеты с ключами из этой БД. Например
GyverDBFile db_flash(&LittleFS, "/data.db");
GyverDB db_ram;
void build(sets::Builder& b) {
settings.attachDB(&db_ram);
b.Input("input2"_h, "...");
settings.attachDB(&db_flash);
b.Input("input1"_h, "...");
}
После выхода из билдера нужно оставлять подключенной ту БД, для которой нужны автоматические обновления, система не сможет обновляться одновременно с нескольких БД. Также нужно оставлять последней подключенной БД, которая пишет на флешку, чтобы система автоматически вызывала её тикер.
Виджеты собираются линейно, вызов функции виджета добавляет его в вебморду. Это означает, что виджеты можно выводить и динамически, особенно удобно это работает с автоматическим id. Например:
int numbers[5];
void build(sets::Builder& b) {
// вывод в цикле
for (int i = 0; i < 5; i++) {
b.Input();
}
// обработка действий также будет работать
for (int i = 0; i < 5; i++) {
if (b.Input()) Serial.println(b.build.value);
}
// массив number с привязанными переменными
for (int i = 0; i < 5; i++) {
b.Number(String("number #") + i, &numbers[i]);
}
// обработка и действий
for (int i = 0; i < 5; i++) {
if (b.Number(String("number #") + i, &numbers[i])) {
Serial.print(String("number #") + i + ": ");
Serial.println(numbers[i]);
}
}
// можно и так
for (int i = 0; i < 5; i++) {
b.Number(String("number #") + i, &numbers[i]);
if (b.wasSet()) {
Serial.print(String("number #") + i + ": ");
Serial.println(numbers[i]);
b.clearSet();
}
}
}
Также можно динамически скрывать виджеты, например
void build(sets::Builder& b) {
if (flag) {
b.Input();
b.Slider();
// ...
}
}
Частым сценарием является открытие группы настроек с активацией режима, это можно сделать так:
void build(sets::Builder& b) {
if (b.Switch()) {
b.reload(); // перезагрузить вебморду по клику на свитч
}
// здесь flag должен быть прочитан из БД или переменной
if (flag) {
b.Input();
b.Slider();
// ...
}
}
Например с БД
DB_KEYS(
kk,
mode_sw
);
void build(sets::Builder& b) {
// запись в БД и перезагрузка
if (b.Switch(kk::mode_sw)) b.reload();
// чтение из БД
if (db[kk::mode_sw]) {
b.Input();
b.Slider();
// ...
}
}
В системе реализован механизм обновлений - вебморда периодически запрашивает у устройства обновления. Если база данных подключена - то при изменениях значений в базе где то в программе библиотека автоматически отправит новые значения в вебморду (например если какое то значение изменилось при помощи кнопки). Примечание: если вебморда открыта одновременно с нескольких браузеров - обновления базы данных получит только тот из них, который запросил их первым. Также можно отправить свои значения, если база данных не подключена или не используется для каких-то виджетов. Для этого нужно подключить обработчик обновлений и вручную отправить данные по id виджета.
void build(sets::Builder& b) {
b.Label("lbl1"_h, "Random");
b.Label("lbl2"_h, "millis()", "", sets::Colors::Red);
}
void update(sets::Updater& upd) {
upd.update("lbl1"_h, random(100));
upd.update("lbl2"_h, millis());
}
void setup() {
sett.begin();
sett.onBuild(build);
sett.onUpdate(update);
}
Warning
В функции update
нельзя складывать строки, либо результат нужно преобразовать к String: upd.update(id, (String)(str + 123 + "abc"));
В обновлении также можно вызвать
upd.alert(текст)
иupd.notice(текст)
- всплывающее окно с текстом
- Вебморда отслеживает статус устройства, при потере связи появится текст offline в заголовке страницы. После потери связи вебморда будет запрашивать информацию о виджетах, это очень удобно при разработке - например добавляем виджет, загружаем прошивку. За это время вебморда уже понимает что устройство оффлайн и при первом успешном подключении выводит актуальные виджеты.
- При изменении значений виджетов вебморда следит за доставкой пакета, при ошибке связи появится надпись error* у соответствующего виджета
Некоторые параметры вебморды можно менять из скетча. Настройки обновляются при перезагрузке или первой загрузке страницы
sett.config.sliderTout = 100;
sett.config.requestTout = 2000;
sett.config.updateTout = 2500;
Виджеты можно объединять в контейнеры. Контейнер нужно начать и закончить, так как пакет данных собирается линейно в целях оптимизации скорости и памяти. Метод beginКонтейнер
всегда вернёт true для красоты организации кода в блоке условия:
void build(sets::Builder& b) {
if (b.beginGroup("Group 1")) {
b.Input("input1"_h, "Text");
b.endGroup(); // закрыть группу
}
}
Второй вариант - у всех контейнеров есть парный класс, который сам откроет и закроет контейнер. Нужно создать объект с любым именем и передать ему билдер:
void build(sets::Builder& b) {
{
sets::Group g(b, "Group 2"); // должен быть первым в блоке
b.Input("input1"_h, "Text");
}
}
Можно создавать вложенные меню. Указанный заголовок будет отображаться на кнопке и в заголовке страницы при входе на меню. Все виджеты и группы, находящиеся в блоке с меню, будут находиться на отдельной странице. Вложенность меню неограниченная.
void build(sets::Builder& b) {
b.Input("input1"_h, "Text 1");
{
sets::Menu g(b, "Submenu");
b.Input("input2"_h, "Text 2");
}
}
Можно располагать виджеты горизонтально в строку, у них может быть общее название. Если у виджета задано название - он будет пытаться растянуться на всю ширину, если нет - то не будет. Частый вариант использования - первый виджет с названием, остальные мелкие без:
void build(sets::Builder& b) {
{
sets::Row g(b);
// sets::Row g(b, "Row");
b.Slider("Slider");
b.LED();
b.Switch();
}
}
Отдельный тип контейнера - кнопки, внутри него можно добавлять только кнопки:
void build(sets::Builder& b) {
{
sets::Buttons btns(b);
// кнопка вернёт true при клике
if (b.Button("btn1"_h, "Button 1")) {
Serial.println("Button 1");
}
if (b.Button("btn2"_h, "Button 2", sets::Colors::Blue)) {
Serial.println("Button 2");
}
}
}
В системе предусмотрена авторизация: если в прошивке указать отличный от пустой строки пароль - вебморда будет работать в "гостевом" режиме: отображаются только разрешённые гостям виджеты, файловый менеджер и OTA скрыты и заблокированы. Для ввода пароля нужно зайти в меню (правая верхняя кнопка) и нажать на ключик. Серый ключик означает что авторизация отключена, зелёный - клиент авторизован, красный - неверный пароль. Пароль может содержать любые символы и иметь любую длину - в явном виде он не хранится и не передаётся. Пароль сохраняется в браузере и авторизация работает автоматически при перезагрузке страницы.
Для разделения админского и гостевого доступа предусмотрен виртуальный контейнер Guest. Если пароль установлен и клиент не авторизован - он будет видеть только виджеты из гостевых контейнеров. Для корректной работы гостевой контейнер не должен прерываться обычными контейнерами. Пример:
void setup() {
// ...
// sett.setPass("pass1234");
sett.setPass(F("pass1234")); // любая строка
}
void build(sets::Builder& b) {
if (b.beginGroup("Group 1")) {
// гости не видят
b.Pass(kk::pass, "Password");
// виджеты, которые видят гости и админы
{
sets::GuestAccess g(b);
b.Input(kk::uintw, "uint");
b.Input(kk::intw, "int");
b.Input(kk::int64w, "int 64");
}
// гости не видят
{
sets::Menu m(b, "sub sub");
b.Label(kk::lbl2, "millis()", "", sets::Colors::Red);
}
b.endGroup();
}
}
В гостевой контейнер можно поместить несколько обычных контейнеров, например групп.
Примечание: если вложенное меню закрыто от гостей, но содержит ещё одно вложенное меню - кнопка открытия меню будет отображаться, но само меню будет пустым
Можно использовать emoji, они неплохо смотрятся в меню. Например с удобного сайта
b.Input(kk::intw, "🔈Громкость");
Особенности некоторых виджетов
Time
- принимает и отправляет время в секундах с начала суток независимо от часового пояса браузераDate
/DateTime
- принимает и отправляет unix-секунды, в браузере выводится с учётом часового пояса браузераSlider
- строка результата кликабельная, можно задать значение вручную, оно подчиняется настройкам min/max/stepColor
- принимает и отправляет цвет в 24-битном формате RRGGBBSelect
- принимает и отправляет индекс подстроки в списке вариантов, начиная с 0Pass
- если передать третьим аргументом строку - она будет отображаться как "заглушка" для окна пароля, напримерb.Pass(pass, "Pass", "***");
Confirm
- при любом выборе юзера виджет возвращаетtrue
. Для определения что именно выбрал юзер можно опроситьb.build.value.toBool()
или подключитьbool
переменнуюButton
- если выводится последней в группе - меняет свой стиль на "ярлык", который прикреплен к окну группы снизу
SettingsGyver
(SettingsGyver.h) - на вебсервере GyverHTTPSettingsESP
(SettingsESP.h) - на стандартном вебсервере ESPSettingsAsync
(SettingsAsync.h) - на асинхронном ESPAsyncWebserver
#define SETT_NO_DB // полностью отключить поддержку GyverDB
Settings(const String& title = "", GyverDB* db = nullptr);
// установить пароль на вебморду. Пустая строка "" чтобы отключить
void setPass(Text pass);
// перезагрузить страницу. Можно вызывать где угодно + в обработчике update
void reload();
// установить заголовок страницы
void setTitle(const String& title);
// подключить базу данных
void attachDB(GyverDB* db);
// использовать автоматические обновления из БД (при изменении записи новое значение отправится в браузер)
void useAutoUpdates(bool use);
// обработчик билда типа f(sets::Builder& b)
void onBuild(BuildCallback cb);
// обработчик обновлений типа f(sets::Updater& upd)
void onUpdate(UpdateCallback cb);
// обработчик скачивания файлов с устройства типа f(Text path)
void onFetch(FileCallback cb);
// обработчик загрузки файлов на устройство типа f(Text path)
void onUpload(FileCallback cb);
// тикер, вызывать в родительском классе
void tick();
// установить размер пакета (умолч. 1024 Б). 0 - отключить разбивку на пакеты. Не работает для Async-версии
void setPacketSize(size_t size);
// установить кастом js код из PROGMEM
void setCustom(const char* js, size_t len, bool gz = false);
// установить кастом js код из файла
void setCustomFile(const char* path, bool gz = false);
// установить инфо о проекте (отображается на вкладке настроек и файлов)
void setProjectInfo(const char* name, const char* link = nullptr);
// настройки вебморды
Config config;
{
// таймаут отправки слайдера, мс. 0 чтобы отключить
uint16_t sliderTout = 100;
// таймаут ожидания ответа сервера, мс
uint16_t requestTout = 2000;
// период обновлений, мс. 0 чтобы отключить
uint16_t updateTout = 2500;
}
// инфо о билде
Build build;
// авто-ID следующего виджета
size_t nextID();
// указатель на текущий SettingsXxx
void* thisSettings();
// перезагрузить страницу (вызывать в действии, например if (...click() b.reload()))
void reload();
// было действие с каким-то из виджетов выше
bool wasSet();
// сбросить флаг чтения wasSet
void clearSet();
// КОНТЕЙНЕРЫ
// разрешить неавторизованным клиентам следующий код
bool beginGuest();
// запретить неавторизованным клиентам
void endGuest();
// группа
bool beginGroup(Text title = Text());
void endGroup();
// вложенное меню
bool beginMenu(Text title);
void endMenu();
// горизонтальная группа виджетов
bool beginRow(Text title = Text());
void endRow();
// ряд кнопок
bool beginButtons();
void endButtons();
// ВИДЖЕТЫ
// ПАССИВНЫЕ
// ================= LOG =================
void Log(size_t id, Logger& log, Text label = "");
void Log(Logger& log, Text label = "");
// ================= LABEL =================
// текстовое значение, может обновляться по id
void Label(size_t id, Text label = "", Text text = Text(), uint32_t color = SETS_DEFAULT_COLOR);
void Label(size_t id, Text label, Text text, sets::Colors color);
void Label(Text label = "", Text text = Text(), uint32_t color = SETS_DEFAULT_COLOR);
void Label(Text label, Text text, sets::Colors color);
// лейбл с численным значением (выполняется быстрее, весит меньше)
void LabelNum(size_t id, Text label, T text, uint32_t color = SETS_DEFAULT_COLOR);
void LabelNum(size_t id, Text label, T text, sets::Colors color);
void LabelNum(Text label, T text, uint32_t color = SETS_DEFAULT_COLOR);
void LabelNum(Text label, T text, sets::Colors color);
void LabelFloat(size_t id, Text label, float text, uint8_t dec = 2, uint32_t color = SETS_DEFAULT_COLOR);
void LabelFloat(size_t id, Text label, float text, uint8_t dec, sets::Colors color);
void LabelFloat(Text label, float text, uint8_t dec = 2, uint32_t color = SETS_DEFAULT_COLOR);
void LabelFloat(Text label, float text, uint8_t dec, sets::Colors color);
// ================= LED =================
// светодиод (value 1 включен - зелёный, value 0 выключен - красный)
void LED(size_t id, Text label, bool value);
void LED(size_t id, Text label = "");
void LED(Text label, bool value);
void LED(Text label = "");
// светодиод с цветом на выбор
void LED(size_t id, Text label, bool value, uint32_t colorOff, uint32_t colorOn);
void LED(size_t id, Text label, bool value, Colors colorOff, Colors colorOn);
void LED(Text label, bool value, uint32_t colorOff, uint32_t colorOn);
void LED(Text label, bool value, Colors colorOff, Colors colorOn);
// ================= TEXT =================
// текстовый абзац
void Paragraph(size_t id, Text label = "", Text text = Text());
void Paragraph(Text label = "", Text text = Text());
// active
// ================= INPUT =================
// ввод текста и цифр [результат - строка], подключаемая переменная - любой тип
bool Input(size_t id, Text label = "", AnyPtr value = nullptr);
bool Input(Text label = "", AnyPtr value = nullptr);
// ================= NUMBER =================
// ввод цифр [результат - строка], подключаемая переменная - любой тип
bool Number(size_t id, Text label = "", AnyPtr value = nullptr);
bool Number(Text label = "", AnyPtr value = nullptr);
// ================= PASS =================
// ввод пароля [результат - строка], подключаемая переменная - любой тип
bool Pass(size_t id, Text label = "", AnyPtr value = nullptr);
bool Pass(Text label = "", AnyPtr value = nullptr);
// ================= COLOR =================
// ввод цвета [результат - 24-бит DEC число], подключаемая переменная - uint32_t
bool Color(size_t id, Text label = "", uint32_t* value = nullptr);
bool Color(Text label = "", uint32_t* value = nullptr);
// ================= SWITCH =================
// переключатель [результат 1/0], подключаемая переменная - bool
bool Switch(size_t id, Text label = "", bool* value = nullptr, uint32_t color = SETS_DEFAULT_COLOR);
bool Switch(size_t id, Text label, bool* value, Colors color);
bool Switch(Text label = "", bool* value = nullptr, uint32_t color = SETS_DEFAULT_COLOR);
bool Switch(Text label, bool* value, Colors color);
// ================= DATE =================
// дата [результат - unix секунды], подключаемая переменная - uint32_t
bool Date(size_t id, Text label = "", uint32_t* value = nullptr);
bool Date(Text label = "", uint32_t* value = nullptr);
// ================= TIME =================
// время [результат - секунды с начала суток], подключаемая переменная - uint32_t
bool Time(size_t id, Text label = "", uint32_t* value = nullptr);
bool Time(Text label = "", uint32_t* value = nullptr);
// ================= DATETIME =================
// дата и время [результат - unix секунды], подключаемая переменная - uint32_t
bool DateTime(size_t id, Text label = "", uint32_t* value = nullptr);
bool DateTime(Text label = "", uint32_t* value = nullptr);
// ================= SLIDER =================
// слайдер [результат - число], подключаемая переменная - любой тип
bool Slider(size_t id, Text label = "", float min = 0, float max = 100, float step = 1, Text unit = Text(), AnyPtr value = nullptr, uint32_t color = SETS_DEFAULT_COLOR);
bool Slider(size_t id, Text label, float min, float max, float step, Text unit, AnyPtr value, Colors color);
bool Slider(Text label = "", float min = 0, float max = 100, float step = 1, Text unit = Text(), AnyPtr value = nullptr, uint32_t color = SETS_DEFAULT_COLOR);
bool Slider(Text label, float min, float max, float step, Text unit, AnyPtr value, Colors color);
// двойной слайдер [результат - число], подключаемая переменная - любой тип
bool Slider2(size_t id_min, size_t id_max, Text label = "", float min = 0, float max = 100, float step = 1, Text unit = Text(), AnyPtr value_min = nullptr, AnyPtr value_max = nullptr, uint32_t color = SETS_DEFAULT_COLOR);
bool Slider2(size_t id_min, size_t id_max, Text label, float min, float max, float step, Text unit, AnyPtr value_min, AnyPtr value_max, Colors color);
// ================= SELECT =================
// опции разделяются ; [результат - индекс (число)], подключаемая переменная - uint8_t
bool Select(size_t id, Text label, Text options, uint8_t* value = nullptr);
bool Select(Text label, Text options, uint8_t* value = nullptr);
// ================= BUTTON =================
// кнопку можно добавлять как внутри контейнера кнопок, так и как одиночный виджет
bool Button(size_t id, Text label = "", uint32_t color = SETS_DEFAULT_COLOR);
bool Button(Text label = "", uint32_t color = SETS_DEFAULT_COLOR);
bool Button(size_t id, Text label, sets::Colors color);
bool Button(Text label, sets::Colors color);
// misc
// окно подтверждения, для активации отправь пустой update на его id или update с текстом подтверждения
bool Confirm(size_t id, Text label = "", bool* ptr = nullptr);
// кастомный виджет, type соответствует имени класса. params - ключи и значения
bool Custom(Text type, size_t id, const BSON& params = BSON(), AnyPtr value = nullptr);
Text
- универсальный текстовый формат, принимает строки в любом виде. При указанииvalue
отличным от стандартного будет отправлено его значение. Иначе будет отправлено значение из БД, если она подключена. Если в качестве значения нужно число - используйте конструкторValue
, напримерb.Color("col", "Color", Value(my_color));
, гдеmy_color
этоuint32_t
.AnyPtr
- указатель на переменную любого типа из списка: float, double, любой целочисленный, String, AnyPtr(char[], size_t len)
Инфо о билде
// тип билда
const Type type;
// клиент авторизован
const bool granted;
// id виджета (действие)
const size_t id;
// значение виджета (действие)
const Text value;
// тип - сборка виджетов
bool isBuild();
// тип - действие (обработка клика или значения)
bool isAction();
// контейнер гостевого доступа
class GuestAccess(Builder& b);
// контейнер группы виджетов
class Group(Builder& b, Text title = Text());
// контейнер вложенного меню
class Menu(Builder& b, Text title);
// горизонтальный контейнер
class Row(Builder& b, Text title);
// контейнер кнопок
class Buttons(Builder& b);
// всплывающее уведомление красное
void alert(Text text);
// всплывающее уведомление зелёное
void notice(Text text);
// пустой апдейт (например для вызова Confirm)
void update(size_t id);
// апдейт с float
void update(size_t id, float value, uint8_t dec = 2);
// апдейт с числом
void update(size_t id, <любой численный тип> value);
// апдейт с текстом
void update(size_t id, <любой текстовый тип> value);
// апдейт логгера
void update(size_t id, Logger& logger);
// апдейт для двойного слайдера
void update2(size_t id_min, <любой численный тип> value_min, <любой численный тип> value_max);
void update2(size_t id_min, float value_min, float value_max, uint8_t dec = 2);
// кастом апдейт для кастом виджета, params - ключи и значения
void update(size_t id, BSON& params);
Logger(size_t size);
// наследует Print
void print(любые_данные);
void println(любые_данные);
// вывод в String
String toString();
-
v1.0
-
v1.0.2
- Добавлен виджет Confirm (всплывающее окно подтверждения)
- Кастомные всплывающие окна для Input (Input теперь работает на просмотре AP WiFi точки на Xiaomi)
-
v1.0.5
- Добавлен виджет LED
- Добавлен файловый менеджер
- Добавлено ОТА обновление
- Добавлена авторизация и гостевой фильтр виджетов
- Новый стиль для Select
-
v1.1.0
- Добавлен виджет Number
- Создание виджета без ID (автоматический ID)
- Создание виджета без лейбла (будет равен типу виджета)
- Привязка внешней переменной к виджету на чтение и запись
- Редактор текстовых файлов в менеджере файлов
- Убран лаг с прошлым состоянием вебморды при обновлении страницы
- Возможность полностью убрать поддержку GyverDB дефайном
- Мелкие фиксы стилей
- Методы build(), value(), id() теперь являются членами (build, value, id) для краткости
-
v1.1.4
- У виджета Slider теперь кликабельный результат, можно ввести число
- Виджеты DateTime и Date теперь выводят и отправляют unix время с учётом часового пояса браузера
-
v1.1.5
- Виджету LED добавлена настройка цвета включенный/выключенный
- Виджету Confirm добавлена возможность подключить bool переменную для результата
- Добавлена возможность отправить всплывающее окно в Update
-
v1.1.6
- Добавлен виджет LabelNum для чисел
- Добавлены настройки вебморды (таймаут соединения, слайдеров, апдейтов)
-
v1.1.8
- Разбивка на пакеты для меньшего использования памяти
- Добавлен LabelFloat
-
v1.1.12
- Добавлена поддержка цвета виджетам Slider и Switch
-
v1.2.0
- Добавлен двойной слайдер
- Добавлен логгер (Web Serial)
- Добавлена поддержка кастомных виджетов на JS
- Добавлены обработчики скачивания и загрузки файлов
- Библиотеку можно найти по названию Settings и установить через менеджер библиотек в:
- Arduino IDE
- Arduino IDE v2
- PlatformIO
- Скачать библиотеку .zip архивом для ручной установки:
- Распаковать и положить в C:\Program Files (x86)\Arduino\libraries (Windows x64)
- Распаковать и положить в C:\Program Files\Arduino\libraries (Windows x32)
- Распаковать и положить в Документы/Arduino/libraries/
- (Arduino IDE) автоматическая установка из .zip: Скетч/Подключить библиотеку/Добавить .ZIP библиотеку… и указать скачанный архив
- Читай более подробную инструкцию по установке библиотек здесь
- Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
- Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
- Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru
Библиотека открыта для доработки и ваших Pull Request'ов!
При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
- Версия библиотеки
- Какой используется МК
- Версия SDK (для ESP)
- Версия Arduino IDE
- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код