functions_menu: как на Arduino с библиотекой LiquidMenu прикреплять функции, срабатывающие по событиям в меню
Данный пример демонстрирует, как прикрепить функции к "строкам" в меню.
Прикрепление функций к различным "строкам" в меню делает возможным выполнение действий с помощью меню (таких как уменьшение яркости светодиода, изменение настроек...). Чтобы прикрепить функцию к строке вам необходимо создать ее с сигнатурой "void functionName(void)
". После создания функция готова быть прикрепленной к строке. Чтобы выполнить это вызовите bool LiquidLine::attach_function(uint8_t number, void (*function)(void))
. LiquidLine
– это объект строки, к которому вы хотите прикрепить функцию, number
– это "id" функции в этой строке, void (*function)(void)
– это просто имя функции без "()
". "id" функции делает возможным прикрепление нескольких функций к одной строке. Пример использования нескольких функций на одной строке – это увеличение или уменьшение некоторого значения (например, яркости светодиода). Удобно прикрепить все различные инкрементирующие функции (например, increase_brightness()
, increase_volume()
) к различным строкам (например, LED brightness: 5, Sound volume: 10), используя одинаковый id (например, 1). То же самое относится к декрементирующим функциям (например, строка LED brightness: 5 с прикрепленной функцией decrease_brightness()
с id 2, строка Sound volume: 10 с прикрепленной функцией decrease_volume()
с id 2). Аналогичные функции должны быть прикреплены к соответствующим строкам с одинаковыми "id", поскольку тогда они могут быть вызваны из одного и того же события (например, нажатие кнопки "UP" вызывает инкрементирующую функцию, на какой бы строке ни находился фокус). В данном примере показана строка, отображающая текущее значение ШИМ светодиода и строки, которые позволяют погасить светодиод или включить/выключить цикл мигания.
Схема
Соединения:
- вывод RS LCD к выводу 12 Arduino;
- вывод E LCD к выводу 11 Arduino;
- вывод D4 LCD к выводу 5 Arduino;
- вывод D5 LCD к выводу 4 Arduino;
- вывод D6 LCD к выводу 3 Arduino;
- вывод D7 LCD к выводу 2 Arduino;
- вывод R/W LCD на корпус;
- вывод VSS LCD на корпус;
- вывод VCC LCD к +5В;
- потенциометр 10 кОм: концы к корпусу и +5В, средний вывод к выводу V0 LCD;
- резистор 150 Ом между +5В и анодом LCD подсветки;
- катод LCD подсветки к корпусу;
- ----
- кнопка (left, влево) к выводу A0 Arduino и к корпусу;
- кнопка (right, вправо) к выводу 7 Arduino и к корпусу;
- кнопка (up, вверх) к выводу 8 Arduino и к корпусу;
- кнопка (down, вниз) к выводу 9 Arduino и к корпусу;
- кнопка (enter, ввод) к выводу 10 Arduino и к корпусу;
- устройство, управляемое ШИМ, (светодиод...) к выводу 6 Arduino.
Код
Код класса Button
(файл Button.h).
#pragma once
class Button
{
public:
Button (uint8_t pin, bool pullup = false, uint16_t debounceDelay = 50)
: _pin(pin), _state(LOW), _lastState(LOW),
_lastMillis(0), _debounceDelay(debounceDelay),
_lastDebounceTime(0)
{
if (pullup == true)
{
pinMode(_pin, INPUT_PULLUP);
}
else
{
pinMode(_pin, INPUT);
}
}
// Устраняет дребезг кнопки и возвращает состояние, если оно только что изменилось.
bool check(bool triggerState = LOW)
{
bool reading = digitalRead(_pin);
// Проверить, изменилось ли состояние кнопки
if (reading != _lastState)
{
_lastDebounceTime = millis();
}
// Проверить, изменилось ли состояние кнопки за '_debounceDelay' миллисекунд.
if ((millis() - _lastDebounceTime) > _debounceDelay)
{
//Проверить, изменилось ли состояние кнопки
if (reading != _state)
{
_state = reading;
return _state;
}
}
_lastState = reading;
// Если этот код был достигнут, он возвращает нормальное состояние кнопки.
if (triggerState == HIGH)
{
return LOW;
}
else
{
return HIGH;
}
}
private:
const uint8_t _pin;
bool _state;
bool _lastState;
uint32_t _lastMillis;
uint16_t _debounceDelay;
uint32_t _lastDebounceTime;
};
Код скетча functions_menu.ino
#include <LiquidCrystal.h>
#include <LiquidMenu.h>
#include "Button.h"
// Выводы, подключенные к дисплею:
const byte LCD_RS = 12;
const byte LCD_E = 11;
const byte LCD_D4 = 5;
const byte LCD_D5 = 4;
const byte LCD_D6 = 3;
const byte LCD_D7 = 2;
//LCD R/W вывод на корпус
//Средний вывод потенциометра 10кОм к VO
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
// Эта переменная может быть передана объекту LiquidMenu для назначения стартового экрана.
const byte startingScreen = 2;
// Инициализация объектов Button
/*
* Класс Button не является частью библиотеки LiquidMenu. Первый
* параметр - это вывод кнопки, второй - включает или выключает внутренний
* подтягивающий резистор (необязательный), и третий - время устранения
* дребезга контактов (необязательный).
*/
const bool pullup = true;
Button left(A0, pullup);
Button right(7, pullup);
Button up(8, pullup);
Button down(9, pullup);
Button enter(10, pullup);
/*
* Перечисление может использоваться для функций обратного вызова. Оно устанавливает
* номер для похожих функций. Позже, когда функция прикрепляется, она может быть передана
* со значением enum, а не с волшебным числом.
*/
// В данном примере два типа функций, один увеличивает какое-то значение,
// а второй уменьшает его.
enum FunctionTypes
{
increase = 1,
decrease = 2,
};
// Это определения выводов и переменных для их значений PWM.
const byte led = 6;
byte led_level = 0;
// Переменные, используемые для установки "настроек".
bool isFading = false;
char* isFading_text;
unsigned int fadePeriod = 100;
bool isBlinking = false;
char* isBlinking_text;
unsigned int blinkPeriod = 1000;
LiquidLine welcome_line1(1, 0, "LiquidMenu ", LIQUIDMENU_VERSION);
LiquidLine welcome_line2(1, 1, "Functions ex.");
LiquidScreen welcome_screen(welcome_line1, welcome_line2);
LiquidLine ledTitleLine(6, 0, "LED");
LiquidLine led_line(4, 1, "Level: ", led_level);
LiquidScreen led_screen(ledTitleLine, led_line);
LiquidLine fade_line(0, 0, "Fade - ", isFading_text);
LiquidLine fadePeriod_line(0, 1, "Period: ", fadePeriod, "ms");
LiquidScreen fade_screen(fade_line, fadePeriod_line);
LiquidLine blink_line(0, 0, "Blink - ", isBlinking_text);
LiquidLine blinkPeriod_line(0, 1, "Period: ", blinkPeriod, "ms");
LiquidScreen blink_screen(blink_line, blinkPeriod_line);
LiquidMenu menu(lcd, startingScreen);
// Функции обратного вызова
void increase_led_level()
{
if (led_level < 225)
{
led_level += 25;
}
else
{
led_level = 0;
}
analogWrite(led, led_level);
}
void decrease_led_level()
{
if (led_level > 25)
{
led_level -= 25;
}
else
{
led_level = 250;
}
analogWrite(led, led_level);
}
void fade_switch()
{
led_off();
if (isFading == true)
{
isFading = false;
isFading_text = (char*)"OFF";
}
else
{
isFading = true;
isFading_text = (char*)"ON";
isBlinking = false;
isBlinking_text = (char*)"OFF";
}
}
void increase_fadePeriod()
{
if (fadePeriod < 3000)
{
fadePeriod += 10;
}
}
void decrease_fadePeriod()
{
if (fadePeriod > 10)
{
fadePeriod -= 10;
}
}
void blink_switch()
{
led_off();
if (isBlinking == true)
{
isBlinking = false;
isBlinking_text = (char*)"OFF";
}
else
{
isBlinking = true;
isBlinking_text = (char*)"ON";
isFading = false;
isFading_text = (char*)"OFF";
}
}
void increase_blinkPeriod()
{
if (blinkPeriod < 3000)
{
blinkPeriod += 50;
}
}
void decrease_blinkPeriod()
{
if (blinkPeriod > 50)
{
blinkPeriod -= 50;
}
}
void led_off()
{
led_level = 0;
analogWrite(led, led_level);
}
// Проверить все кнопки
void buttonsCheck()
{
if (right.check() == LOW)
{
menu.next_screen();
}
if (left.check() == LOW)
{
menu.previous_screen();
}
if (up.check() == LOW)
{
// Вызывает функцию, идентифицированную
// значением increase или 1 для выделенной строки.
menu.call_function(increase);
}
if (down.check() == LOW)
{
menu.call_function(decrease);
}
if (enter.check() == LOW)
{
// Переключает фокус на следующую строку.
menu.switch_focus();
}
}
// Функция погашения.
void fade()
{
static bool goingUp = true;
if (goingUp)
{
led_level += 25;
}
else
{
led_level -= 25;
}
if (led_level > 225)
{
goingUp = false;
led_level = 250;
}
if (led_level < 25 && goingUp == false)
{
goingUp = true;
led_level = 0;
}
analogWrite(led, led_level);
}
// Функция мигания.
void blink()
{
static bool blinkState = LOW;
if (blinkState == LOW)
{
blinkState = HIGH;
led_level = 255;
}
else
{
blinkState = LOW;
led_level = 0;
}
analogWrite(led, led_level);
}
void setup()
{
Serial.begin(250000);
pinMode(led, OUTPUT);
lcd.begin(16, 2);
// Увеличивающие функции прикреплены с идентификатором 1.
/*
* Эта функция может быть позже вызвана нажатием кнопки 'UP',
* когда фокус на строке 'led_line'. Если фокус находится на другой строке,
* то будет вызвана соответствующая функция той строки.
*/
led_line.attach_function(increase, increase_led_level);
// Уменьшающие функции прикреплены с идентификатором 2.
led_line.attach_function(decrease, decrease_led_level);
// Здесь одна и та же функция прикрепляется с двумя разными идентификаторами.
// Она будет вызвана при нажатиях кнопок 'UP' или 'DOWN'.
fade_line.attach_function(1, fade_switch);
fade_line.attach_function(2, fade_switch);
fadePeriod_line.attach_function(increase, increase_fadePeriod);
fadePeriod_line.attach_function(decrease, decrease_fadePeriod);
blink_line.attach_function(1, blink_switch);
blink_line.attach_function(2, blink_switch);
blinkPeriod_line.attach_function(increase, increase_blinkPeriod);
blinkPeriod_line.attach_function(decrease, decrease_blinkPeriod);
menu.add_screen(welcome_screen);
menu.add_screen(led_screen);
menu.add_screen(fade_screen);
menu.add_screen(blink_screen);
isFading_text = (char*)"OFF";
isBlinking_text = (char*)"OFF";
menu.update();
}
void loop()
{
buttonsCheck();
static unsigned long lastMillis_blink = 0;
if ((isFading == true) && ((millis() - lastMillis_blink) > fadePeriod))
{
lastMillis_blink = millis();
fade();
menu.update();
}
static unsigned long lastMillis_fade = 0;
if ((isBlinking == true) && ((millis() - lastMillis_fade) > blinkPeriod))
{
lastMillis_fade = millis();
blink();
menu.update();
}
}
Для более подробной информации смотрите документацию на библиотеку LiquidMenu.