Simple web interface builder for esp8266 and ESP32

Related tags

Arduino GyverPortal
Overview

Foo Foo Foo

GyverPortal

Простой конструктор веб интерфейса для esp8266 и ESP32

  • Простой конструктор - делаем страницы без знаний HTML и CSS
  • Библиотека является обёрткой для стандартной ESP8266WebServer
  • Позволяет быстро создать вебморду для управления и настройки своего девайса
  • Компактный читаемый код в "скетче", никаких внешних обработчиков и лямбда-функций
  • Конструктор использует стандартные HTML формы, CSS и javascript
  • Элементы конструктора хранятся во Flash памяти
  • Никаких глобальных буферов, всё генерируется на лету
  • Приятный дизайн из коробки + тёмная тема
  • Адаптировано под мобильные устройства и ПК
  • Встроенные инструменты для удобного парсинга значений с формы
  • Возможность настроить автоматическое обновление значений переменных по действию со страницы
  • Встроенные жабаскрипты для AJAX, работа без обновления всей страницы:
    • Клики по компонентам, изменение их значений
    • Обновление компонентов по таймеру
    • График в реальном времени
    • Текстовое окно отладки (отправляем из программы)
  • Компоненты конструктора:
    • Заголовок
    • Подпись
    • Разделитель
    • Перенос строки
    • Блок для объединения компонентов
    • Веб-форма (блок)
    • Кнопка submit (для форм)
    • Поле ввода текста
    • Многострочное поле ввода текста
    • Поле ввода пароля
    • Галочка (чекбокс)
    • Выключатель
    • Слайдер
    • Выбор времени
    • Выбор даты
    • Селектор (дропбокс)
    • Кнопка
    • Мини кнопка
    • "Светодиод" индикатор
    • Окно лога для отладки (веб Serial порт)
    • Несколько типов графиков (требуется интернет)

demo

Возможности

Библиотека позволяет генерировать динамический веб интерфейс для управления своим электронным устройством из браузера (нужно зайти на IP адрес платы в строке адреса). Три основных способа взаимодействия с интерфейсом и сценарии использования:

  • Форма и кнопка отправить: при нажатии на кнопку submit страница перезагружается, а в программу приходят данные со всех компонентов, входящих в форму (текст в поле ввода, положения слайдеров и чекбоксов, и так далее). Удобно для однократного ввода данных (настройки подключения и тому подобное).
  • Клик по компоненту: при клике на почти любой компонент интерфейса (при изменении его состояния или значения) можно получить его актуальное значение без перезагрузки страницы. Удобно для управления и настройки (галочки, кнопки, слайдеры).
  • Обновление значений и состояний компонентов в реальном времени без перезагрузки страницы. Удобно для индикации работы и получения текущих численных и текстовых значений из программы, вывод графиков в реальном времени.

Совместимость

esp8266, esp32

Содержание

Установка

  • Библиотеку можно найти по названию 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 библиотеку… и указать скачанный архив
  • Читай более подробную инструкцию по установке библиотек здесь

Инициализация

GyverPortal portal;

Документация

Функции конструктора
// создание страницы
BUILD_BEGIN(строка);                // начать построение. Добавляет начальный HTML код
BUILD_END();                        // завершить построение. Добавляет завершающий HTML код и открывает страницу

// темы
add.THEME(тема);                    // установить тему
GP_LIGHT    - светлая тема
GP_DARK     - тёмная тема

// создание формы
add.FORM_BEGIN(имя);                // начать форму с именем (имя)
add.FORM_END();                     // завершить форму

// пустая форма с одной кнопкой
add.FORM_SUBMIT(имя, текст);        // форма с именем (имя) кнопкой (текст)

// компоненты формы
add.BUTTON(имя, текст);             // кнопка
add.BUTTON(имя, текст, id);         // кнопка (id компонента, данные с которого кнопка отправит данные по click)
add.BUTTON_MINI(имя, текст);        // мини кнопка
add.BUTTON_MINI(имя, текст, id);    // мини кнопка (id компонента, данные с которого кнопка отправит данные по click)

add.NUMBER(имя, подсказка, число);  // поле ввода текста, число
add.TEXT(имя, подсказка, текст);    // поле ввода текста
add.PASS(имя, подсказка, текст);    // поле ввода пароля
add.CHECK(имя);                     // чекбокс, умолч. выключен
add.CHECK(имя, состояние);          // чекбокс
add.SWITCH(имя);                    // выключатель, умолч. выключен
add.SWITCH(имя, состояние);         // выключатель
add.DATE(имя);                      // ввод даты, умолч. пустой
add.DATE(имя, GPdate);              // ввод даты
add.TIME(имя);                      // ввод времени, умолч. пустой
add.TIME(имя, GPtime);              // ввод времени
add.SELECT(имя, список);            // селектор (дропбокс), элементы списка разделены запятой. Список может быть PSTR
add.SELECT(имя, список, активный);  // + текущий активный пункт
add.SLIDER(имя, число, мин, макс);       // слайдер
add.SLIDER(имя, число, мин, макс, шаг);  // слайдер
add.COLOR(имя);                     // выбор цвета, умолч. чёрный
add.COLOR(имя, число);              // выбор цвета
add.SUBMIT(текст);                  // кнопка отправки формы
add.LED_RED(имя);                   // красный светодиод-индикатор
add.LED_GREEN(имя);                 // зелёный светодиод-индикатор

// прочее для оформления
add.TITLE(текст);                   // заголовок
add.TITLE(текст, имя);              // + имя компонента (для update())
add.LABEL(текст);                   // подпись (для кнопок, полей, чекбоксов итд)
add.LABEL(текст, имя);              // + имя компонента (для update())
add.LABEL(число);                   // подпись, число
add.LABEL(число, имя);              // + имя компонента (для update())
add.AREA(имя, к-во строк, текст);   // большое поле ввода текста
add.AREA(имя, к-во строк);          // большое поле ввода текста
add.BREAK();                        // перенести строку
add.HR();                           // горизонтальный разделитель
add.BLOCK_BEGIN();                  // начать отрисовку блока
add.BLOCK_END();                    // завершить отрисовку блока

// прочее
add.AJAX_UPDATE(список, период);    // передать список обновления компонентов
// для списка можно использовать макрос PSTR

// большое поле для ввода текста
add.AREA(имя, к-во строк, текст);
add.AREA(имя, к-во строк);
add.AREA(имя);

// графики
// лёгкий статичный график без масштаба
add.PLOT<к-во осей, к-во данных>
    (имя, подписи, данные 
    int16_t, 
    int dec = 
    0)
add.PLOT_DARK
    <к-во осей, к-во данных>
     (имя, подписи, данные 
     int16_t, 
     int dec = 
     0)


     // статический график с масштабом и привязкой ко времени
add.PLOT_STOCK
     <к-во осей, к-во данных>
      (имя, подписи, массив времён, массив данных, 
      int dec = 
      0)
add.PLOT_STOCK_DARK
      <к-во осей, к-во данных>
       (имя, подписи, массив времён, массив данных, 
       int dec = 
       0)


       // динамический график, вызывает update
add.AJAX_PLOT(имя, к-во осей, к-во точек по Х, период update);
add.AJAX_PLOT_DARK(имя, к-во осей, к-во точек по Х, период update);


       // создание кастомной страницы

       GP_BUILD(строка);                   
       // запустить конструктор (можно вызывать где угодно)

       GP_SHOW();                          
       // отобразить страницу (вызывать только внутри функции конструктора!)
add.PAGE_BEGIN();                   
       // начальный HTML код
add.PAGE_BLOCK_BEGIN()              
       // центральный div блок
add.PAGE_BLOCK_END()                
       // центральный div блок
add.PAGE_END();                     
       // завершающий HTML код
add.AJAX_CLICK();                   
       // обработчик кликов
      
     
    
   
Методы класса
// система
void start();                       // запустить портал
void start(WIFI_AP);                // запустить портал с DNS сервером (для AP)
void stop();                        // остановить портал// показать свою страницу
void showPage(String&);             // показать свою страницу
void show();                        // вызвать конструктор и показать страницу

// подключение
void attachBuild(void*);            // подключить функцию-билдер страницы
void attachForm(void*);             // подключить функцию, которая вызывается при нажатии submit
void attachClick(void*);            // подключить функцию, которая вызывается при клике (кнопка, чекбокс, свитч, слайдер, селектор)
void attachUpdate(void*);           // подключить функцию, которая вызывается при AJAX обновлении со страницы

// опрос
bool tick();                        // тикер портала. Вернёт true, если портал запущен

bool form();                        // вернёт true, если было нажатие на любой submit
bool form(char* name);              // вернёт true, если был submit с указанной формы
String& formName();                 // получить имя теукщей submit формы

bool click();                       // вернёт true, если был клик по (кнопка, чекбокс, свитч, слайдер, селектор)
bool click(char* name);             // вернёт true, если был клик по указанному элементу
String& clickName();                // получить имя теукщего кликнутого компонента
const String& clickText();          // получить имя текст кликнутого компонента

bool update();                      // вернёт true, если было обновление
bool update(char* name);            // вернёт true, если было update с указанного компонента
String& updateName();               // вернёт имя обновлённого компонента
void answer(String& s);             // отправить ответ на обновление
void answer(int s);
void answer(char* s);
void answer(int16_t* v, int am);         // массив int размерностью am, для графика
void answer(int16_t* v, int am, int dec);// + делитель

bool root();                        // вернёт true, если открыта главная страница (/)
String& uri();                      // адрес текущего запроса

// список автообновления
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 байта, для цвета

// парсеры
String getString(char* name);       // получить String строку с компонента name
char* getChars(char* name);         //получить char* строку с компонента name
void copyStr(char* name, char* dest);   // переписать char строку с компонента name к себе в dest
long getInt(char* name);            // получить целое число с компонента name
float getFloat(char* name);         // получить float число с компонента name
bool getCheck(char* name);          // получить состояние чекбокса с компонента name
GPdate getDate(char* name);         // получить дату с компонента name в формате GPdate
GPtime getTime(char* name);         // получить время с компонента name в формате GPtime
uint32_t getColor(char* name);      // получить цвет с компонента name
uint8_t getSelected(char* name, char* list);    // получить номер выбранного пункта в дроплисте list (может быть PSTR)
Хранение и изменение времени
// структура для хранения даты
struct GPdate {
  int16_t year;
  uint8_t month, day;
};

// структура для хранения времени
struct GPtime {
  uint8_t hour, minute, second;
};

// получить unix время для графика
uint32_t GPunix(год, месяц, день, час, минута, секунда);
uint32_t GPunix(год, месяц, день, час, минута, секунда, gmt);
// gmt - часовой пояс, по умолч. 0 (пример: Москва gmt = 3)
// месяц и день начинаются с 1, не с 0!
Утилиты
char* splitList(char* str);             // разделить строку на подстроки. Цыганские фокусы
int8_t inList(char* name, char* list);  // получить номер, под которым name входит в list (вида "val1,val2,val3")
int8_t inList(String& name, char* list);

String encodeDate(GPdate& d);           // склеить дату в строку String
void encodeDate(char* str, GPdate& d);  // склеить дату в строку str[11]
String encodeDate(year, month, day);    // склеить дату в строку String
GPdate decodeDate(char* str);           // разобрать строковую дату[11] в структуру

String encodeTime(GPtime& t);           // склеить время в строку String
void encodeTime(char* str, GPtime& t);  // склеить время в строку str[9]
String encodeTime(hour, minute, second);// склеить время в строку String
GPtime decodeTime(char* str);           // разобрать строковое время[9] в структуру

String encodeColor(uint32_t color);     // собрать цвет в String #rrggbb
uint32_t decodeColor(char* hex);        // разобрать цвет #rrggbb в число

// добавить новое значение в массив с перемоткой (для графиков)
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);     // добавить секунды, массив, размер массива

Таблица поддержки режимов работы компонентами

Компонент/Вызов form() click() update()
TITLE
LABEL
BUTTON
BUTTON_MINI
NUMBER
TEXT
PASS
AREA
CHECK
SWITCH
DATE
TIME
SLIDER
COLOR
SELECT
LED_RED
LED_GREEN

1. Сервер

Библиотека может работать как в локальной сети (esp подключается к роутеру), так и в режиме точки доступа (смартфон подключается к esp).

1.1 Подключение к роутеру

  WiFi.mode(WIFI_STA);
  WiFi.begin("login", "pass");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(WiFi.localIP());
  // ...
  portal.start();   // запускаем портал

Для подключения к порталу нужно зайти в браузере на IP адрес платы, который выдал ей роутер. В примере выше этот адрес выводится в монитор порта.

1.2 Создание точки

В этом режиме при запуске портала нужно передать WIFI_AP для запуска DNS сервера

  WiFi.mode(WIFI_AP);
  WiFi.softAP("My Portal");
  portal.start(WIFI_AP);   // запускаем портал с настройкой на режим AP

На стандартных настройках IP адрес для подключения в этом режиме будет 192.168.4.1

1.3 Тикер

В GyverPortal используется стандартная библиотека ESP8266WebServer, поэтому для обеспечения работы сервера нужно вызывать portal.tick() в цикле программы. Возвращает true, если сервер запущен в данный момент.

1.4 Создаём GyverPortal глобально

GyverPortal portal;
void build() {}

void setup() {
  // подключаемся к сети
  portal.attachBuild(build);
  portal.start();
}

void loop() {
  portal.tick();
  // опрос действий
}

1.5 Создаём GyverPortal локально

void build() {}

void f() {
  GyverPortal portal;
  portal.attachBuild(build);
  portal.start();

  while (portal.tick()) {
    // опрос действий
  }
}

Для выхода из цикла можно вызвать portal.stop() по таймауту или сигналу с браузера.

2. Конструктор страниц

2.1. Создаём функцию конструктора

  1. Создаём функцию вида: void f(). Далее в ней:
  2. Создём пустую строку: String s;.
  3. Запускаем конструктор: BUILD_BEGIN(s). Передаём созданную строку. Здесь добавляется начальный HTML код.
  4. (Опционально) применяем тему: add.THEME(тема), GP_LIGHT/GP_DARK
  5. Строим страницу, используя конструктор или прибавляя свои данные к строке.
  6. Завершаем работу конструктора: BUILD_END(). Здесь добавляется завершающий HTML код и страница отправляется на сервер.

Шаблон функции конструктора:

void build() {
  String s;
  BUILD_BEGIN(s);
  add.THEME(GP_LIGHT);
  // собираем страницу
  // ...
  BUILD_END();
}

2.2 Подключаем функцию конструктора

Передаём в библиотеку нашу функцию-конструктор страницы:

portal.attachBuild(build);

Библиотека сама будет вызывать её, когда потребуется отобразить страницу. Функций-конструкторов (а следовательно и страниц) может быть несколько и их можно переключать.

3. Использование форм

3.1 Собираем страницу с формами

Основная суть использования форм:

  • Форма имеет своё уникальное имя, должно начинаться с /
  • Внутри формы может быть сколько угодно элементов, но только одна кнопка типа SUBMIT
  • При нажатии на SUBMIT esp получает имя формы и данные из всех элементов внутри этой формы
  • При нажатии на SUBMIT страница перезагружается, поэтому значения компонентов страницы нужно хранить в переменных и передавать при следующей сборке страницы

Пример с двумя формами, первая может передать текст из окна ввода, вторая - только факт нажатия кнопки:

форма_1
    ввод текста
    кнопка submit
форма_1

форма_2
    кнопка submit
форма_2

В конструкторе GyverPortal это будет выглядеть так:

void build() {
  String s;                     // создать строку
  BUILD_BEGIN(s);               // запустить конструктор
  add.THEME(GP_LIGHT);          // применить тему

  add.FORM_BEGIN("/login");     // начать форму, передать имя
  add.TEXT("txt", "Login", ""); // ввод текста, подсказка Login, текста нет
  add.BREAK();                  // перенос строки
  add.SUBMIT("Submit");         // кнопка Submit
  add.FORM_END();               // завершить форму

  add.FORM_BEGIN("/exit");      // начать форму, передать имя
  add.SUBMIT("Exit");           // кнопка Exit
  add.FORM_END();               // завершить форму

  BUILD_END();                  // завершить построение
}

Результат работы конструктора:
demo
Все инструменты конструктора описаны в документации выше.

3.2 Опрос действий

  • При нажатии любой кнопки типа SUBMIT в браузере функция form() вернёт true
  • Функция должна опрашиваться после tick()
  • Для поиска формы, с которой пришёл сигнал, используем form(имя) - вернёт true, если имя совпало
portal.tick();
if (portal.form()) {
  Serial.print("Submit form: ");
  if (portal.form("/login")) Serial.println("Login");
  if (portal.form("/exit")) Serial.println("Exit");
}

3.3 Подключение обработчика

Вместо ручного опроса form() можно подключить свою функцию вида void f(GyverPortal*), она будет вызвана при нажатии на любой SUBMIT. Эту функцию нужно будет передать в attachForm().

void myAction(GyverPortal* p) {
  // имеем доступ к объекту портала, который отправил вызов
  if (p -> form("/exit")) Serial.println("exit");
}

void setup() {
  portal.attachForm(myAction);
}

3.4 Парсинг данных

В библиотеке реализованы готовые инструменты для полученя данных из компонентов формы (см. документацию выше). Например выведем в порт содержимое поля ввода текста:

portal.tick();
if (portal.form("/login")) Serial.println(portal.getString("txt"));
// где "txt" - имя компонента

4. Использование кликов

4.1 Отличие от форм

В библиотеке реализован механизм, позволяющий обрабатывать действия на странице без её перезагрузки (как при использовании форм):

  • Форма позволяет по нажатию одной кнопки получить значения с нескольких компонентов. Страница перезагрузится.
  • Клик позволяет получить текущее (изменённое) значение только с кликнутого компонента. Страница не перезагрузится.

4.2 Опрос действий

  • При клике по некоторым компонентам или изменении их значения (см. таблицу в документации) функция click() вернёт true
  • Функция должна опрашиваться после tick()
  • Для поиска компонента, с которого пришёл сигнал, используем click(имя) - вернёт true, если имя совпало
portal.tick();
if (portal.click("mybutton")) Serial.println("Click!");

4.3 Подключение обработчика

Вместо ручного опроса click() можно подключить свою функцию вида void f(GyverPortal*), она будет вызвана при нажатиях на компоненты. Эту функцию нужно будет передать в attachClick().

void myClick(GyverPortal* p) {
  // имеем доступ к объекту портала, который отправил вызов
  if (p -> click("mybutton")) Serial.println("Click!");
}

void setup() {
  portal.attachClick(myClick);
}

4.4 Парсинг данных

Парсинг данных от кликов можно производить при помощи тех же функций, что и для форм.

4.5 Подключение кнопки на другой компонент

Кнопку можно подключить к другому компоненту, при клике по кнопке будет вызван update с именем кнопки и данными из указанного места. Для подключения нужно указать имя компонента третьим аргументом:

add.BUTTON(имя кнопки, текст кнопки, имя компонента);
add.BUTTON_MINI(имя кнопки, текст кнопки, имя компонента);

5. Использование обновлений

В библиотеке реализован механизм скриптовых запросов со страницы по таймеру. Это позволяет обновлять значения некоторых компонентов и надписей (см. таблицу в документации) без обновления страницы в браузере.

5.1 Подключение обновлений

Для включения режима обновлений нужно добавить в начало страницы блок AJAX_UPDATE:

void build() {
  String s;
  BUILD_BEGIN(s);
  add.AJAX_UPDATE("name1,name2,name3");
  // ...
  add.LABEL("NAN", "val");  // будем обновлять текст
  BUILD_END();
}
  • Функция AJAX_UPDATE принимает список имён компонентов, разделённых запятой.
  • ПРОБЕЛ ПОСЛЕ ЗАПЯТОЙ НЕ СТАВИМ.
  • Также можно указать период запросов на обновления в миллисекундах add.AJAX_UPDATE("name1,name2", 5000);, по умолчанию - 1000 (1 секунда).
  • Не все компоненты поддерживают режим обновлений (см. таблицу в документации).

5.2 Опрос обновлений

  • При наступлении обновления функция update() вернёт true
  • Функция должна опрашиваться после tick()
  • Для поиска компонента, с которого пришёл сигнал, используем update(имя) - вернёт true, если имя совпало
  • Нужно ответить на запрос обновления при помощи функции answer(). В неё передаётся актуальное значение для компонента
  • Если не ответить на обновление до следующего вызова tick() - библиотека ответит пустым ответом, чтобы страница не зависла
portal.tick();
if (portal.update()) {
  if (portal.update("val")) portal.answer(random(1000));
}

5.3 Подключение обработчика

Вместо ручного опроса update() можно подключить свою функцию вида void f(GyverPortal*), она будет вызвана при обновлении любого компонента. Эту функцию нужно будет передать в attachUpdate().

answer(random(1000)); } void setup() { portal.attachUpdate(myUpdate); }">
void myUpdate(GyverPortal* p) {
  if (p -> update("val")) p -> answer(random(1000));
}

void setup() {
  portal.attachUpdate(myUpdate);
}

6. Автоматическое обновление переменных

[См. примеры demoSubmitAuto и demoClickAuto] Вместо ручного парсинга можно указать библиотеке переменные, которые будут автоматически получать новые значения с указанных компонентов страницы. Это работает как для форм, так и для кликов.

  • Инициализируем список, вызвав .list.init(количество), передаём размер списка в количестве переменных.
  • Добавляем переменную по её адресу:
    • .list.add(&переменная, имя, тип) - с указанием имени компонента и его типа
    • .list.add(&переменная, форма, имя, тип) - с указанием имени формы, имени компонента и типа

Указанные переменные обновят свои значения при действии с формы с указанным именем или при клике. Если имя формы не указано - компонент будет парситься при действии с любой формы. Для работы с кликами не нужно указывать имя формы.

6.1 Поддержка фичи компонентами, связь с типами

Тип данных Тип/Компонент 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

7. Графики

7.1 Общие особенности

Совместимость

Графики 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);

В конструктор передаём как

add.PLOT<2, 20>("table", names, arr);

Обновление динамических графиков

Динамический график вызывает update, отвечаем ему новыми значениями и он строит график в реальном времени. Для передачи значений по нескольким осям используем answer(массив, размер) или answer(массив, размер, dec), где dec имеет смысл делителя (см. выше).

7.2 График PLOT

Лёгкий статический график без масштаба
[См. пример staticPlot]
demo

add.PLOT<к-во осей, к-во данных>
   (имя, подписи, данные 
   int16_t, 
   int dec = 
   0)
add.PLOT_DARK
   <к-во осей, к-во данных>
    (имя, подписи, данные 
    int16_t, 
    int dec = 
    0)
   
  

7.3 График PLOT_STOCK

Статический график с масштабом и привязкой ко времени
[См. пример stockPlot]
demo

add.PLOT_STOCK<к-во осей, к-во данных>
   (имя, подписи, массив времён, массив данных, 
   int dec = 
   0)
add.PLOT_STOCK_DARK
   <к-во осей, к-во данных>
    (имя, подписи, массив времён, массив данных, 
    int dec = 
    0)
   
  

Данный график требует для отображения массив даты и времени типа uint32_t, содержащий время в формате unix.

7.4 График AJAX_PLOT

Динамический график, вызывает update по своему имени, требует ответа
[См. пример ajaxPlot]
demo

add.AJAX_PLOT(имя, к-во осей, к-во точек по Х, период update);
add.AJAX_PLOT_DARK(имя, к-во осей, к-во точек по Х, период update);

8. Лог

В библиотеке реализована возможность делать print() в специальное окно лога на странице:

  • Окно лога можно создать только одно
  • Обновление происходит автоматически, раз в секунду
  • Страница не обновляется
  • Можно отправлять любые данные, как Serial

8.1 Подключение окна лога

Добавляем add.AREA_LOG(к-во строк) в нужное место страницы

8.2 Запуск лога

Вызываем log.start(размер буфера). Размер буфера по умолчанию 64 символа

  • Примечание: это размер буфера на стороне библиотеки, то есть ограничение на количество символов на одну отправку на страницу (раз в секунду). У страницы браузера свой буфер для отображения текста!

8.3 Вывод данных

Просто вызываем log.print() или log.println() как у обычного Serial. См. пример demoLog.

9. Свои компоненты, API

9.1 Кастомный конструктор

Конструктор GyverPortal ничем не ограничивает построение страницы: достаточно прибавить к строке любой HTML код между запуском GP_BUILD(s) и завершением конструктора GP_SHOW():

void build() {
  String s;
  GP_BUILD(s);
  // собираем страницу
  // ...
  GP_SHOW();
}

Для справки:
Стандартный BUILD_BEGIN(String) внутри состоит из:

  GP_BUILD(s);
  add.PAGE_BEGIN();
  add.AJAX_CLICK();
  add.PAGE_BLOCK_BEGIN();

Стандартный BUILD_END() внутри состоит из:

 add.PAGE_BLOCK_END();
 add.PAGE_END();
 GP_SHOW();

9.2 Свой код

Достаточно прибавить любой HTML код к строке, например:

");">
s += F("\"email\" class=\"myClass\">");

Можно обернуть в F macro, чтобы не занимать оперативку.

9.3 API

Для обеспечения работоспособности механизмов библиотеки в кастомных компонентах нужно соблюдать следующие моменты:

  • Если нужна поддержка кликов - добавить в страницу add.AJAX_CLICK()
  • У компонентов формы должен быть указан атрибут name для передачи данных через submit.
  • У кликабельных компонентов должен быть указан атрибут onclick с параметром-функцией: onclick="GP_click(this)". Библиотека сама перехватит вызов и направит в click().
  • У компонентов, с которых нужен сигнал click() по изменению данных, должен быть указан атрибут onchange с параметром-функцией: onchange="GP_click(this)". Библиотека сама перехватит вызов и направит в click().
  • У компонентов, для которых нужны обновления update(), должен быть указан атрибут id. Его значение также нужно передать в add.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 теперь отправляет цвет

Баги и обратная связь

При нахождении багов создавайте Issue, а лучше сразу пишите на почту [email protected] Библиотека открыта для доработки и ваших Pull Request'ов!

Issues
  • ESP32 platform.io

    ESP32 platform.io

    Буду премного благодарен, если выложите, рабочую версию примера wifiLogin для ESP32 и platform.io

    Перенес void loginPortal() выше void setup() сменил пин кнопки. Стало компилироваться и прошиваться. AP запускается подключается, в терминале Portal start но зайти на 192.168.4.1 не получается Заранее благодарю

    opened by Juri4 8
  • неявный лимит размерности графиков

    неявный лимит размерности графиков

    Необходимо или четко обозначить в документации Лимит числа точек в 256 единиц, или что на мой взгляд правильнее - показать формулу расчета максимального числа точек - вплоть до всей свободной памяти.

    opened by Nordicx86 6
  • Добавить больше возможностей работы с цветом

    Добавить больше возможностей работы с цветом

    Я может быть невнимательный, но пересмотрел все исходники и не нашел как из uint32_t цвета достать привычные RGB компоненты. И наоборот - перегнать RGB в uint32_t - очень не хватает. Если оно где-то все же есть - ткните носом плиз

    opened by devDebajo 3
  • обновление данных в поле ввода текста

    обновление данных в поле ввода текста

    Добрый день, подскажите как правильно обновлять данные в области add.TEXT("txt", "", valtext);, в общем задача следующая в данной области длинна строки не должна превышать 5 символов, по задумке если число символов в строке больше 5 то после обновления область add.TEXT("txt", "", valtext); в ней должно остаться только 5 символов, но как сделать обновление никак не разберусь. Заранее спасибо.

    opened by Aleksey-748 1
  • divide h and cpp

    divide h and cpp

    Возможное решение для https://github.com/GyverLibs/GyverPortal/issues/16 В Platformio работает, в Arduino ide не тестировал. Объявление String* _gp_sptr = nullptr; void* _gp_ptr = nullptr; из utils.h пришлось перенести в GyverPortal.cpp. Возможно комментарии надо перенести в файлы .h, не смотрел как правильно

    opened by IlnurKashapov 0
  • Разбить на заголовочные файлы .h и .сpp

    Разбить на заголовочные файлы .h и .сpp

    При работе в Platformio при использовании библиотеки в отдельном файле (не main.cpp) проекта сталкиваешься с подобной ошибкой https://arduino.stackexchange.com/questions/71700/platformio-collect2-exe-error-ld-returned-1-exit-status

    opened by IlnurKashapov 1
  • Базовая авторизация

    Базовая авторизация

    Можно ли добавить возможность включения базовой авторизации? Как я понимаю, это по идее должно быть в методе showPage что то типа: if(_is_enabled_auth) if (!server.authenticate(_www_username, _www_password)) return server.requestAuthentication(DIGEST_AUTH, _www_realm, "Authentication Failed");

    И вспомогательные методы: void setPassword(const char* www_password) { _www_password = www_password; }

    void setUserName(const char* www_username) { _www_username = www_username; }

    void onAuthMode() { _is_enabled_auth = true; }

    void offAuthMode() { _is_enabled_auth = false; }

    opened by ZinovkinDmitry 1
  • Аналоги Web библиотек

    Аналоги Web библиотек

    EmbUI https://github.com/DmytroKorniienko/EmbUI Асинхронная, стабильная, с открытым исходным кодом, есть встроенный клиент mqtt, чутка своеобразен в написании кода, с авто построением интерфейса по коду, можно писать кастомные модули, есть обратная связь, есть авторизация при входе Написанные на нем популярные для своего круга проект лампы https://github.com/DmytroKorniienko/FireLamp_JeeUI

    CRMui https://github.com/WonderCRM/CRMui3 Асинхронная, стабильная, с полу открытым исходным кодом, есть баннер не для коммерческого использования, есть подобие pwa и прост в написании кода, с авто построением интерфейса по коду, красивый с темной темой возможно менять цвета фона, нет возможности писать кастомные модули, есть обратная связь, есть авторизация при входе На его старом интерфейсе написаны часы https://cloud.mail.ru/public/5eHE/dCHUyqrr1/WiFi-CLOCK/

    JeeUI https://github.com/jeecrypt/JeeUIFramework Прародитель EmbUI CRMui На нем был написан прикольный проектик часов который перешел в CRMui Все возможности как EmbUI, не полностью доделанная есть кое какие баги, первая версия красивая в плане css, с авто построением интерфейса по коду, нет возможности писать кастомные модули, есть приложение

    WiFiManager https://github.com/tzapu/WiFiManager Нет асинхронности, большей части ее используют для написания своего проекта для упрощения кода, интерфейс нужно писать или изменить предлагаемый, но есть авто построение меню. Похоже на GyverPortal

    Если развивается то в правильную сторону

    opened by phenomenonRT 0
  • Обработка кликов по форме.

    Обработка кликов по форме.

    Здравствуйте! Пробую работу примера menuTabs. Не могу найти причину странного отрабатывания логики. при на жатии на кнопку передается предыдущее значение. при нажатии на туже кнопку повторно, передается правильное значение. image

    https://user-images.githubusercontent.com/12124643/154198881-da7ea20a-73eb-4edd-a873-b74eda613011.mp4

    opened by Juri4 4
Owner
Alex
Alex
Library for ESP32 and ESP8266 to work with the Fernando K app

App Fernando K This library is meant to work with the Fernando K app https://play.google.com/store/apps/details?id=com.appfernandok https://apps.apple

null 0 Aug 5, 2020
The Approximate Library is a WiFi Arduino library for building proximate interactions between your Internet of Things and the ESP8266 or ESP32

The Approximate Library The Approximate library is a WiFi Arduino Library for building proximate interactions between your Internet of Things and the

David Chatting 98 Jun 25, 2022
A library for writing modern websockets applications with Arduino (ESP8266 and ESP32)

Arduino Websockets A library for writing modern websockets applications with Arduino (see prerequisites for supported platforms). This project is base

Gil Maimon 317 Jun 27, 2022
Arduino library for sending email and SMS from nothing but the ESP8266!

Did you know your ESP8266 could send Email and SMS without any special hardware or paid services like Twilio? With AlertMe, your ESP8266 project can:

Lixie Labs 61 Feb 24, 2022
A library to simplify the process of getting and storing data to Antares IoT Platform through HTTP on ESP8266.

Antares ESP8266 HTTP This is the documentation for Antares ESP8266 library. This library is meant to simplify the process of retrieving and deploying

null 6 Jul 2, 2021
A library to simplify the process of subscribing and publishing data to Antares IoT Platform through MQTT on ESP8266.

Antares ESP8266 MQTT A Library to simplify the process of MQTT publication and subscription to Antares IoT Platform using ESP8266. This library works

null 6 Mar 9, 2022
This Arduino IDE for ArduCAM ESP8266 UNO Board with Integrated ArduCAM Library and Examples

ArduCAM_ESP8266_UNO Please use josn board manager script from http://www.arducam.com/downloads/ESP8266_UNO/package_ArduCAM_index.json to download ESP8

Lee 81 Jan 8, 2022
ESP8266 examples and toolchain setup

README These apps accomplish the following: -- ESP8266-EVB-blinkLED : Blink by green LED on ESP8266-EVB -- arduino_style : -- esphttpd : Advanced web-

OLIMEX LTD BULGARIA 278 Jun 27, 2022
Anto client library for ESP8266-Arduino

Anto client library for ESP8266-Arduino ESP8266-AntoIO provides common and easy way to connect your ESP8266 to Anto.io IoT platform service. Stable ve

Anto.io Internet of Things platform 10 Dec 1, 2021
Connecting an ESP8266 to a Google Calendar service to turn an useless "On Air" sign into a smart meeting indicator

ESP8266-GoogleCalendar Intro I had an useless "On Air" sign hanging around my home, and I thought it would be cool to connect it to my Google Calendar

3D Fix It 2 Jan 20, 2022
Library for auto Light Emitting Diode (LED) control based on the timer function of the ESP8266

Library for auto Light Emitting Diode (LED) control based on the timer function of the ESP8266. The purpose is to have LED running for their own without the need to to such things like blinking on your own.

Dirk Ohme 1 Jan 18, 2022
ESP8266 MQTT Sharp-fu-y30 air purifier controller

ESP8266 MQTT Sharp-fu-y30 air purifier controller Disclaimer Note: if you decide to do those changes to your air purifier, you take all the responsibi

xis 2 May 14, 2022
ESP32-S2 and CC1101S 433Mhz usb stick to record and send car gates/garages data keys and open stuff

HackZeGarage ESP32-S2 and CC1101S 433Mhz usb stick to record and send car gates/garages data keys and open stuff **HackZeGarage @sulfuroid / Dr CADIC

Dr PhilMorph 5 Mar 16, 2022
Arduino web server library.

aWOT Arduino web server library. Documentation 1. Getting started Hello World Basic routing Application generator Serving static files 2. Guide Routin

Lasse Lukkari 234 Jun 12, 2022
Frontend web application to control an arduino

Arduino Smart Blinds Frontend web application to control an arduino This project aims at created a frontent web page that communicates with an arduino

Corentin Royer 1 Jan 23, 2022
IOTBOT, which is designed as an Internet-oriented robotic coding training kit and powered by the ESP32 processor

IOTBOT-Firmware! Test Series IOTBOT, which is designed as an Internet-oriented robotic coding training kit and powered by the ESP32 processor, knows n

null 1 Dec 29, 2021
Arduino polyphonic synthesizer project (not a Moog) for ESP32 - STM32 - Teensy and more

ml_synth_basic_example Arduino polyphonic synthesizer project (not a Moog) for ESP32 - STM32 - Teensy and more link to the video My intention was to m

Marcel 13 Jun 14, 2022
Arduino library for providing a convenient C++ interface for accessing UAVCAN.

107-Arduino-UAVCAN Arduino library for providing a convenient C++ interface for accessing UAVCAN (v1.0-beta) utilizing libcanard. This library works f

107-Systems 47 Jun 30, 2022
Unified interface for selecting hardware or software SPI implementations on Arduino platforms

AceSPI Unified interface for selecting hardware or software SPI implementations on Arduino platforms. The code was initially part of the AceSegment li

Brian Park 1 Oct 22, 2021