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.


На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


Сообщить об ошибке