Добрый день, уважаемый читатель!
Казалось бы – зачем нужна какая-то библиотека для мигания светодиодом на ESP? Мигание светодиодом сложности особой не представляет. Делов-то – “дергай” соответствующий вывод микроконтроллера, вот светодиод и мигает. Мало того – все “демки” для микроконтроллеров с этого начинаются, своеобразный “Hello, world” для ардуинщиков.
Однако всё меняется, когда хочется “выжать” из единственного светодиода максимум полезной информации. Видели когда-нибудь как работает светодиод на автосигнализации? Когда все “спокойно” – мигнет кратенько один раз в несколько секунд и всё. Если была тревога – по количеству вспышек уже при подходе к автомобилю можно определить, что случилось. Этот же метод обозначения событий и неисправностей используется и на множестве других устройств – смарткассах, принтерах и т.д.
Да еще при всем при программировании желательно не особо отвлекаться от основной задачи. Конечно, описанный выше алгоритм можно реализовать на циклах и задержках, но это муторно и долго. И нужно постоянно помнить о светодиодах и текущих режимах мигания. Вот бы сделать так, что бы “отправил светодиоду команду, как мигать – и забыл“.
Описанная в данной статье библиотека как раз и решает обе задачи.
Данная версия библиотеки может быть использована на любом микроконтроллере, в том числе и на ESP32. Для ESP32 есть более оптимальный вариант, реализованный в виде задачи FreeRTOS. Сия библиотека написана и отлажена на PlatformIO, но на Arduino IDE так же будет работать, пример вложен в архив с исходниками. Ссылки на скачивание в конце статьи
Возможности и описание работы:
- Управление светодиодами по принципу “вызвал команду и забыл”. Для управления каждым отдельным светодиодом создается свой экземпляр класса rLed. Но не забудьте добавить вызов ledLoop() в функцию loop() Вашего скетча, иначе чуда не произойдет.
- Возможные режимы работы:
- ledOff() – светодиод выключен постоянно
- ledOn() – светодиод включен постоянно
- flashSet(quantity, duration, interval) – мигнуть заданное количество раз с заданными длительностью и периодом, а затем выключить
- blinkSet (quantity, duration, period) – мигать постоянно с заданными длительностью и периодом. Если указано количество quantity > 1, то светодиод будет мигать сериями вспышек. Для равномерного мигания (меандром) необходимо указать длительность равной периоду и quantity = 1
- blinkOff() – отключить мигание
- Имеется возможность принудительно подавить вспышки светодиода на какое-то время. Например, можно гасить светодиоды на устройствах в ночное время, дабы не мешать сну.
- Возможно включение светодиода как с помощью установки высокого уровня, так и низкого (в зависимости от схемы подключения светодиода). Кроме того, поддерживается работа не только с GPIO микроконтроллера, но и управление светодиодами через I2C платы расширения типа PCF8574 с помощью пользовательской функции обратного вызова.
- “Системный” светодиод для отображения состояния устройства. Системный светодиод удобен тем, что “о нем заранее известно” другим библиотекам, и можно отправлять команды из библиотеки подключения к WiFi, MQTT, Telegram и т.д. Управление светодиодом можно как по аналогии с “обычными” светодиодами, так и с помощью установки специальных статусов (заранее предустановленные режимы мигания):
- SYSLED_ERROR – “общая” ошибка
- SYSLED_WARNING – предупреждение
- SYSLED_WIFI_CONNECTED – подключение к WiFi точке доступа установлено
- SYSLED_WIFI_INET_AVAILABLE – интернет доступен (можно определить с помощью ping-а)
- SYSLED_WIFI_ERROR – ошибка подключения к WiFi (точка доступа не доступна или авторизация не проходит)
- SYSLED_MQTT_ERROR – не удается подключиться к MQTT-брокеру или отправить данные
- SYSLED_TELEGRAM_ERROR – не удается отправить данные в telegram-канал
- SYSLED_OTHER_PUB_ERROR – не удается отправить данные на другие ресурсы (ThingSpeak и т.д.)
Режим мигания является “запоминаемым”. Достаточно отправить светодиоду команду blinkSet, и этот режим мигания будет автоматически восстанавливаться после любых ledOn+ledOff и (или) flashSetавтоматически. Это позволяет не задумываться о том, какой режим мигания был установлен, если возникает необходимость срочно помигать светодиодиком для индикации каких либо событий. Чтобы отключить мигание совсем – необходимо отправить команду blinkOff. Например, у меня “нормальный” режим работы обозначается редкими одиночными вспышками один раз в 3 секунды. В случае какого-либо сбоя в работе (нет wifi, например) – режим мигания меняется. Если необходимо “обозначить” отправку данных на mqtt-брокер я просто отправляю ledOn в начале передачи пакета, и ledOff в конце. Можно также использовать команду на серию вспышек flashSet для большей заметности. Когда задача получит команду ledOff (либо серия будет завершена), она автоматически восстановит последний режим мигания, если он был активирован.
Пример:
ledOn(); // светодиод включен ledOff(); // светодиод выключен flashSet(3, 100, 500); // светодиод мигнет три раза с длительностью 100 мс с паузой между вспышками по 500 мс, после чего погаснет blinkSet(2, 100, 5000); // светодиод начинает мигать сериями из 2 вспышек с длительностью 100 мс и паузой между сериями 5000 мс ledOn(); // светодиод включен, мигание временно приостановлено ledOff(); // возвращаемся к последнему режим мигания (сериями из 2-х миганий по 100 мс через 5 секунд) flashSet(30, 100, 500); // светодиод мигнет 30 раз с длительностью 100 мс с паузой между вспышками 500 мс // после чего снова автоматически возвращаемся в последний режим мигания (сериями из 2-х миганий по 100 мс через 5 секунд) ledOff(); // ничего не изменится ;-) blinkSet(1, 500, 500); // изменение режима мигания - непрерывное равномерное мигание 0,5 с горит / 0,5 с не горит blinkOff(); // отключить режим мигания, светодиод выключен
Разумеется, между командами должны быть какие-нибудь промежутки времени, чтобы светодиод успел отработать команды. Без этих пауз светодиод сразу выполнит их все и выключится (последняя команда).
Видео, демонстрирующее работу библиотеки:
Видео было записано на версии для ESP32, но на других контроллерах все выглядит точно также.
- Красный светодиод на видео – “системный”. Он отображает “общее” состояние устройства. При включении имитирует подключение к WiFi (равномерное мигание). После соединения с WiFi (через 10 секунд) переходит в режим “проверка интернета / нет интернета” (две вспышки – пауза, две вспышки – пауза и т.д.). Еще через 5 секунд переходит в нормальный режим (редкие вспышки). Еще через некоторое время светодиод мигнет однократной серией для отображения передачи данных в эфир и вернется в “нормальный” режим.
- Синий светодиод просто мигает с частотой 1 Гц (0,5 секунды вспышка + 0,5 секунды пауза)
- Желтый светодиод тоже мигает постоянно, но уже длительность паузы в 4 раза больше длительности вспышки.
- Зеленый светодиод мигает сериями по 3 коротких (100 мс) вспышки через каждые 2 секунды.
Пример работы на реальном проекте: WiFi-сигнализации
- Синий светодиод – системный. Редкие вспышки – признак нормальной работы устройства. Короткие серии 3*75мс – передача данных на MQTT брокер, их видно при переключении режимов.
- Красный светодиод – отображает прием управляющих команд с приемника 433 MHz
- Извещатель (сирена + мигалка) подключен через ключи на биполярных транзисторах, но суть та же. Отображает тревоги (равномерное мигание) и переключение режимов (серии с разным количеством вспышек).
Демонстрационный проект
Подходит как для PlatfromIO, так и для Arduino IDE (скачать архив вместе с вложенной библиотекой можно в конце статьи)
#include "rLed.h" #include "rLedSys.h" rLed ledBlue(D3, true); int step = 0; void setup() { Serial.begin(74880); Serial.println("ledSystem: Connect to WiFi..."); ledSysInit(D4, false, false, NULL); ledSysBlinkAuto(); Serial.println("ledBlue: Init LED & turn on"); ledBlue.initGPIO(); ledBlue.ledOn(); } void loop() { ledSysLoop(); ledBlue.ledLoop(); if (step == 0 && millis() > 5000) { step = 1; Serial.println("ledSystem: Connected to WiFi, retrive SNTP time..."); ledSysStateSet(SYSLED_WIFI_CONNECTED); Serial.println("ledBlue: Set blink mode"); ledBlue.blinkSet(3, 100, 1000); }; if (step == 1 && millis() > 25000) { step = 2; Serial.println("ledSystem: Connected to WiFi, all OK..."); ledSysStateSet(SYSLED_WIFI_INET_AVAILABLE); Serial.println("ledBlue: Set flash mode"); ledBlue.flashSet(10, 100, 300); }; if (step == 2 && millis() > 60000) { step = 3; Serial.println("ledSystem: set error..."); ledSysStateSet(SYSLED_ERROR); Serial.println("ledBlue: Disable LED"); ledBlue.ledEnabled(false); }; if (step == 3 && millis() > 90000) { Serial.println("ledSystem: clear error..."); ledSysStateClear(SYSLED_ERROR); Serial.println("ledBlue: Enable LED"); step = 4; ledBlue.ledEnabled(true); }; }
Описание функций
Для создания экземпляра класса rLed используйте конструктор:
rLed(const int8_t ledGPIO, const bool ledHigh);
где: ledGPIO – номер вывода, к которому подключен светодиод. ledHigh – true = включение с помощью установки на GPIO высокого уровня, false = с помощью установки на GPIO низкого уровня.
Если необходимо использовать функцию обратного вызова, укажите ее дополнительно через setCustomControl(const ledCustomControl_t customControl).
Для инициализации GPIO на вывод используйте функцию initGPIO(). Но можно этого не делать, она будет вызвана автоматически при первом обращении к светодиоду. При использовании customControl инициализация порта не производится, Вы должны сделать это самостоятельно.
После этого можно использовать остальные публичные методы для изменения режимов:
void ledEnabled(const bool newEnabled); void ledOn(); void ledOff(); void blinkOn(const ledMode_t newMode, const uint16_t blinkQuantity, const uint16_t blinkDuration, const uint16_t blinkInterval); void blinkSet(const uint16_t blinkQuantity, const uint16_t blinkDuration, const uint16_t blinkInterval); void flashSet(const uint16_t flashQuantity, const uint16_t flashDuration, const uint16_t flashInterval); void blinkOff();
Самое главное – не забудьте добавить ledLoop() в Вашу главную функцию loop()!
Изменение режимов мигания для системного светодиода может быть осуществлено с помощью макросов предпроцессора, определенных в файле project_config.h. Файл project_config.h может отсутствовать, в этом случае будут использованы значения по умолчанию. Необходимо, чтобы этот файл при компиляции был доступен не только из главного скетча, но и из библиотек.
В PlatformIO можно указать компилятору, где искать файл project_config.h, с помощью опции build_flags = -Iкаталог в platformio.ini. Например, я поместил project_config.h в подкаталог src (там же, где и основной файл проекта). В этом случае я добавлю в platformio.ini следующие строки:
[env] build_flags = -Isrc
В Arduino IDE я пока не нашел другого способа, кроме как скопировать исходники библиотек и project_config.h в тот же каталог, где и располагается сам скетч. Это не очень удобно, но Arduino IDE и сама по себе не самый удобный инструмент.
Скачать библиотеку можно с помощью кнопки:
В архиве Вы также найдете примеры скетча для Arduino IDE и PlatformIO – загляните в папку examples
Библиотека асинхронного управления светодиодами + демонстрационные проекты для PlatformIO и ArduinoIDE.
Версия для ESP32:
То же самое, но реализовано в виде задачи FreeRTOS. Читать здесь.
Библиотека асинхронного управления светодиодами для ESP32 + демонстрационные проекты для PlatformIO и ArduinoIDE.
Пожалуйста, оцените статью:
Как правильно ставить библиотеку в Arduino IDE для платформы Arduino NANO?
При перемещении в папку liraries компилятор не находит заголовочный файл. Перетащил заголовочные файлы из папки include в основную – компилятор их нашел но выдал гору ошибок по недействительным ссылкам
Добрый день.
Для Arduino IDE два варианта:
– либо сложить все файлы в одну кучу к к основному файлу (как это и сделано в примере для Arduino IDE)
– либо создать в каталоге с Вашими библиотеками новый, но заголовочный и cpp файл должны быть вместе.
Посмотрите по аналогии с другими библиотеками, которые Вы ставили через Ardiuino IDE.
Точнее подсказать не могу, давно уже в Arduino не захаживал
Добрый день, выдает при компиляции ошибки:
Все файлы библиотеки лежат в папке, где и основной файл.
Почему так может быть?
Compiling .pio\build\megaatmega2560\src\main.cpp.o
Compiling .pio\build\megaatmega2560\src\rLed.cpp.o
Compiling .pio\build\megaatmega2560\src\rLedSys.cpp.o
Compiling .pio\build\megaatmega2560\lib7cd\Wire\Wire.cpp.o
In file included from src\rLedSys.cpp:1:0:
src\rLedSys.h:15:47: error: ‘BIT0’ was not declared in this scope
static const int SYSLED_ERROR = BIT0;
^~~~
src\rLedSys.h:15:47: note: suggested alternative: ‘INT0’
static const int SYSLED_ERROR = BIT0;
^~~~
INT0
src\rLedSys.h:16:47: error: ‘BIT1’ was not declared in this scope
static const int SYSLED_WARNING = BIT1;
^~~~
src\rLedSys.h:16:47: note: suggested alternative: ‘INT1’
static const int SYSLED_WARNING = BIT1;
^~~~
INT1
src\rLedSys.h:17:47: error: ‘BIT2’ was not declared in this scope
static const int SYSLED_WIFI_CONNECTED = BIT2;
^~~~
src\rLedSys.h:17:47: note: suggested alternative: ‘INT2’
static const int SYSLED_WIFI_CONNECTED = BIT2;
^~~~
INT2
src\rLedSys.h:18:47: error: ‘BIT3’ was not declared in this scope
static const int SYSLED_WIFI_INET_AVAILABLE = BIT3;
^~~~
src\rLedSys.h:18:47: note: suggested alternative: ‘INT3’
static const int SYSLED_WIFI_INET_AVAILABLE = BIT3;
^~~~
INT3
src\rLedSys.h:19:47: error: ‘BIT4’ was not declared in this scope
static const int SYSLED_WIFI_ERROR = BIT4;
^~~~
src\rLedSys.h:19:47: note: suggested alternative: ‘INT4’
static const int SYSLED_WIFI_ERROR = BIT4;
^~~~
INT4
src\rLedSys.h:20:47: error: ‘BIT5’ was not declared in this scope
static const int SYSLED_MQTT_ERROR = BIT5;
^~~~
src\rLedSys.h:20:47: note: suggested alternative: ‘INT5’
static const int SYSLED_MQTT_ERROR = BIT5;
^~~~
INT5
src\rLedSys.h:21:47: error: ‘BIT6’ was not declared in this scope
static const int SYSLED_TELEGRAM_ERROR = BIT6;
^~~~
src\rLedSys.h:21:47: note: suggested alternative: ‘INT6’
static const int SYSLED_TELEGRAM_ERROR = BIT6;
^~~~
INT6
src\rLedSys.h:22:47: error: ‘BIT7’ was not declared in this scope
static const int SYSLED_OTHER_PUB_ERROR = BIT7;
^~~~
src\rLedSys.h:22:47: note: suggested alternative: ‘INT7’
static const int SYSLED_OTHER_PUB_ERROR = BIT7;
^~~~
INT7
*** [.pio\build\megaatmega2560\src\rLedSys.cpp.o] Error 1
Не определены константы BIT0 … BIT7.
Я использую ESP32, в ESP-IDF эти константы уже имеются. Вам же, видимо, придется сделать это самостоятельно.
В ESP-IDF это выглядит так:
….
#define BIT7 0x00000080
#define BIT6 0x00000040
#define BIT5 0x00000020
#define BIT4 0x00000010
#define BIT3 0x00000008
#define BIT2 0x00000004
#define BIT1 0x00000002
#define BIT0 0x00000001