buttons_menu: как на Arduino с библиотекой LiquidMenu использовать кнопки, функции обратного вызова и переменные, меняющие текст
Данный пример демонстрирует, как использовать кнопки, функции обратного вызова и переменные, изменяющие текст.
В примере создается три экрана. Первый показывает статическую информацию. Второй показывает показание аналогового входа и состояние светодиода на выводе 13. И третий экран показывает значение ШИМ, поданное на вывод 3. Аналоговое значение периодически считывается и присваивается прикрепленной переменной, которая так же меняется. Вывод 3 выхода ШИМ управляется через функции обратного вызова, прикрепленные к их строке: "void pwm_up()
" and "void pwm_down()
". Чтобы вызвать функции перейдите к третьему экрану, используя кнопки "left" и "right", выделите строку, используя кнопку "enter", а затем нажмите кнопку "up" или "down".
Схема
Соединения:
- вывод 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;
- светодиод к выводу 13 Arduino (необязательно);
- какой-либо аналоговый сигнал к выводу A5 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;
};
Код скетча buttons_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);
// Инициализация объектов Button
/*
* Класс Button не является частью библиотеки LiquidMenu library. Первый
* параметр - это вывод кнопки, второй - включает или выключает внутренний
* подтягивающий резистор (необязательный), и третий - время устранения
* дребезга контактов (необязательный).
*/
const bool pullup = true;
Button left(A0, pullup);
Button right(7, pullup);
Button up(8, pullup);
Button down(9, pullup);
Button enter(10, pullup);
// Переменные, используемые для демонстрации управления ШИМ с помощью функций обратного вызова.
/*
* Переменная 'pwmLevel' позже передается объекту 'pwm_line', чтобы быть
* показанной на дисплее, и функция прикрепляется к тому же объекту LiquidLine.
* Функции прикрепляются к разным объектам LiquidLine с номером для
* идентификации, например, pwm_line.attach_function(2, func); тогда как
* menu.call_function(2) вызывается при каком-либо событии, например, нажатии кнопки,
* из-за чего вызывается функция, идентифицируемая с 2 для выделенной строки.
* Похожие функции (например, инкрементирование) могут быть прикреплены к разным
* объетам LiquidLine с одинаковыми номерами идентификации, а затем вызываться
* при некотором событии (например, нажатии кнопки 'UP') с помощью menu.call_function(2),
* что вызовет функцию только для выделенной строки, которая идентифицируется с
* номером 2.
*/
const byte pwmPin = 6;
byte pwmLevel = 0;
// Переменные для управления выводом и отображения состояния с помощью текста.
// char* используется для добавления изменяющегося текста в объект the LiquidLine.
const byte ledPin = LED_BUILTIN;
bool ledState = LOW;
char* ledState_text;
/*
* Переменная 'analogValue' позже конфигурируется для печати на дисплее.
* На этот раз для хранения предыдущего аналогового значения используется статическая переменная.
*/
const byte analogPin = A5;
unsigned short analogValue = 0;
LiquidLine welcome_line1(1, 0, "LiquidMenu ", LIQUIDMENU_VERSION);
LiquidLine welcome_line2(0, 1, "Buttons example");
LiquidScreen welcome_screen(welcome_line1, welcome_line2);
LiquidLine analog_line(0, 0, "Analog: ", analogValue);
LiquidLine ledState_line(0, 1, "LED is ", ledState_text);
LiquidScreen screen2(analog_line, ledState_line);
LiquidLine pwm_line(0, 0, "PWM level: ", pwmLevel);
LiquidScreen pwm_screen(pwm_line);
LiquidMenu menu(lcd);
// Функция для прикрепления к объекту pwm_line.
/*
* Данная функция используется для инкрементирования уровния ШИМ на
* 'pwmPin'. Она увеличивает значение 'pwmLevel', а затем
* записывает его в вывод.
*/
void pwm_up()
{
if (pwmLevel < 225)
{
pwmLevel += 25;
}
else
{
pwmLevel = 250;
}
analogWrite(pwmPin, pwmLevel);
}
// Функция для прикрепления к объекту pwm_line.
void pwm_down()
{
if (pwmLevel > 25)
{
pwmLevel -= 25;
}
else
{
pwmLevel = 0;
}
analogWrite(pwmPin, pwmLevel);
}
void setup()
{
Serial.begin(250000);
pinMode(analogPin, INPUT);
pinMode(ledPin, OUTPUT);
pinMode(pwmPin, OUTPUT);
lcd.begin(16, 2);
// Прикрепить функции к объектам LiquidLine.
pwm_line.attach_function(1, pwm_up);
pwm_line.attach_function(2, pwm_down);
menu.add_screen(welcome_screen);
menu.add_screen(screen2);
menu.add_screen(pwm_screen);
}
void loop() {
// Проверить все кнопки
if (right.check() == LOW)
{
Serial.println(F("RIGHT button clicked"));
menu.next_screen();
}
if (left.check() == LOW)
{
Serial.println(F("LEFT button clicked"));
menu.previous_screen();
}
if (up.check() == LOW)
{
Serial.println(F("UP button clicked"));
// Вызвать функцию, идентифицированную номером 1
// для выделенной строки.
menu.call_function(1);
}
if (down.check() == LOW)
{
Serial.println(F("DOWN button clicked"));
menu.call_function(2);
}
if (enter.check() == LOW)
{
Serial.println(F("ENTER button clicked"));
// Переключить фокус на следующую строку.
menu.switch_focus();
}
// Периодически переключать состояние светодиода.
static unsigned long lastMillis = 0;
static unsigned int period = 2000;
if (millis() - lastMillis > period)
{
lastMillis = millis();
Serial.print("LED turned ");
if (ledState == LOW)
{
ledState = HIGH;
// Изменить текст, который напечатан на дисплее.
ledState_text = (char*)"ON";
Serial.println(ledState_text);
menu.update();
}
else
{
ledState = LOW;
ledState_text = (char*)"OFF";
Serial.println(ledState_text);
menu.update();
}
digitalWrite(ledPin, ledState);
// Значение 'analogValue' обновляется каждую секунду.
analogValue = analogRead(analogPin);
static unsigned short lastAnalogValue = 0;
if (analogValue != lastAnalogValue)
{
lastAnalogValue = analogValue;
menu.update();
}
}
}
Для более подробной информации смотрите документацию на библиотеку LiquidMenu.