system_menu: как на Arduino с библиотекой LiquidMenu построить систему меню
Данный пример демонстрирует, как построить систему меню.
Система меню – это коллекция меню. Чтобы построить систему меню, вам необходимо поместить меню в объект класса LiquidSystem
. Методы, используемые с LiquidMenu
, должны теперь вызываться напрямую от объекта LiquidSystem
. Для переключения между меню используйте метод bool LiquidSystem::change_menu(LiquidMenu &p_liquidMenu)
. Данный метод может быть обернут внутрь функции обратного вызова и вызываться через меню. Данный пример также использует EEPROM для сохранения состояния светодиода и периода считывания.
Схема
Соединения:
- вывод 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;
};
Код скетча system_menu.ino
#include <LiquidCrystal.h>
#include <LiquidMenu.h>
#include <EEPROM.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
const bool pullup = true;
Button left(A0, pullup);
Button right(7, pullup);
Button up(8, pullup);
Button down(9, pullup);
Button enter(10, pullup);
// Определение выводов и переменных для их состояний.
const byte pin6 = 6;
byte pin6_level = 0;
const byte pinA4 = A4;
byte pinA4_value = 0;
const byte pinA5 = A5;
byte pinA5_value = 0;
// Период считывания аналоговых отсчетов в секундах.
// Позже это значение будет перезаписано значением из EEPROM.
unsigned short sample_period = 2;
// Текст, используемый для индикации сохранения строк.
char* input_saved;
char* output_saved;
enum FunctionTypes
{
increase = 1,
decrease = 2,
};
// Объект LiquidLine может использоваться более одного раза.
LiquidLine back_line(11, 1, "/BACK");
LiquidLine welcome_line1(1, 0, "LiquidMenu ", LIQUIDMENU_VERSION);
LiquidLine welcome_line2(1, 1, "System example");
LiquidScreen welcome_screen(welcome_line1, welcome_line2);
// Эти строки направляют к другим меню.
LiquidLine outputs_line(0, 0, "/Outputs");
LiquidLine inputs_line(0, 1, "/Inputs");
LiquidScreen io_screen(outputs_line, inputs_line);
// Это первое меню.
LiquidMenu main_menu(lcd, welcome_screen, io_screen, 1);
LiquidLine pin6_line(0, 0, "Pin 6: ", pin6_level);
LiquidScreen pin6_screen(pin6_line);
LiquidLine oSave_line(0, 0, "Save", output_saved);
LiquidScreen oSecondary_screen(oSave_line, back_line);
// Это второе меню.
LiquidMenu outputs_menu(lcd, pin6_screen, oSecondary_screen);
LiquidLine pinA4_line(0, 0, "Pin A4: ", pinA4_value);
LiquidLine pinA5_line(0, 1, "Pin A5: ", pinA5_value);
LiquidScreen pinsA4_A5_screen(pinA4_line, pinA5_line);
LiquidLine iSample_line(0, 0, "Sample: ", sample_period, "s");
LiquidLine iSave_line(0, 1, "Save", input_saved);
LiquidScreen iSecondary_screen(iSample_line, iSave_line, back_line);
// И это последнее третье меню.
LiquidMenu inputs_menu(lcd, pinsA4_A5_screen, iSecondary_screen);
/*
* Объект LiquidSystem объединяет объекты LiquidMenu из системы меню.
* Он обеспечивает те же функции, что и LiquidMenu, плюс добавляет
* add_menu() и change_menu().
*/
LiquidSystem menu_system(main_menu, outputs_menu, inputs_menu);
// Проверить все кнопки.
void buttonsCheck() {
if (right.check() == LOW)
{
menu_system.next_screen();
}
if (left.check() == LOW)
{
menu_system.previous_screen();
}
if (up.check() == LOW)
{
menu_system.call_function(increase);
}
if (down.check() == LOW)
{
menu_system.call_function(decrease);
}
if (enter.check() == LOW)
{
menu_system.switch_focus();
}
}
// Функция обратного вызова будет прикреплена к back_line.
void go_back()
{
// Данная функция ссылается на необходимое меню.
menu_system.change_menu(main_menu);
}
void goto_outputs_menu()
{
menu_system.change_menu(outputs_menu);
}
void goto_inputs_menu()
{
menu_system.change_menu(inputs_menu);
}
void increase_pin6()
{
if (pin6_level < 225)
{
pin6_level += 25;
}
else
{
pin6_level = 250;
}
analogWrite(pin6, pin6_level);
output_saved = (char*)" ";
}
void decrease_pin6()
{
if (pin6_level > 25)
{
pin6_level -= 25;
}
else
{
pin6_level = 0;
}
analogWrite(pin6, pin6_level);
output_saved = (char*)" ";
}
void save_input()
{
EEPROM.put(11, sample_period);
input_saved = (char*)" *";
}
void save_output()
{
EEPROM.put(9, pin6_level);
output_saved = (char*)" *";
}
void increase_samplePeriod()
{
if (sample_period < 10)
{
sample_period++;
input_saved = (char*)" ";
}
}
void decrease_samplePeriod()
{
if (sample_period > 0)
{
sample_period--;
input_saved = (char*)" ";
}
}
void setup()
{
Serial.begin(250000);
pinMode(pin6, OUTPUT);
// Прочитать значения, записанные в EEPROM
EEPROM.get(9, pin6_level);
EEPROM.get(11, sample_period);
analogWrite(pin6, pin6_level);
lcd.begin(16, 2);
back_line.set_focusPosition(Position::LEFT);
back_line.attach_function(1, go_back);
back_line.attach_function(2, go_back);
outputs_line.attach_function(1, goto_outputs_menu);
outputs_line.attach_function(2, goto_outputs_menu);
inputs_line.attach_function(1, goto_inputs_menu);
inputs_line.attach_function(2, goto_inputs_menu);
pin6_line.attach_function(increase, increase_pin6);
pin6_line.attach_function(decrease, decrease_pin6);
oSave_line.attach_function(1, save_output);
oSave_line.attach_function(2, save_output);
iSave_line.attach_function(1, save_input);
iSave_line.attach_function(2, save_input);
iSample_line.attach_function(increase, increase_samplePeriod);
iSample_line.attach_function(decrease, decrease_samplePeriod);
input_saved = (char*)" *";
output_saved = (char*)" *";
menu_system.update();
}
void loop()
{
buttonsCheck();
static unsigned long lastMillis_sample = 0;
if (millis() - lastMillis_sample > (sample_period * 1000))
{
lastMillis_sample = millis();
pinA4_value = analogRead(pinA4);
pinA5_value = analogRead(pinA5);
menu_system.update();
}
}
Для более подробной информации смотрите документацию на библиотеку LiquidMenu.