functions_menu: как на Arduino с библиотекой LiquidMenu прикреплять функции, срабатывающие по событиям в меню

Добавлено7 мая 2018 в 00:41

Данный пример демонстрирует, как прикрепить функции к "строкам" в меню.

Прикрепление функций к различным "строкам" в меню делает возможным выполнение действий с помощью меню (таких как уменьшение яркости светодиода, изменение настроек...). Чтобы прикрепить функцию к строке вам необходимо создать ее с сигнатурой "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" вызывает инкрементирующую функцию, на какой бы строке ни находился фокус). В данном примере показана строка, отображающая текущее значение ШИМ светодиода и строки, которые позволяют погасить светодиод или включить/выключить цикл мигания.

Схема

Схема для примера functions_menu
Схема для примера functions_menu

Соединения:

  • вывод 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.

Теги

ArduinoLCD дисплейМенюПрограммирование