Релиз v3 пока не вышел, но вы можете скачать репозиторий и присоединиться к тестированию.
Есть изменения в логике работы и частичная несовместимость. Список функций и методов ниже - актуальный, остальная документация ниже слегка устарела. Полная документация к v3 находится в разработке и доступна на Wiki репозитория. Также руководствуйтесь примерами.
Простой конструктор веб интерфейса для ESP8266 и ESP32
- Позволяет быстро создать универсальную вебморду для управления и настройки своего девайса
- Возможность создания многостраничных и динамических веб-интерфейсов в несколько строк кода
- Не требует знания HTML, CSS и JavaScript. Все стили и скрипты уже заложены в библиотеке
- Не требует загрузки файлов в SPIFFS, но стили и скрипты можно подгружать оттуда
- Страница собирается из готовых компонентов конструктора прямо в скетче
- Лёгкий вес, небольшое использование динамической памяти во время генерации страницы
- Работает на базе стандартных библиотек esp, ничего дополнительно устанавливать не нужно
- Относительно стильный дизайн, светлая и тёмная темы, возможность кастомизации некоторых компонентов
- Встроенные модули:
- Автоматизированная загрузка файлов
- Автоматизированное скачивание файлов
- Кеширование файлов из SPIFFS памяти
- AJAX и jQuery (опционально) обновление значений на странице
- Автоматический опрос и обновление переменных
- Перезагрузка страницы из скетча
- Авторизация на сервере по логину-паролю
- DNS сервер (для работы как точка доступа)
- mDNS (для открытия интерфейса по заданному адресу вместо IP)
- OTA обновление прошивки и памяти через браузер (возможна защита паролем)
- Компоненты конструктора:
- Оформление
- Заголовок
- Подпись
- Разделитель
- Перенос строки
- Разметка страницы
- Блок для объединения компонентов по вертикали (2 стиля с заголовком, 2 без)
- Блок для объединения компонентов по горизонтали с настройкой выравнивания
- Объединение вертикальных блоков по горизонтали + responsive
- Макросы блоков для упрощения кода конструктора
- Форма
- Веб-форма
- Кнопка submit
- Прочие компоненты
- Текст на цветной подложке
- Поле ввода текста
- Многострочное поле ввода текста
- Поле ввода цифр
- Поле ввода пароля
- Спиннер (поле с цифрой и кнопками +-)
- Галочка (чекбокс)
- Выключатель
- Слайдер
- Спойлер
- Выбор времени
- Выбор даты
- Выбор цвета
- Выпадающий список (дропбокс)
- Кнопка
- Кнопка-ссылка
- Кнопка-скачивание файла
- Кнопка загрузки файла на сервер
- "Светодиод" индикатор трёх типов
- Окно лога для отладки (веб Serial порт)
- Несколько типов графиков
- FontAwesome или локальные иконки для кнопок
- Блок динамической навигации со вкладками
- Блок навигации со ссылками
- Блоки для вывода изображений, видео и текстовых файлов из SPIFFS
- Всплывающие окна: alert, prompt, confirm с отправкой действия в программу
- Всплывающие подсказки для всех компонентов
- Оформление
esp8266, esp32
Некоторые элементы могут некрасиво отображаться на Firefox, т.к. сделаны под Chrome, Safari, Edge, Opera
- Библиотеку можно найти по названию GyverPortal и установить через менеджер библиотек в:
- 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: найти библиотеку как при установке и нажать "Обновить"
- Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
GyverPortal portal;
Функции конструктора
// ======== СОЗДАНИЕ СТРАНИЦЫ ========
GP.BUILD_BEGIN(); // начать построение (начальный HTML код), ширина колонки 350px
GP.BUILD_BEGIN(ширина); // + ширина колонки (int) в px
GP.BUILD_BEGIN_FILE(); // то же самое, но файл скриптов скачается из памяти (/gp_data/scripts.js)
GP.BUILD_BEGIN_FILE(ширина); // + ширина колонки (int) в px
GP.BUILD_END(); // завершить построение (завершающий HTML код)
// =============== ТЕМА ==============
GP.THEME(тема); // установить тему из памяти прошивки (GP_LIGHT, GP_DARK)
GP.THEME_FILE(имя); // установить тему из файла, файл класть в /gp_data/, файл указывать без расширения (напр. "GP_DARK")
// =========== AJAX UPDATE ===========
GP.UPDATE(список); // обновление. Передать список компонентов через запятую без пробелов
GP.UPDATE(список, период); // + период обновления в мс (умолч. 1000)
// =============== LOG ==============
GP.AREA_LOG(); // окно лога
GP.AREA_LOG(строк); // + кол-во строк (умолч. 5)
GP.AREA_LOG(строк, период); // + период обновления, мс (умолч. 1000)
GP.AREA_LOG(имя, лог); // окно лога ручное. Передать объект GPlog
GP.AREA_LOG(имя, лог, строк); // + кол-во строк (умолч. 5)
GP.AREA_LOG(имя, лог, строк, период); // + период обновления, мс (умолч. 1000)
// =========== JQUERY UPD ===========
GP.JQ_SUPPORT(); // поддержка jquery. Файл скачается с https://code.jquery.com/
GP.JQ_SUPPORT_FILE(); // поддержка jquery, файл скачается из памяти (/gp_data/jquery.js)
GP.JQ_UPDATE_BEGIN(); // начать обновляемый блок с периодом 1 секунда (один на страницу!)
GP.JQ_UPDATE_BEGIN(период); // + период обновления в мс
GP.JQ_UPDATE_BEGIN(период, задержка); // + задержка клика (умолч. 100мс)
GP.JQ_UPDATE_END(); // закончить обновляемый блок
// ============= POP UP =============
GP.ALERT(имя); // всплывающее окно предупреждения. Ответь на update текстом, он будет отображён
GP.ALERT(имя, период); // + период обновления в мс (умолч. 1000)
GP.ALERT(имя, текст); // Ответь на update цифрой 1, будет отображён текст
GP.ALERT(имя, текст, период); // + период обновления в мс (умолч. 1000)
GP.PROMPT(имя); // всплывающее окно с полем вводом текста. Ответь на update текстом, он будет отображён
GP.PROMPT(имя, период); // + период обновления в мс (умолч. 1000)
GP.PROMPT(имя, текст); // Ответь на update цифрой 1, будет отображён текст
GP.PROMPT(имя, текст, период); // + период обновления в мс (умолч. 1000)
GP.CONFIRM(имя); // всплывающее окно с кнопкой ДА и ОТМЕНА. Ответь на update текстом, он будет отображён
GP.CONFIRM(имя, период); // + период обновления в мс (умолч. 1000)
GP.CONFIRM(имя, текст); // Ответь на update цифрой 1, будет отображён текст
GP.CONFIRM(имя, текст, период); // + период обновления в мс (умолч. 1000)
// ============== RELOAD =============
GP.RELOAD(имя); // скрытый блок перезагрузки страницы. Добавь его имя в UPDATE и ответь 1 на update, чтобы обновить страницу
// ========== ФОРМАТИРОВАНИЕ =========
GP.BREAK(); // перенести строку
GP.HR(); // горизонтальная линия-разделитель
// =============== СЕТКА ==============
GP.GRID_BEGIN(); // начать сборку BLOCK блоков по горизонтальной сетке
GP.GRID_END(); // завершить
GP.GRID_RESPONSIVE(ширина); // располагать блоки по вертикали, если ширина меньше указанной. int, в пикселях (например 700). Указывать ПОСЛЕ GP.THEME()!!!
// ============== СПОЙЛЕР =============
GP.SPOILER_BEGIN(текст); // начать спойлер
GP.SPOILER_BEGIN(текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.SPOILER_END(); // завершить спойлер
// =============== БЛОК ==============
GP.BLOCK_BEGIN(); // начать отрисовку блока (объединение компонентов по вертикали)
GP.BLOCK_BEGIN(ширина); // + ширина строкой "100px", "25%" и так далее (умолч. 100%)
GP.BLOCK_TAB_BEGIN(текст); // блок с подписью на плашке
GP.BLOCK_TAB_BEGIN(текст, ширина); // + ширина строкой "100px", "25%" и так далее (умолч. 100%)
GP.BLOCK_TAB_BEGIN(текст, ширина, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BLOCK_THIN_BEGIN(); // блок без подложки
GP.BLOCK_THIN_BEGIN(ширина); // + ширина строкой "100px", "25%" и так далее (умолч. 100%)
GP.BLOCK_THIN_TAB_BEGIN(текст); // блок с подписью на плашке
GP.BLOCK_THIN_TAB_BEGIN(текст, ширина); // + ширина строкой "100px", "25%" и так далее (умолч. 100%)
GP.BLOCK_END(); // завершить отрисовку блока (любого выше)
// =========== ОБЪЕДИНЕНИЕ ==========
GP.BOX_BEGIN(); // начать объединение компонентов по горизонтали
GP.BOX_BEGIN(выравнивание); // + выравнивание (GP_CENTER, GP_LEFT, GP_RIGHT, GP_EDGES), умолч. GP_EDGES
GP.BOX_BEGIN(выравнивание, ширина); // + ширина строкой "100px", "25%" и так далее (умолч. 100%)
GP.BOX_END(); // завершить объединение
// ============ НАВИГАЦИЯ ===========
GP.NAV_TABS_LINKS(список адресов, список подписей); // блок с кнопками-ссылками
GP.NAV_TABS_LINKS(список адресов, список подписей, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.NAV_TABS(список); // блок динамической навигации, передать список вкладок
GP.NAV_TABS(список, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.NAV_BLOCK_BEGIN(); // начало вкладки навигации
GP.NAV_BLOCK_END(); // конец вкладки навигации
// ============ ПОДПИСИ =============
GP.TITLE(текст); // заголовок
GP.TITLE(текст, имя); // + имя компонента (для update())
GP.LABEL(текст); // подпись (для кнопок, полей, чекбоксов итд)
GP.LABEL(текст, имя); // + имя компонента (для update())
GP.SPAN(текст); // просто текст
GP.SPAN(текст, выравнивание); // + выравнивание строкой "center", "right", "left", "justify" (оставь пустым "" чтобы было по центру)
GP.SPAN(текст, выравнивание, имя); // + имя компонента (для update())
GP.LABEL_BLOCK(текст); // яркий текстовый лейбл
GP.LABEL_BLOCK(текст, имя); // + имя компонента (для update())
GP.LABEL_BLOCK(текст, имя, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.HINT(имя, текст); // всплывающая подсказка с текстом "текст" для элемента "имя". Вызывать после добавления компонента
// ========== ИНДИКАТОРЫ ============
GP.LED(имя); // зелёный - вкл, красный - выкл
GP.LED(имя, состояние); // + состояние (bool)
GP.LED_GREEN(имя); // зелёный - вкл, чёрный - выкл
GP.LED_GREEN(имя, состояние); // + состояние (bool)
GP.LED_RED(имя); // красный - вкл, чёрный - выкл
GP.LED_RED(имя, состояние); // + состояние (bool)
// ============= ИКОНКИ =============
GP.ICON_SUPPORT(); // добавить поддержку иконок FontAwesome (требуется подключение к Интренет)
String GP.ICON(имя); // вставка иконки FontAwesome. Имена - https://fontawesome.com/v4/icons/ , указывать без "fa", например "cloud"
String GP.ICON(имя, размер); // + размер (int) в пикселях
String GP.ICON_FILE(uri, размер); // отобразить иконку из файла SPIFFS (должнен быть настроен download), размер int в px
// ============= ФОРМА ==============
GP.FORM_BEGIN(имя); // начать форму с именем (имя)
GP.FORM_END(); // завершить форму
GP.SUBMIT(текст); // кнопка отправки формы
GP.SUBMIT(текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.HIDDEN(имя, значение); // скрытый элемент
GP.FORM_SUBMIT(имя, текст); // пустая форма с кнопкой submit
GP.FORM_SUBMIT(имя, текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.FORM_SUBMIT(имя, текст, имя hidden, значение hidden); // пустая форма с кнопкой submit и HIDDEN компонентом
GP.FORM_SUBMIT(имя, текст, имя hidden, значение hidden, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
// ============= ФАЙЛЫ =============
GP.FILE_UPLOAD(имя); // кнопка для загрузки файла (файлов) на сервер
GP.FILE_UPLOAD(имя, текст); // + текст на кнопке
GP.FILE_UPLOAD(имя, текст, расширения); // + разрешённые для загрузки типы данных (см тут https://www.w3schools.com/tags/att_input_accept.asp)
GP.FOLDER_UPLOAD(имя); // кнопка для загрузки папки с файлами на сервер. Название файла будет содержать полный путь в указанной папке
GP.FOLDER_UPLOAD(имя, текст); // + текст на кнопке
GP.OTA_FIRMWARE(); // загрузка файла прошивки для обновления
GP.OTA_FIRMWARE(текст); // + текст на кнопке
GP.OTA_FILESYSTEM(); // загрузка файла файловой системы для обновления
GP.OTA_FILESYSTEM(текст); // + текст на кнопке
GP.IMAGE(ссылка); // картинка. Указать ссылку на файл в памяти
GP.IMAGE(ссылка, ширина); // + ширина строкой "100px", "25%" и так далее (умолч "", т.е. авто)
GP.VIDEO(ссылка); // видео. Указать ссылку на файл в памяти
GP.VIDEO(ссылка, ширина); // + ширина строкой "100px", "25%" и так далее (умолч "", т.е. авто)
GP.EMBED(ссылка); // текст. Указать ссылку на файл в памяти
GP.EMBED(ссылка, ширина); // + ширина строкой "100px", "25%" и так далее (умолч "", т.е. авто)
// ============= КНОПКА =============
GP.BUTTON(имя, текст); // кнопка
GP.BUTTON(имя, текст, id); // + id компонента, данные с которого кнопка отправит данные по click
GP.BUTTON(имя, текст, id, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON(имя, текст, id, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.BUTTON_MINI(имя, текст); // мини кнопка
GP.BUTTON_MINI(имя, текст, id); // + id компонента, данные с которого кнопка отправит данные по click
GP.BUTTON_MINI(имя, текст, id, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON_MINI(имя, текст, id, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
// ========= КНОПКА-ССЫЛКА =========
GP.BUTTON_LINK(ссылка, текст); // кнопка-ссылка для навигации по сайту
GP.BUTTON_LINK(ссылка, текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON_LINK(ссылка, текст, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.BUTTON_MINI_LINK(ссылка, текст); // мини кнопка-ссылка для навигации по сайту
GP.BUTTON_MINI_LINK(ссылка, текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON_MINI_LINK(ссылка, текст, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
// ========= КНОПКА-СКАЧКА =========
GP.BUTTON_DOWNLOAD(ссылка, текст); // кнопка для скачивания файла
GP.BUTTON_DOWNLOAD(ссылка, текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON_DOWNLOAD(ссылка, текст, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.BUTTON_MINI_DOWNLOAD(ссылка, текст); // мини кнопка для скачивания файла
GP.BUTTON_MINI_DOWNLOAD(ссылка, текст, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.BUTTON_MINI_DOWNLOAD(ссылка, текст, цвет, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
// ============== ВВОД ==============
GP.NUMBER(имя, подсказка, число); // поле ввода числа, int
GP.NUMBER(имя, подсказка, число, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.NUMBER(имя, подсказка, число, ширина, откл); // + режим "только чтение" - true, по умолч false
GP.NUMBER_F(имя, подсказка, число); // поле ввода числа, float
GP.NUMBER_F(имя, подсказка, число, знаков); // + кол-во знаков после запятой (умолч. 2)
GP.NUMBER_F(имя, подсказка, число, знаков, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.NUMBER_F(имя, подсказка, число, знаков, ширина, откл); // + режим "только чтение" - true, по умолч false
GP.TEXT(имя); // поле ввода текста
GP.TEXT(имя, подсказка); // + подсказка
GP.TEXT(имя, подсказка, текст); // + текст
GP.TEXT(имя, подсказка, текст, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.TEXT(имя, подсказка, текст, ширина, откл); // + режим "только чтение" - true, по умолч false
GP.PASS(имя); // поле ввода пароля
GP.PASS(имя, подсказка); // подсказка
GP.PASS(имя, подсказка, текст); // + текст
GP.PASS(имя, подсказка, текст, ширина); // + ширина строкой "100px", "25%" и так далее (умолч 90%)
GP.AREA(имя); // большое поле для ввода текста
GP.AREA(имя, высота); // + высота в количестве строк (умолч. 1)
GP.AREA(имя, высота, текст); // + отображаемый текст
GP.AREA(имя, высота, текст, откл); // + режим "только чтение" - true, по умолч false
// =========== ВЫКЛЮЧАТЕЛИ ===========
GP.CHECK(имя); // чекбокс, умолч. выключен
GP.CHECK(имя, состояние); // + состояние
GP.CHECK(имя, состояние, откл); // + режим "только чтение" - true, по умолч false
GP.SWITCH(имя); // выключатель, умолч. выключен
GP.SWITCH(имя, состояние); // + состояние
GP.SWITCH(имя, состояние, откл); // + режим "только чтение" - true, по умолч false
// ============ ДАТА-ВРЕМЯ ===========
GP.DATE(имя); // ввод даты
GP.DATE(имя, GPdate); // + значение
GP.DATE(имя, GPdate, откл); // + режим "только чтение" - true, по умолч false
GP.TIME(имя); // ввод времени
GP.TIME(имя, GPtime); // + значение
GP.TIME(имя, GPtime, откл); // + режим "только чтение" - true, по умолч false
// =============== ЦВЕТ ===============
GP.COLOR(имя); // выбор цвета, умолч. чёрный
GP.COLOR(имя, число); // выбор цвета
GP.COLOR(имя, число, откл); // + режим "только чтение" - true, по умолч false
// ============== ВЫБОР ==============
GP.SELECT(имя, список); // селектор (дропбокс)
GP.SELECT(имя, список, активный); // + текущий активный пункт (int)
GP.SELECT(имя, список, активный, нумерация); // + текущий активный пункт (int)
GP.SELECT(имя, список, активный, нумерация, откл); // + режим "только чтение" - true, по умолч false
// Здесь список:
// - Строка любого формата, пункты разделены запятой: "пункт1,пункт2,пункт3"
// - Массив String[], последняя строка должна быть пустой!
// - Массив char**, последняя строка должна быть пустой! (напр. char* names[] = {"p1", "p2", ""})
// ============== СЛАЙДЕР ==============
GP.SLIDER(имя);
GP.SLIDER(имя, значение); // слайдер 0..100
GP.SLIDER(имя, значение, мин, макс); // слайдер с шагом 1
GP.SLIDER(имя, значение, мин, макс, шаг); // + шаг
GP.SLIDER(имя, значение, мин, макс, шаг, знаков); // + кол-во знаков после запятой (умолч. 0)
GP.SLIDER(имя, значение, мин, макс, шаг, знаков, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.SLIDER(имя, значение, мин, макс, шаг, знаков, цвет, откл); // + режим "только чтение" - true, по умолч false
// ============== СПИННЕР ==============
GP.SPINNER(имя);
GP.SPINNER(имя, значение); // спиннер, значение поддерживает float
GP.SPINNER(имя, значение, мин, макс); // + минимум максимум, поддерживает float
GP.SPINNER(имя, значение, мин, макс, шаг); // + шаг (умолч. 1), поддерживает float
GP.SPINNER(имя, значение, мин, макс, шаг, знаков); // + кол-во знаков после запятой (умолч. 0)
GP.SPINNER(имя, значение, мин, макс, шаг, знаков, цвет); // + цвет из списка (умолч. GP_GREEN) или цвет в формате PSTR("#rrggbb")
GP.SPINNER(имя, значение, мин, макс, шаг, знаков, цвет, ширина);// + ширина строкой "100px", "25%" и так далее (умолч 80px)
// ============== ГРАФИКИ ==============
// лёгкий статичный график без масштаба
GP.PLOT<к-во осей, к-во данных>(имя, подписи, данные int16_t, int dec = 0, int height = 400);
GP.PLOT_DARK<к-во осей, к-во данных>(имя, подписи, данные int16_t, int dec = 0, int height = 400);
// статичный график с масштабом и привязкой ко времени
GP.PLOT_STOCK<к-во осей, к-во данных>(имя, подписи, массив времён, массив данных, int dec = 0, int height = 400, bool local = 0);
GP.PLOT_STOCK_DARK<к-во осей, к-во данных>(имя, подписи, массив времён, массив данных, int dec = 0, int height = 400, bool local = 0);
// динамический график, вызывает update
GP.AJAX_PLOT(имя, к-во осей, к-во точек по Х, период update, int height = 400, bool local = 0);
GP.AJAX_PLOT_DARK(имя, к-во осей, к-во точек по Х, период update, int height = 400, bool local = 0);
// ============== КАСТОМ ==============
GP.PAGE_BEGIN(); // начальный HTML код
GP.PAGE_BEGIN(ширина); // + ширина колонки (int) в px (умолч. 350)
GP.BUILD_BEGIN_FILE(); // начальный HTML код, но скрипты берутся из FS памяти
GP.BUILD_BEGIN_FILE(ширина); // + ширина колонки (int) в px (умолч. 350)
GP.PAGE_BLOCK_BEGIN() // центральный div блок
GP.PAGE_BLOCK_END() // центральный div блок
GP.PAGE_END(); // завершающий HTML код
GP.JS_TOP(); // верхний блок JS скриптов (для кликов, слайдеров и т.д.)
GP.JS_TOP_FILE(); // файл скриптов скачается из памяти (/gp_data/scripts.js)
GP.JS_BOTTOM(); // нижний блок JS скриптов (для кликов, слайдеров и т.д.)
GP.SEND(код); // добавить свой код на страницу ("строка", F("строка"), String)
GP.SEND_P(pgm код); // добавить свой код на страницу (PGM_P, PROGMEM)
// ============ ЦВЕТА ===========
// цвет с постфиксом _B - яркий
GP_RED
GP_RED_B
GP_PINK
GP_PINK_B
GP_VIOL
GP_VIOL_B
GP_BLUE
GP_BLUE_B
GP_CYAN
GP_CYAN_B
GP_GREEN
GP_GREEN_B
GP_YELLOW
GP_ORANGE
GP_ORANGE_B
GP_GRAY
GP_BLACK
// ============= МАКРОСЫ =============
GP_MAKE_FORM(act, subm, args);
GP_MAKE_GRID(args);
GP_MAKE_BOX(args);
GP_MAKE_BOX(align, args);
GP_MAKE_BOX(align, width, args);
GP_MAKE_BLOCK(args);
GP_MAKE_BLOCK(width, args);
GP_MAKE_BLOCK_TAB(text, args);
GP_MAKE_BLOCK_TAB(text, width, args);
GP_MAKE_BLOCK_TAB(text, width, style, args);
GP_MAKE_BLOCK_THIN(args);
GP_MAKE_BLOCK_THIN(width, args);
GP_MAKE_BLOCK_THIN_TAB(text, args);
GP_MAKE_BLOCK_THIN_TAB(text, width, args);
GP_MAKE_JQ_UPDATE(name, args);
GP_MAKE_JQ_UPDATE(name, prd, args);
GP_MAKE_NAV_BLOCK(args);
GP_MAKE_SPOILER(txt, args);
GP_MAKE_SPOILER(txt, style, args);
Методы класса
// =============== СИСТЕМА ==============
void start(); // запустить портал
void start("host"); // запуск с поддержкой mDNS, указать адрес (см. доку).
void start("host", port); // запуск с поддержкой mDNS и указанием порта. Если mDNS не нужен - передай ""
void stop(); // остановить портал// показать свою страницу
bool state(); // проверить, запущен ли портал
void setBufferSize(int sz); // задать размер буфера страницы, байт (умолч. 1000)
// ============ АВТОРИЗАЦИЯ ============
void enableAuth(char* login, char* pass); // включить авторизацию по логину-паролю
void disableAuth(); // отключить авторизацию
// ================ OTA ================
void enableOTA(); // подключить OTA обновление по адресу /ota_update
void enableOTA(login, pass); // + авторизация
// =============== ATTACH ==============
void attachBuild(func()); // подключить функцию-билдер страницы
void attachBuild(func(GyverPortal&)); // + передача объекта (см. Локальный портал)
void detachBuild(); // отключить
void attach(func()); // подключить функцию-обработчик действия
void attach(func(GyverPortal&)); // + передача объекта (см. Локальный портал)
void detach(); // отключить
// =============== TICK ==============
bool tick(); // тикер портала. Вернёт true, если портал запущен
// ============== ACTIONS =============
// =============== FORM ===============
bool form(); // вернёт true, если было нажатие на любой submit
bool form(имя); // вернёт true, если был submit с указанной формы
bool formSub(имя); // вернёт true, если был submit с форм, url которых начинается с name
String formName(); // получить имя теукщей submit формы
// ============== REQUEST =============
bool request(); // вернёт true, если был http запрос
bool request(url); // вернёт true, если был http запрос на указанный url
// =============== CLICK ==============
bool click(); // вернёт true, если был клик по (кнопка, чекбокс, свитч, слайдер, селектор)
bool click(имя); // вернёт true, если был клик по указанному элементу
String clickName(); // получить имя теукщего кликнутого компонента
bool clickDown(имя); // вернёт true, если кнопка была нажата
bool clickUp(имя); // вернёт true, если кнопка была отпущена
// =============== UPDATE ==============
bool update(); // вернёт true, если было обновление
bool update(имя); // вернёт true, если было update с указанного компонента
String updateName(); // вернёт имя обновлённого компонента
// автоматическое обновление. Отправит значение из указанной переменной
// Вернёт true в момент обновления
bool updateString(имя, String& f);
bool updateInt(имя, int f);
bool updateFloat(имя, float f, int dec = 2);
bool updateCheck(имя, bool f);
bool updateDate(имя, GPdate f);
bool updateTime(имя, GPtime f);
bool updateColor(имя, GPcolor f);
bool updateSelected(имя, int f);
bool updateLog(const String& n, GPlog& log);
// =============== ANSWER ==============
void answer(const String& s; // отправить ответ на обновление
void answer(GPcolor col); // ответ с цветом
void answer(GPdate date); // ответ с датой
void answer(GPtime time); // ответ со временем
void answer(int v); // ответ с числом
void answer(float v, uint8_t dec); // ответ с float и кол-вом знаков
void answer(int16_t* v, int am); // массив int размерностью am, для графика
void answer(int16_t* v, int am, int dec); // + делитель
// =============== UPLOAD ==============
bool upload(); // вернёт true, если был запрос на загрузку файла
bool upload(имя); // вернёт true, если был запрос на загрузку файла с указанной кнопки
void saveFile(File file); // установить файл для загрузки
void saveFile(имя); // установить файл для загрузки
bool uploadEnd(); // вернёт true, если завершена загрузка файла
String& uploadName(); // имя формы загрузки файла
String& fileName(); // имя файла при загрузке
void uploadAuto(bool mode); // автоматическая загрузка файла по uri (по умолч. выкл, false)
// ============== DOWNLOAD ==============
bool download(); // вернёт true, если был запрос на скачивание файла
void sendFile(File file); // отправить файл
void sendFile(имя); // отправить файл
void downloadAuto(bool mode); // автоматическое скачивание файла по uri (по умолч. выкл, false)
// ================ URI =================
String uri(); // адрес текущей страницы
bool uri(uri); // true если uri совпадает
bool root(); // открыта главная страница сайта /
// ========= ПОЛУЧЕНИЕ ЗНАЧЕНИЙ ==========
// ОПАСНЫЕ ФУНКЦИИ (не проверяют есть ли запрос). Конвертируют и возвращают значение
String getString(имя); // получить String строку с компонента
int getInt(имя); // получить число с компонента
float getFloat(имя); // получить float с компонента
bool getCheck(имя); // получить состояние чекбокса
GPdate getDate(имя); // получить дату с компонента
GPtime getTime(имя); // получить время с компонента
GPcolor getColor(имя); // получить цвет с компонента
int getSelected(имя); // получить номер выбранного пункта в дроплисте
// вариант без имени (нулевой аргумент), опрашивать только в условии, например if (p.click("name")) Serial.println(p.getString())
String getString(); // получить String строку с компонента
int getInt(); // получить число с компонента
float getFloat(); // получить float с компонента
bool getCheck(); // получить состояние чекбокса
GPdate getDate(); // получить дату с компонента
GPtime getTime(); // получить время с компонента
GPcolor getColor(); // получить цвет с компонента
int getSelected(); // получить номер выбранного пункта в дроплисте
// БЕЗОПАСНЫЕ ФУНКЦИИ (проверяют запрос). Копируют данные из запроса в переменную
// вернёт true, если имя компонента есть в запросе. Можно использовать для form() и click()
bool copyStr(имя, char* t);
bool copyString(имя, String& t);
bool copyInt(имя, int& t);
bool copyFloat(имя, float& t);
bool copyCheck(имя, bool& t);
bool copyDate(имя, GPdate& t);
bool copyTime(имя, GPtime& t);
bool copyColor(имя, GPcolor& t);
bool copySelected(имя, int& t);
// для автоматического опроса click
bool clickStr(имя, char* t);
bool clickString(имя, String& t);
bool clickInt(имя, int& t);
bool clickFloat(имя, float& t);
bool clickCheck(имя, bool& t);
bool clickDate(имя, GPdate& t);
bool clickTime(имя, GPtime& t);
bool clickColor(имя, GPcolor& t);
bool clickSelected(имя, int& t);
// ============= IP REMOTE CLIENT ============
IPAddress clientIP(); // вернёт IP адрес клиента
bool clientFromNet(IPAddress NetIP, uint8_t mask); // вернёт true, если IP адрес клиента принадлежит указанной сети
// ================= ВЕБ-ЛОГ =================
GPlog(); // пустой конструктор
GPlog(const char* name); // задать имя
void start(int n = 64); // запустить
void stop(); // остановить
bool state(); // лог запущен
void write(uint8_t n); // записать байт
void print(); // отправить любые данные
void println(); // отправить любые данные
char* read(); // прочитать char*
void clear(); // очистить
bool available(); // есть данные для чтения
// ========== СПИСОК АВТООБНОВЛЕНИЯ ==========
list.init(количество); // инициализировать список, указать количество
list.clear(); // очистить список
list.add(адрес, имя, тип); // добавить переменную, указать имя компонента и тип
list.add(адрес, имя формы, имя, тип); // добавить переменную, ИМЯ ФОРМЫ, указать имя компонента и тип
// типы для списка
T_CSTR - массив char
T_STRING - строка String
T_TIME - время типа GPtime
T_DATE - дата типа GPdate
T_CHECK - boolean, для чекбокса
T_BYTE - целое 1 байт
T_INT - целое 4 байта
T_FLOAT - float
T_COLOR - целое 4 байта, для цвета
// ============ ПУБЛИЧНЫЕ ОБЪЕКТЫ ============
File file;
GPlist list;
GPlog log;
ESP8266HTTPUpdateServer/HTTPUpdateServer httpUpdater;
ESP8266WebServer/WebServer server;
// ========== ДЕФАЙНЫ НАСТРОЕК ==========
// объявлять ДО ПОДКЛЮЧЕНИЯ БИБЛИОТЕКИ GyverPortal
#define GP_NO_MDNS // убрать поддержку mDNS из библиотеки (вход по хосту в браузере)
#define GP_NO_DNS // убрать поддержку DNS из библиотеки (для режима работы как точка доступа)
#define GP_NO_OTA // убрать поддержку OTA обновления прошивки
#define GP_NO_UPLOAD // убрать поддержку загрузки файлов на сервер
#define GP_NO_DOWNLOAD // убрать поддержку скачивания файлов с сервера
Хранение и изменение даты GPdate
// структура для хранения даты GPdate
// переменные
uint16_t year;
uint8_t month, day;
// инициализация
GPdate();
GPdate(int year, int month, int day); // из трёх чисел
GPdate(String str); // из строки вида yyyy-mm-dd
// методы
void set(int nyear, int nmonth, int nday); // установить
String encode(); // преобразовать в строку вида yyyy-mm-dd
void decode(String str); // обновить из строки вида yyyy-mm-dd
Хранение и изменение времени GPtime
// структура для хранения даты GPtime
// переменные
uint8_t hour, minute, second;
// инициализация
GPtime();
GPtime(int hour, int minute, int second); // из трёх чисел
GPtime(String str); // из строки вида hh:mm:ss
// методы
void set(int nhour, int nminute, int nsecond = 0); // установить
String encode(); // преобразовать в строку вида hh:mm:ss
void decode(String str); // обновить из строки вида hh:mm:ss
Хранение и изменение времени GPunix
// получить unix время для графика
uint32_t GPunix(год, месяц, день, час, минута, секунда);
uint32_t GPunix(год, месяц, день, час, минута, секунда, gmt);
uint32_t GPunix(GPdate d, GPtime t);
uint32_t GPunix(GPdate d, GPtime t, int8_t gmt);
// gmt - часовой пояс, по умолч. 0 (пример: Москва gmt = 3)
// месяц и день начинаются с 1, не с 0!
Хранение и изменение цвета GPcolor
// см. пример gpcolor_demo
// структура для хранения цвета GPcolor
// переменные
uint8_t r, g, b;
// инициализация
GPcolor();
GPcolor(uint32_t color);
GPcolor(byte r, byte g, byte b);
GPcolor(String s) // из строки вида #RRGGBB
// методы
String encode(); // преобразовать в строку вида #RRGGBB
void decode(String str); // обновить из строки вида #RRGGBB
void setRGB(r, g, b); // установить цвет побайтно
void setHEX(uint32_t col); // установить 24 бит цвет
uint32_t getHEX(); // получить 24 бит цвет
// к структуре можно присвоить uint32_t число
Утилиты
// получить номер, под которым name входит в list вида "val1,val2,val3"
int GPinList(const String& s, const String& list);
// получить строку, которая входит в список list "val1,val2,val3" под номером idx
String GPlistIdx(int idx, const String& li);
// получить тип файла (вида image/png) по его пути uri
String GPfileType(const String& uri);
// добавить новое значение в массив с перемоткой (для графиков)
GPaddInt(int16_t val, int16_t* arr, uint8_t am); // новое значение, массив, размер массива
GPaddUnix(uint32_t val, uint32_t* arr, uint8_t am); // новое значение, массив, размер массива
GPaddUnixS(int16_t val, uint32_t* arr, uint8_t am); // добавить секунды, массив, размер массива
PGM_P GPgetAlign(GPalign a); // получить align для flex
Компонент/Вызов | form() | click() | update() |
---|---|---|---|
TITLE | ✔ | ||
LABEL | ✔ | ||
LABEL_BLOCK | ✔ | ||
BUTTON | ✔ | ||
BUTTON_MINI | ✔ | ||
NUMBER | ✔ | ✔ | ✔ |
TEXT | ✔ | ✔ | ✔ |
PASS | ✔ | ✔ | ✔ |
AREA | ✔ | ✔ | ✔ |
CHECK | ✔ | ✔ | ✔ |
SWITCH | ✔ | ✔ | ✔ |
DATE | ✔ | ✔ | ✔ |
TIME | ✔ | ✔ | ✔ |
COLOR | ✔ | ✔ | ✔ |
SLIDER | ✔ | ✔ | ✔ |
SPINNER | ✔ | ✔ | ✔ |
SELECT | ✔ | ✔ | ✔ |
LED_RED | ✔ | ✔ | |
LED_GREEN | ✔ | ✔ | |
LED | ✔ | ✔ |
Библиотека позволяет управлять электронным устройством из браузера. Между esp и браузером устанавливаются отношения клиент-сервер. Браузер (клиент) может:
- Запросить с esp страницу, которую нужно отобразить
- Запросить с esp конкретные данные, которые нужно обновить на странице (текст, состояние чекбокса)
- Отправить на esp данные со страницы (факт нажатия кнопки, текст)
Esp (сервер) может только отвечать на запросы от браузера, самостоятельно ничего отправить на страницу он не может. Но в библиотеке есть инструменты, позволяющие наладить такое взаимодействие с элементами интерфейса.
Взаимодействие с библиотекой сводится к двум основным моментам:
- У нас есть функция, которая вызывается библиотекой, когда браузер запрашивает отображение страницы. В этой функции мы собираем страницу из готовых блоков, затем библиотека её отправляет
- У нас есть функция, которая вызывается библиотекой, когда приходит действие с браузера. В этой функции мы получаем со страницы данные, сигналы, а также отправляем ответы на запросы
На данный момент в библиотеке есть три типа действий с браузера:
- Форма с кнопкой: при нажатии на кнопку типа
submit
страница перезагружается, а в программу приходят данные со всех компонентов, входящих в форму (текст в поле ввода, положения слайдеров и чекбоксов, и так далее). Удобно для однократного ввода данных, настройки подключения и тому подобное. - Клик/изменение: при клике на почти любой компонент интерфейса или при изменении его состояния или значения можно получить его актуальное значение без перезагрузки страницы. Удобно для управления и настройки (галочки, кнопки, слайдеры, выбор цвета).
- Обновление значений и состояний компонентов на странице в реальном времени без перезагрузки страницы. Удобно для индикации работы и получения текущих численных и текстовых значений из программы, вывод графиков в реальном времени, состояний чекбоксов и лампочек.
Библиотека может работать как в локальной сети (esp подключается к роутеру), так и в режиме точки доступа (смартфон подключается к esp).
После организации связи нужно вызвать portal.start()
для запуска сервера.
WiFi.mode(WIFI_STA);
WiFi.begin("login", "pass");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP());
// ...
portal.start(); // запускаем портал
Для подключения к порталу нужно зайти в браузере на IP адрес платы, который выдал ей роутер. В примере выше этот адрес выводится в монитор порта.
В версии 2.0 библиотеки появилась встроенная возможность запуска функции обнаружения: при запуске портала передаём адрес,
по которому хотим заходить свою плату в браузере или обнаруживать в локальной сети. Например portal.start("my_esp")
без пробелов.
Что делать дальше:
- Windows
- Установить утилитку Bonjour от Apple, ссылка на официальный сайт
- Для открытия портала вместо IP адреса использовать
имя.local
, из примера выше -http://my_esp.local/
- Круто же!
- Android
- Скачать из Play Market приложение Service Browser
- Открыть пункт
_http._tcp. (World Wide Web HTTP)
- Увидеть там IP адрес платы напротив указанного имени хоста
- Удобно!
В этом режиме портал нужно запускать start()
после установки режима работы WIFI_AP
: библиотека определяет текущий режим работы и запускает DNS сервер.
WiFi.mode(WIFI_AP);
WiFi.softAP("My Portal");
// ...
portal.start(); // запускаем портал
На стандартных настройках IP адрес для подключения в этом режиме будет 192.168.4.1
С версии 2.1 появилась защита веб-интерфейса паролем: нужно авторизироваться для отображения страницы. Для подключения авторизации достаточно вызвать функцию и указать логин-пароль
portal.enableAuth("login", "password");
Также авторизацию можно отключить:
portal.disableAuth();
В GyverPortal используется стандартная библиотека ESP8266WebServer, поэтому
для обеспечения работы сервера нужно вызывать portal.tick()
в цикле программы. Возвращает true
, если сервер запущен в данный момент.
Взаимодействие между платой и браузером происходит по следующему сценарию: на esp запщуен сервер, браузер является клиентом. При открытии портала в браузере esp получает запрос и отправляет страницу. При действиях пользователя (нажал на кнопку) или скриптов со стороны браузера (запрос данных) esp получает запрос на действие. В библиотеке начиная с версии 2.0 это реализовано так:
- Создаём свою функцию вида
void f()
илиvoid f(GyverPortal &p)
(в этот вариант будет автоматически передан ваш экземпляр GyverPortal) - Подключаем эту функцию в библиотеку:
portal.attach(f)
Теперь указанная функция будет автоматически вызываться при действии в браузере, их всего три:
- Отправка формы
- Клик по элементу
- Запрос на обновление
В этом сценарии объект создан глобально и может опрашиваться в любом месте программы
GyverPortal portal;
void build() {
// конструктор страницы
}
void action() {
// опрос действий
// переменная portal в области видимости
}
void setup() {
// подключаемся к сети
portal.attachBuild(build);
portal.attach(action);
portal.start();
}
void loop() {
portal.tick();
}
В этом сценарии объект создан локально, например в цикле. Функция-обработчик действий должна содержать переданный по ссылке объект портала, чтобы работать вне области определения!
void build() {
// конструктор страницы
}
void action(GyverPortal &p) {
// опрос действий
// здесь переменная p является ссылкой на portal (ниже)
// можно обращаться к ней точно так же, if (p.click())
}
void f() {
GyverPortal portal;
portal.attachBuild(build);
portal.attach(action);
portal.start();
while (portal.tick());
}
В таком сценарии для выхода из цикла можно вызвать portal.stop()
по таймауту или кнопке с браузера.
Конструктор реализован следующим образом: есть функция, которая сама будет вызываться, когда нужно отправить
в браузер страницу. В этой функции мы добавляем компоненты на страницу, обращаясь к GP
с указанием элемента.
Создаём функцию вида: void f()
. Далее в ней:
- Запускаем конструктор:
BUILD_BEGIN()
. Здесь добавляется начальный HTML код - (Опционально) применяем тему:
GP.THEME(тема)
(GP_LIGHT/GP_DARK) - Строим страницу, добавляя компоненты, например
GP.BUTTON(...)
- Завершаем работу конструктора:
BUILD_END()
. Здесь добавляется завершающий HTML код
Общий вид функции конструктора:
void build() {
BUILD_BEGIN();
GP.THEME(GP_LIGHT);
// собираем страницу
// ...
BUILD_END();
}
Примечание: внутри функции конструктора не рекомендуется создавать динамические данные (
String
, вызовыnew
иmalloc
). Это приведёт к фрагментации памяти на время работы функции конструктора, в сильно загруженной программе из за этого может закончиться оперативная память, если страница будет большая!
Передаём в библиотеку нашу функцию-конструктор страницы:
portal.attachBuild(build);
Библиотека сама будет вызывать её, когда потребуется отобразить страницу. Функций-конструкторов (а следовательно и страниц) может быть несколько и их можно переключать.
Также сборку страницы можно производить по "условиям", добавляя и убирая компоненты согласно сценарию работы (см. пример menuTabs)
Основная суть использования форм:
- Форма имеет своё уникальное имя, должно начинаться с
/
- Внутри формы может быть сколько угодно элементов, но только одна кнопка типа
SUBMIT
- При нажатии на SUBMIT esp получает имя формы и данные из всех элементов внутри этой формы
- При нажатии на SUBMIT страница перезагружается, поэтому значения компонентов страницы нужно хранить в переменных и передавать при следующей сборке страницы
Пример с двумя формами, первая может передать текст из окна ввода, вторая - только факт нажатия кнопки:
форма_1
ввод текста
кнопка submit
форма_1
форма_2
кнопка submit
форма_2
В конструкторе GyverPortal это будет выглядеть так:
void build() {
BUILD_BEGIN(); // запустить конструктор
GP.THEME(GP_LIGHT); // применить тему
GP.FORM_BEGIN("/login"); // начать форму, передать имя
GP.TEXT("txt", "Login", ""); // ввод текста, подсказка Login, текста нет
GP.BREAK(); // перенос строки
GP.SUBMIT("Submit"); // кнопка Submit
GP.FORM_END(); // завершить форму
GP.FORM_BEGIN("/exit"); // начать форму, передать имя
GP.SUBMIT("Exit"); // кнопка Exit
GP.FORM_END(); // завершить форму
BUILD_END(); // завершить построение
}
Результат работы конструктора:
Все инструменты конструктора описаны в документации выше.
- При нажатии любой кнопки типа
SUBMIT
в браузере функцияform()
вернётtrue
- Функция должна опрашиваться внутри подключенной в
attach()
функции - Для поиска формы, с которой пришёл сигнал, используем
form(имя)
- вернётtrue
, если имя совпало- Лучше обернуть поиск в
if (form())
, чтобы не тратить процессорное время на сравнение строк
- Лучше обернуть поиск в
void action() {
if (portal.form()) {
Serial.print("Submit form: ");
if (portal.form("/login")) Serial.println("Login");
if (portal.form("/exit")) Serial.println("Exit");
}
}
В библиотеке реализованы готовые инструменты для полученя данных из компонентов формы (см. документацию выше). Например выведем в порт содержимое поля ввода текста:
if (portal.form("/login")) Serial.println(portal.getString("txt"));
// где "txt" - имя компонента
Важно! Получать данные с компонента формы можно только внутри условия (здесь
if (portal.form("/name"))
), так как esp не хранит в себе код страницы, она получает конкретные данные только при действии пользователя!
В библиотеке реализован механизм, позволяющий обрабатывать действия на странице без её перезагрузки (как при использовании форм):
- Форма позволяет по нажатию одной кнопки получить значения с нескольких компонентов. Страница перезагрузится.
- Клик позволяет получить текущее (изменённое) значение только с кликнутого компонента. Страница не перезагрузится.
- При клике по некоторым компонентам или изменении их значения (см. таблицу в документации) функция
click()
вернётtrue
- Функция должна опрашиваться внутри подключенной в
attach()
функции - Для поиска компонента, с которого пришёл сигнал, используем
click(имя)
- вернётtrue
, если имя совпало- Лучше обернуть поиск в
if (click())
, чтобы не тратить процессорное время на сравнение строк
- Лучше обернуть поиск в
void action() {
if (portal.click("mybutton")) Serial.println("Click!");
}
Парсинг данных от кликов можно производить при помощи тех же функций, что и для форм.
Важно! Получать данные с компонента формы можно только внутри условия (здесь
if (portal.click("/name"))
), так как esp не хранит в себе код страницы, она получает конкретные данные только при действии пользователя!
Кнопку (BUTTON, BUTTON_MINI) можно "подключить" к другому компоненту: при клике по кнопке будет вызван сигнал click
с именем кнопки и данными с указанного компонента. Для подключения нужно указать имя компонента третьим аргументом при добавлении кнопки:
GP.BUTTON(имя кнопки, текст кнопки, имя компонента);
GP.BUTTON_MINI(имя кнопки, текст кнопки, имя компонента);
Пример, клик по кнопке отправляет текст из поля txt:
GP.TEXT("txt", "");
GP.BUTTON_MINI("btn", "Send", "txt");
В библиотеке реализован механизм скриптовых запросов со страницы по таймеру. Это позволяет обновлять значения некоторых компонентов и надписей (см. таблицу в документации) без обновления страницы в браузере.
Для включения режима обновлений нужно добавить в начало страницы блок AJAX_UPDATE:
void build() {
BUILD_BEGIN();
GP.AJAX_UPDATE("name1,name2,name3");
// ...
GP.LABEL("NAN", "val"); // будем обновлять текст
BUILD_END();
}
- Функция
AJAX_UPDATE
принимает список имён компонентов, разделённых запятой. - ПРОБЕЛ ПОСЛЕ ЗАПЯТОЙ НЕ СТАВИМ.
- Также можно указать период запросов на обновления в миллисекундах
GP.AJAX_UPDATE("name1,name2", 5000);
, по умолчанию - 1000 (1 секунда). - Не все компоненты поддерживают режим обновлений (см. таблицу в документации).
- При наступлении обновления функция
update()
вернётtrue
- Функция должна опрашиваться внутри подключенной в
attach()
функции - Для поиска компонента, с которого пришёл сигнал, используем
update(имя)
- вернётtrue
, если имя совпало- Лучше обернуть поиск в
if (update())
, чтобы не тратить процессорное время на сравнение строк
- Лучше обернуть поиск в
- Нужно ответить на запрос обновления при помощи функции
answer()
. В неё передаётся актуальное значение для компонента - Если не ответить на обновление - библиотека ответит пустым ответом, чтобы страница не зависла
void action() {
if (portal.update("val")) portal.answer(random(1000));
}
[См. примеры demoSubmitAuto и demoClickAuto] Вместо ручного парсинга можно указать библиотеке переменные, которые будут автоматически получать новые значения с указанных компонентов страницы. Это работает как для форм, так и для кликов.
- Инициализируем список, вызвав
.list.init(количество)
, передаём размер списка в количестве переменных. - Добавляем переменную по её адресу:
.list.add(&переменная, имя, тип)
- с указанием имени компонента и его типа.list.add(&переменная, форма, имя, тип)
- с указанием имени формы, имени компонента и типа
Указанные переменные обновят свои значения при действии с формы с указанным именем или при клике. Если имя формы не указано - компонент будет парситься при действии с любой формы. Для работы с кликами не нужно указывать имя формы.
Тип данных | Тип/Компонент | TEXT/NUMBER | PASS | CHECK | SWITCH | DATE | TIME | SLIDER | COLOR | SELECT | AREA |
---|---|---|---|---|---|---|---|---|---|---|---|
char[] |
T_CSTR | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |||
String |
T_STRING | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | |||
GPtime |
T_TIME | ✔ | |||||||||
GPdate |
T_DATE | ✔ | |||||||||
bool |
T_CHECK | ✔ | ✔ | ||||||||
byte , char |
T_BYTE | ✔ | ✔ | ✔ | |||||||
int , long |
T_INT | ✔ | ✔ | ✔ | |||||||
float |
T_FLOAT | ✔ | ✔ | ✔ | |||||||
uint32_t |
T_COLOR | ✔ |
Графики AJAX_PLOT
и PLOT_STOCK
несовместимы в одном интерфейсе!
У всех трёх типов графиков есть аргумент dec
, по умолчанию равен 0. Это делитель, на который (если отличен от 0) будут делиться значения точек графика и переводиться в float
.
Таким образом можно отображать данные с плавающей точкой и не хранить в памяти лишние 2 байта. Получили температуру 22.5 градусов, умножаем на 10 и сохраняем в массив. Вызываем график с dec
, равным 10.
Все графики поддерживают вывод по нескольким осям (общая ось X).
Подписи храним в массиве char, например так:
const char *names[] = {"kek", "puk",};
Статические графики отображают данные при перезагрузке страницы. Таким образом в конструктор должен быть передан массив с актуальными значениями.
В библиотеке реализованы функции для удобного добавления нового значения к массиву (с автоматической "перемоткой"):
GPaddInt(int16_t val, int16_t* arr, uint8_t am); // новое значение, массив, размер массива
GPaddUnix(uint32_t val, uint32_t* arr, uint8_t am); // новое значение, массив, размер массива
GPaddUnixS(int16_t val, uint32_t* arr, uint8_t am); // добавить секунды, массив, размер массива
Например, есть массив int arr[2][20]
- хранит 20 значений для двух осей графика. Можно обновлять его и хранить в EEPROM, обеспечивая бесперерывную работу. Для добавления нового значения делаем по своему таймеру:
GPaddInt(новое, arr[0], 20);
GPaddInt(новое, arr[1], 20);
В конструктор передаём как
GP.PLOT<2, 20>("table", names, arr);
Динамический график вызывает update
, отвечаем ему новыми значениями и он строит график в реальном времени. Для передачи значений по нескольким осям используем
answer(массив, размер)
или answer(массив, размер, dec)
, где dec имеет смысл делителя (см. выше).
Лёгкий статический график без масштаба
[См. пример staticPlot]
GP.PLOT<к-во осей, к-во данных>(имя, подписи, данные int16_t, int dec = 0)
GP.PLOT_DARK<к-во осей, к-во данных>(имя, подписи, данные int16_t, int dec = 0)
Статический график с масштабом и привязкой ко времени
[См. пример stockPlot]
GP.PLOT_STOCK<к-во осей, к-во данных>(имя, подписи, массив времён, массив данных, int dec = 0)
GP.PLOT_STOCK_DARK<к-во осей, к-во данных>(имя, подписи, массив времён, массив данных, int dec = 0)
Данный график требует для отображения массив даты и времени типа uint32_t
, содержащий время в формате unix.
Динамический график, вызывает update по своему имени, требует ответа
[См. пример ajaxPlot]
GP.AJAX_PLOT(имя, к-во осей, к-во точек по Х, период update);
GP.AJAX_PLOT_DARK(имя, к-во осей, к-во точек по Х, период update);
В библиотеке реализована возможность делать print()
в специальное окно лога на странице:
- Окно лога можно создать только одно
- Обновление происходит автоматически, раз в секунду
- Страница не обновляется
- Можно отправлять любые данные, как Serial
Добавляем GP.AREA_LOG(к-во строк)
в нужное место страницы
Вызываем log.start(размер буфера)
. Размер буфера по умолчанию 64 символа
- Примечание: это размер буфера на стороне библиотеки, то есть ограничение на количество символов на одну отправку на страницу (раз в секунду). У страницы браузера свой буфер для отображения текста!
Просто вызываем log.print()
или log.println()
как у обычного Serial. См. пример demoLog.
Добавлено в версии 2.1. Представляет собой страницу, на которой можно выбрать бинарник и обновить прошивку и SPIFFS. Включается функцией
enableOTA()
- страница обновления без пароляenableOTA("login", "pass")
- страница обновления требует авторизации с указанным логином-паролем
Страница обновления доступна по адресу x.x.x.x/ota_update
. Для обновления нужен .bin файл прошивки, его можно получить нажав Скетч/Экспорт бинарного файла. Файл появится в папке со скетчем.
Для обновления данных в SPIFFS памяти понадобится плагин для Arduino IDE, который сгенерирует бинарный файл:
- esp32 data uploader https://github.com/lorol/arduino-esp32fs-plugin
- esp8266 data uploader https://github.com/earlephilhower/arduino-esp8266littlefs-plugin
При нажатии кнопки Data Upload (и отключенной плате) снизу в логе находим путь к папке билда. Например:
- C:\Users\Alex\AppData\Local\Temp\arduino_build_232786
Оттуда и берём bin файл с датой.
Логика создания многостраничного интерфейса следующая:
- Внутри вашей функции-конструктора функция
portal.uri()
возвращает адрес открытой в браузере "страницы" (часть после IP адреса) - Страницы создаются виртуально по условиям: при совпадении адреса добавляем в страницу нужные компоненты
- Навигация может осуществляться как из адресной строки в браузере, так и при помощи кнопок-ссылок
- При создании страницы, на которой присутствует форма
form
, виртуальный адрес этой страницы должен совпадать с именем формы, чтобы после отправки формы браузер получил ту же самую страницу!
Пример, интерфейс с тремя виртуальными страницами:
/
- главная страница с кнопками-ссылками на остальные страницы/save
- здесь формаform
с текстовым полем и кнопкой сохранения, а также кнопкой-ссылкой "назад"/status
- здесь мигает "светодиод" при помощи инструментаupdate
, а также кнопка-ссылка "назад"
void build() {
BUILD_BEGIN();
GP.THEME(GP_DARK);
// страница с формой
if (portal.uri() == "/save") {
GP.FORM_BEGIN("/save");
GP.TEXT("txt", "", ""); GP.BREAK();
GP.SUBMIT("Save");
GP.FORM_END();
GP.BUTTON_LINK("/", "Back");
// страница с лампочкой
} else if (portal.uri() == "/status") {
GP.AJAX_UPDATE("led");
GP.LABEL("LED: ");
GP.LED_RED("led", 0); GP.BREAK();
GP.BUTTON_LINK("/", "Back");
// главная страница "/"
} else {
GP.BUTTON_LINK("/save", "Save page"); GP.BREAK();
GP.BUTTON_LINK("/status", "Status page");
}
BUILD_END();
}
void action() {
if (portal.form("/save")) Serial.println(portal.getString("txt"));
static bool led;
if (portal.update("led")) portal.answer(led = !led);
}
Конструктор GyverPortal ничем не ограничивает построение страницы: достаточно прибавить к строке любой HTML код между запуском GP_BUILD()
и завершением конструктора GP_SHOW()
.
Страница собирается во внутреннюю строку _GP
, но доступна она по указателю:
void build() {
GP_BUILD();
// собираем страницу
*_GP += F("some HTML code");
GP_SHOW();
}
Для справки:
Стандартный BUILD_BEGIN()
внутри состоит из:
GP.PAGE_BEGIN();
GP.AJAX_CLICK();
GP.PAGE_BLOCK_BEGIN();
Стандартный BUILD_END()
внутри состоит из:
GP.PAGE_BLOCK_END();
GP.PAGE_END();
Достаточно прибавить любой HTML код к строке, например:
*_GP += F("<input type=\"email\" class=\"myClass\">");
Можно обернуть в F macro, чтобы не занимать оперативку.
Для обеспечения работоспособности механизмов библиотеки в кастомных компонентах нужно соблюдать следующие моменты:
- Если нужна поддержка кликов - добавить в страницу
GP.AJAX_CLICK()
- У компонентов формы должен быть указан атрибут name для передачи данных через submit.
- У кликабельных компонентов должен быть указан атрибут onclick с параметром-функцией:
onclick="GP_click(this)"
. Библиотека сама перехватит вызов и направит вclick()
. - У компонентов, с которых нужен сигнал
click()
по изменению данных, должен быть указан атрибут onchange с параметром-функцией:onchange="GP_click(this)"
. Библиотека сама перехватит вызов и направит вclick()
. - У компонентов, для которых нужны обновления
update()
, должен быть указан атрибутid
. Его значение также нужно передать вGP.AJAX_UPDATE()
. - Если нужен клик, который передаёт данные с другого компонента, указываем атрибут с функцией
onclick="GP_clickid(btn,tar)"
, гдеbtn
- имя (для библиотеки) кликающего компонента, аtar
- атрибутid
целевого компонента, с которого нужно передать данные. - Для ручной передачи в библиотеку сигнала о клике нужно отправить http POST запрос вида
GP_click?имя=значение
- Для ручной передачи в библиотеку сигнала об обновлении нужно отправить http GET запрос вида
GP_update?id_компонента=
-
v1.0
-
v1.1 - улучшил графики и стили
-
v1.2
- Блок NUMBER теперь тип number
- Добавил большое текстовое поле AREA
- Добавил GPunix
- Улучшил парсинг
- Добавил BUTTON_MINI
- Кнопки могут передавать данные с других компонентов (кроме AREA и чекбоксов)
- Добавил PLOT_STOCK - статический график с масштабом
- Добавил AJAX_PLOT_DARK
- Изменён синтаксис у старых графиков
- Фичи GPaddUnix и GPaddInt для графиков
- Убрал default тему
- Подкрутил стили
- Добавил окно лога AREA_LOG и функцию лога в целом
-
v1.3 - переделал GPunix, мелкие фиксы, для списков можно использовать PSTR
-
v1.4 - мелкие фиксы, клик по COLOR теперь отправляет цвет
-
v1.5 - добавил блок "слайдер+подпись"
-
v1.5.1 - мелкий фикс копирования строк
-
v1.5.2 - добавлен meta charset="utf-8", английский README (спасибо VerZsuT)
-
v1.6 - добавлены инструменты для работы c цветом. Добавил answer() для даты, времени и цвета
-
v1.7 - поддержка ESP32
-
v2.0: Большое обновление! Логика работы чуть изменена, обнови свои скетчи!
- Много оптимизации/облегчения/ускорения
- Полная поддержка ESP32
- Переделана логика опроса действий (более правильно и оптимально + работает на ESP32) с сохранением легаси
- Убран DateTimeP (не используется в библиотеке) и вынес отдельно в библиотеку DatePack
- Переделан и облегчен модуль лога (log)
- Добавлен MDNS, чтобы не искать IP платы в мониторе порта (см. доку)
- Автоопределение режима работы WiFi. Переделан start() с сохранением легаси (см. доку)
- Упрощён билдер, строку создавать и передавать не нужно (см. доку)
- Объект билдера теперь называется GP (вместо add) с сохранением легаси
- Пофикшены варнинги
- Добавлены удобства для работы с цветом GPcolor, датой GPdate и временем GPtime
- Удалены старые функции преобразования цвета и даты-времени (см. доку)
- Портал теперь возвращает цвет в формате GPcolor, автообновление переменных тоже работает с GPcolor
- Все примеры протестированы на esp8266 и esp32
-
v2.1
- Вернул функции root() и uri() для удобства создания многостраничности
- Добавлен пример организации многостраничности
- Добавлена кнопка-ссылка BUTTON_LINK
- Добавлена авторизация по логину-паролю (см. доку)
- Добавлено OTA обновление прошивки из браузера, в т.ч. с паролем (см. доку)
-
v3.0: Очень много всего нового, всё не смог перечислить =)
- Огромное спасибо DenysChuhlib и DAK85 за идеи и наработки!
- Добавлен "объектный" режим работы, в котором компоненты удобнее конфигурируются, автоматически получают новые значения и код программы становится сильно компактнее
- Полностью переписан механизм конструктора, сборка занимает во много раз меньше памяти в SRAM за счёт отправки страницы частями
- Переделан механизм добавления кастомного кода на страницу
- Аргументы конструктора теперь принимают const String& - можно передавать строки, const строки, F macro строки
- Переделаны строковые утилиты
- Полностью переделан слайдер
- Убран вариант слайдера с текстом и компонент LABEL_MINI
- Добавлена возможность задания ширины некоторым компонентам
- У некоторых компонентов появилась опция "только чтение"
- Редизайн светодиодов LED GREEN/RED, добавлен LED (красно-зелёный)
- Добавлен компонент BOX_BEGIN/BOX_END, позволяющий удобно собирать компоненты в группы с нужным размером и выравниванием
- Добавлен блок LABEL_BLOCK для выделения текста
- Внутренний AJAX_CLICKS заменён на JS_TOP
- Переделан основной контейнер страницы для удобства кастомизации под любую ширину интерфейса
- Добавлен элемент навигации по динамическим вкладкам NAV_TABS (+ NAV_BLOCK_BEGIN и NAV_BLOCK_END)
- Добавлен элемент навигации с кнопками-ссылками NAV_TABS_LINKS
- Добавлена поддержка FontAwesome иконок для кнопок и панели навигации https://fontawesome.com/v4/icons/
- Пофикшена бага при использовании старого сценария опроса действий
- AJAX_UPDATE переименован в UPDATE с сохранением легаси
- Добавлен блок FILE_UPLOAD для загрузки файлов на сервер
- Добавлен удобный механизм скачивания файлов из SPIFFS памяти с поддержкой 33 типов файлов
- Добавлены блоки для вывода изображений, видео и текстовых файлов из SPIFFS
- Примеры переименованы и сгруппированы по смыслу, добавлены новые примеры
- Добавлен механизм request
- Подключаемым функциям добавлены варианты с адресом на GyverPortal
- Добавлены более удобные варианты компонента SELECT и способы его опроса (getSelectedIdx)
- Механизм update теперь работает с SELECT блоками
- Добавлен шаблон для удобного создания кастомных блоков
- Исправлена работа кликов и обновлений на подстраницах
- Добавлена мини кнопка-ссылка + кнопки для скачивания файлов
- Добавлен оффлайн-режим для графиков (не нужно подключение к Интернет)
- Добавлен блок для добавления стилей из spiffs
- SLIDER теперь умеет работать с float, добавлен NUMBER_F для float
- Добавлен элемент SPINNER
- AREA теперь отсылает сигнал click
- Добавлены макросы для удобной сборки блоков
- И прочее прочее
При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru Библиотека открыта для доработки и ваших Pull Request'ов!
При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
- Версия библиотеки
- Какой используется МК
- Версия SDK (для ESP)
- Версия Arduino IDE
- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код