Разработка программных продуктов

  • Разработка web-приложений
  • Онлайн сервисы и личные кабинеты
  • Автоматизация бизнес процессов
  • Внедрение и поддержка
  • Мониторинг и аналитика
Звоните в любое время!
+7 920 20 20 232

Разработка

скоро

Инфраструктура

скоро

Простое меню для Arduino.

Сделаем простенькое меню для Arduino и LCD Keyboard шилда. Которое будет в дальнейшем управлять параметрами настройки и сохранять их.

Как выглядит готовый результат:

Меню у меня просто плоское, без вложенных подменю, мне кажется так проще и понятнее. У каждого наименования параметра есть код, который поможет описать его в инструкциях или документации. Для начала у меня только параметры настройки даты и времени, которые будут сохраняться в модуль реального времени, при нажатии "save & exit". Код получился простой и я надеюсь понятный.

Итак, готовая прошивка для простого меню с комментариями:

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
char str_format[16]; //Временная переменная для sprintf

unsigned long Uptime; // Переменная хранит время работы в ms

// Это все параметры которые можем менять в меню
int Null = 0; // Этот параметр как заглушка, если менять нечего, костыль конечно ...
int DateDay = 1;
int DateMonth = 1;
int DateYear = 17;
int TimeHour = 0;
int TimeMin = 0;
int TimeSecond = 0;

// Переменные для кнопок
int const ButtonInterval = 200;	// Интервал срабатывания кнопки при удержании
int	ButtonPress = 0;			// Код нажатой кнопки, или 0 если не нажата
unsigned long	ButtonPressTime = 0; // Время на устройстве в которое была нажата кнопка

// Тип структуры данных описывающих пункт меню
typedef struct _MenuItem {
    String title;			// Имя меню
    void(*on_click)(int);	// Ссылка на функцию вызова обработки изменения значения, параметр +1 или -1)
    int &param;				// Ссылка на переменную значения
    char format[16];		// Формат вывода значения переменной
} MenuItem;

// Меню, первый пункт это не параметр, а просто выход с сохранением
// Название пункта меню / Функция обработки нажатия / Переменная значения / Формат вывода
MenuItem MenuItems[] = {
    { "SAVE & EXIT     ", MenuSaveRam,		Null,		"      Press [+]" },
    { "D01: Date Day   ", SettingDateDay,	DateDay,	"VAL: %02d" },
    { "D02: Date Mount ", SettingDateMonth,     DateMonth,	"VAL: %02d" },
    { "D03: Date Year  ", SettingDateYear,	DateYear,	"VAL: 20%02d" },
    { "D04: Time Hour  ", SettingTimeHour,	TimeHour,	"VAL: %02d" },
    { "D05: Time Min   ", SettingTimeMin,	TimeMin,	"VAL: %02d" }
};

// Это переменные для работы меню
int MenuCurrent = 0; // Выбранный пункт меню
bool MenuEnter = false; // Мы находимся в меню?
int MenuCount = 6; // Количество пунктов меню

// Загрузка
void setup() {
    lcd.begin(16, 2); // Инициализируем дисплей
}

// Главный цикл
void loop() {

    Uptime = millis(); // Сохраняем время работы каждый цикл

    if (!MenuEnter) { // Если мы не в меню то выводим всякую фигню =)
        lcd.setCursor(0, 0);
        lcd.print("DespVK rev.01.01"); // Версии Железа и Прошивки
        lcd.setCursor(0, 1);
        sprintf(str_format, "UT: %7lu s. ", Uptime / 1000); // Форматируем строку %lu это вывод unsigned long
        lcd.print(str_format); // Время работы в секундах
    };

    if (ButtonPress == 0) { // Если кнопки не были нажаты ранее
        int ButtonPinValue = analogRead(0); // Проверяем значение, не нажата ли кнопка
        if (ButtonPinValue < 60)		ButtonPress = 4; // Нажата [+]
        else if (ButtonPinValue < 200)	ButtonPress = 2; // Нажата [Prev]
        else if (ButtonPinValue < 400)	ButtonPress = 3; // Нажата [Next]
        else if (ButtonPinValue < 600)	ButtonPress = 5; // Нажата [-]
        else if (ButtonPinValue < 800)	ButtonPress = 1; // Нажата [Menu]
    } else { // Кнопка была нажата ранее
        if (ButtonPressTime == 0) { // Если не замеряли интервал нажатия кнопки
            ButtonPressTime = Uptime; // Засекаем когда была нажата кнопка
            ButtonClick(ButtonPress); // Вызывается функция обработки нажатия на кнопку
        }
        if (ButtonPressTime + ButtonInterval < Uptime) { // Если кнопка была нажата раньше чем ButtonInterval ms назад
            ButtonPressTime = 0; // Сбрасываем время
            ButtonPress = 0; // Отжимаем кнопку (это имитуреет многократное нажатие с интервалом ButtonInterval если кнопку держать)
        }
    }

}

// Обработка нажатия кнопки
void ButtonClick(int ButtonId) {

    if (MenuEnter) { // Если мы в меню
        if (ButtonId == 1) MenuEnter = false;	// Клик [Menu] Выход из меню
        if (ButtonId == 2) MenuCurrent--;		// Клик [Prev] Позицию ниже
        if (ButtonId == 3) MenuCurrent++;		// Клик [Next] Позиция выше
        MenuCurrent = constrain(MenuCurrent, 0, MenuCount - 1);	// Ограничиваем меню
        if (ButtonId == 4) MenuItems[MenuCurrent].on_click(1);	// Клик [+] Увеличиваем значение параметра
        if (ButtonId == 5) MenuItems[MenuCurrent].on_click(-1);	// Клик [-] Уменьшаем значение параметра
    } else {
        if (ButtonId == 1) MenuEnter = true; // Вход в меню
    }

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(MenuItems[MenuCurrent].title); // Выводим заголовок
    lcd.setCursor(0, 1);
    sprintf(str_format, MenuItems[MenuCurrent].format, MenuItems[MenuCurrent].param); // Форматируем вывод значения
    lcd.print(str_format); // Выводим значение
}

// Функции вызываемые для изменения значений переменных

void MenuSaveRam(int Concat) { // SAVE & EXIT
    if (Concat == 1) { //TODO Сохранение параметров в RAM };
	    MenuEnter = false; // Выход из меню
    }
}

void SettingDateDay(int Concat) {
    DateDay = constrain(DateDay + Concat, 1, 31); // Изменяем с ограничением
}

void SettingDateMonth(int Concat) {
    DateMonth = constrain(DateMonth + Concat, 1, 12);
}

void SettingDateYear(int Concat) {
    DateYear = constrain(DateYear + Concat, 0, 99);
}

void SettingTimeHour(int Concat) {
    TimeHour = constrain(TimeHour + Concat, 0, 23);
}

void SettingTimeMin(int Concat) {
    TimeMin = constrain(TimeMin + Concat, 0, 59);
}

Надеюсь, этот код кому-то сэкономит время =)