Как стать автором
Обновить

Простой графический интерфейс для M5Stack (Arduino)

Время на прочтение 10 мин
Количество просмотров 8.9K

Привет! Сегодня мы познакомимся с такой штукой как M5 UI. Благодаря M5 UI Вы можете с помощью пары строк кода подключать всевозможные поля, кнопки, ползунки и переключатели, создавать условные слоя. Несмотря на то, что процесс подключения элементов UI очень прост, Вы также можете воспользоваться наглядным инструментом M5 UI Designer for Arduino IDE.



Рисунок 1


Необходимо рассмотреть все существующие на сегодняшний день элементы из библиотеки M5 UI на практике, так же ознакомиться с процессом создания интерфейса в приложении M5 UI Designer.


Краткая справка


Графический интерфейс представляет собой совокупность функциональных элементов, необходимых для взаимодействия с пользователем. В качестве таких элементов выступают всевозможные поля ввода/вывода текста, кнопки, чекбоксы, ползунки, переключатели и многие другие. В качестве примера графического интерфейса давайте посмотрим на рисунок 2.



Рисунок 2. Элементы пользовательского интерфейса


Для того, чтобы установить фокус на следующий элемент (выбрать элемент) используйте поочерёдное нажатие клавиш Fn и TAB

Inputbox представляет собой область ввода текстовой информации на экране с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 32 px. В верхней части располагается надпись (например: Enter user name), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается прямоугольная область, в которую пользователь может вводить данные с клавиатуры. При наведении фокуса на данный элемент — происходит подсветка нижней части. Для того чтобы изменить значение используйте клавиши с буквами и цифрами на клавиатуре.


Textbox представляет собой область вывода текстовой информации на экране. Размеры могут быть заданы пользователем, но не могут быть меньше размеров одного символа. Состоит данный элемент из одной части. Текст умещается по всей площади и не выходит за пределы. Наведение фокуса на данный элемент не предусмотрено.


Waitingbar представляет собой область вывода графической информации на экране с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 12 px. В верхней части располагается надпись (например: Connection to Wi-Fi), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается прямоугольная область, которая закрашивается периодически оранжевыми и черными квадратами. Наведение фокуса на данный элемент не предусмотрено.


Progressbar представляет собой область вывода графической информации на экране с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 12 px. В верхней части располагается надпись (например: Times of the check), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается прямоугольная область, которая закрашивается в зависимости установленного значения (до 10% — красным, до 30% — оранжевым, до 80% — зелёным, до 100% — синим цветом). Наведение фокуса на данный элемент не предусмотрено.


Selectbox представляет собой область выбора текстовой информации на экране с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 44 px. В верхней части располагается надпись (например: Mode), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается прямоугольная область выбора, в которой пользователь может выбирать данные с клавиатуры. Для того чтобы изменить значение нажмите клавишу Fn, затем K / M или аналогично стрелки вверх / вниз.


Checkbox представляет собой область ввода единственного значения (true/false) на экране с фиксированной высотой 32 px. Ширина может быть задана пользователем, но не может быть меньше 44 px. В левой части расположен флаг (если закрашен, то true, если нет — false). В правой части располагается надпись (например: Remember password). Для того чтобы снять или установить флаг нажмите клавишу SPACE.


Button представляет собой область вызова любой пользовательской функции (с сигнатурой void (String*)) с фиксированной высотой 32 px. Ширина может быть задана пользователем, но не может быть меньше 22 px. По центру располагается надпись (например: Launch). Отличительной особенностью данного элемента является поддержка иконок из стандартного набора (рис. 3). Для того чтобы нажать на кнопку нажмите клавишу SPACE.



Рисунок 3. Коды стандартных иконок 24 x 24 px


Если будет подключена иконка, то минимальная ширина будет 51 px.


Rangebox представляет собой выбора целого числового значения из заданного диапазона с фиксированной высотой 50 px. Ширина может быть задана пользователем, но не может быть меньше 32 px. В верхней части располагается надпись (например: Speed), обратите внимание на то, что в конце автоматически будет добавлен символ ':'. В нижней части располагается область содержащая полосу и прилегающий ползунок. Для того чтобы изменить значение нажмите клавишу Fn, затем N / $ или аналогично стрелки влево / вправо.


Перечень компонентов для урока


  • PC/MAC;
  • M5STACK;
  • FACES;
  • FACES Keyboard;
  • кабель USB-C из стандартного набора;
  • цветные провода из стандартного набора (тип розетка-вилка);
  • макетная плата для пайки 5 х 7 см;
  • паяльник 40 или 60 Вт;
  • канифоль паяльная;
  • олово паяльное;
  • ножницы;
  • резистор 36 ом (1 шт.);
  • резистор 160 кОм (1 шт.);
  • микросхема 74HC595N (1 шт.);
  • резистор 220 Ом (1 шт.);
  • светодиоды: оранжевый, зеленый, жёлтый, красный (4 шт.);
  • транзистор мощный BC337 (1 шт.);
  • резистор 100 кОм (1 шт.);
  • электродвигатель постоянного тока (1 шт.).

Начнём!


Шаг 1. Установка библиотеки M5 UI


Перейдите по ссылке M5 UI for Arduino IDE в разделе Downloads (внизу этой страницы) и скачайте архив с библиотекой с GitHub (рис. 3.1).



Рисунок 3.1. Нажмите на кнопку Clone or download, затем Download ZIP


Запустите Arduino IDE и добавьте скаченный архив (рис. 3.2).



Рисунок 3.2. Нажмите Sketch, Include Library, Add .ZIP Library...


После этого библиотека будет успешно добавлена. На этом всё.


Шаг 2. Установка и инструмента M5 UI Designer


Аналогичным образом перейдите по ссылке M5 UI Designer for Arduino IDE в разделе Downloads (внизу этой страницы) и скачайте архив с библиотекой с GitHub в рабочий стол, затем извлеките содержимое и откройте документ index.html с помощью браузера (beta-версия работает исключительно под браузерами на движке Chrome) (рис. 3.4)



Рисунок 3.4


На этом установка инструмента завершена (рис. 3.5).



Рисунок 3.5. Знакомьтесь — M5 UI Designer


Шаг 3. Клавиша Enter — особенная (⊙_⊙)


Представьте себе такую ситуацию — пользователь ввёл необходимую информацию в тот же Inputbox и ему необходимо её обработать, например: отправить куда-нибудь. Как сообщить M5 что пользователь завершил ввод? Верно — нажатием на клавишу Enter (как один из вариантов).
В любом месте кода Вы всегда можете привязать пользовательскую функцию к клавише Enter, главное — чтобы сигнатура пользовательской функции была следующей void (String*).


void userFunction1(String* rootVar) {
    // reaction after pressing the Enter key
}

...
UIEnter = userFunction1;

Шаг 4. Азбука морзе, Inputbox и Textbox (^_^♪)


Давайте добавим первый наш элемент — Inputbox. В него мы будем вводить текст и после нажатия на клавишу Enter будем слышать из динамика код Морзе (рис. 4).



Рисунок 4. Код Морзе


Откройте M5 UI Designer, перетащите Inputbox и Textbox, задайте ширину и заголовки. В разделе Tools > User Functions нажмите на значок "жёлтая молния" и введите имя новой пользовательской функции Morse. Затем в разделе Properties > Enter key выберите Morse (рис. 4.1).



Рисунок 4.1.


Выделите весь текст из раздела Source и скопируйте в Arduino IDE.


/* User functions: */
void Morse(String* rootVar) {
 int MorseCodes[] =
 {
  0,1,-1,-1, // A
  1,0,0,0, // B
  1,0,1,0, // C
  1,0,0,-1, // D
  0,-1,-1,-1, // E
  0,0,1,0,  //F
  1,1,0,-1, // G
  0,0,0,0,  // H
  0,0,-1,-1,  // I
  0,1,1,1, // J
  1,0,1,-1, // K
  0,1,0,0, // L
  1,1,-1,-1, // M
  1,0,-1,-1,  // N
  1,1,1,-1, // O
  0,1,1,0,  // P
  1,1,0,1,  // Q
  0,1,0,-1, // R
  0,0,0,-1, // S
  1,-1,-1,-1, // T
  0,0,1,-1, // U
  0,0,0,1,  // V
  0,1,1,-1, // W
  1,0,0,1,  // X
  1,0,1,1, // Y
  1,1,0,0 // Z
 };
 for (int i = 0; i < UIInputbox_v05700a.length(); i++) {
  char chr = UIInputbox_v05700a[i];
  if (chr == ' ')
  {
    M5.Speaker.mute();
    delay(350);
  }
  else
  {
    int chrNum = (chr - 'a') * 4;
    for (int j = chrNum; j < (chrNum + 4); j++)
    {
      M5.Speaker.tone(440);
      if (MorseCodes[j] == 0)
        delay(50);
      else if (MorseCodes[j] == 1)
        delay(200);
      M5.Speaker.mute();
      delay(150);
    }
  }
 }
}

Как видите — весь каркас кода сгенерированный M5 UI Designer остался абсолютно в стандартном виде без изменений. Единственное, что мы изменили — пользовательская функция Morse. Всё очень просто :) (рис. 4.2).



Рисунок 4.2


Шаг 5. Bruteforce и Waitingbar ≧(◕ ‿‿ ◕)≦


Аналогичным образом добавим Waitingbar. Будем генерировать 8-битный случайный код, а затем его подбирать. Индикатором процесса будет как раз Waitingbar. После того, как код будет подобран Waitingbar будет скрыт. Запуск процесса будет происходить после нажатия на клавишу Enter (рис. 5).



Рисунок 5.


В этом примере мы несколько модифицируем функцию слоя default добавлением void UIDiable(bool, String*) после перечисления элементов. Это необходимо для того, чтобы Waitingbar был скрыт на время бездействия.


/* Function for layer default: */
void LayerFunction_default(String* rootVar) {
 /* UI Elements */
 ...
 UIDisable(true, &UIWaitingbar_yksk2w8);
}

void UIDisable(bool, String*) или void UISet(String*, int) используется для того, чтобы скрыть элемент. Где bool — может принимать значения true/false т.е. скрыть/показать элемент; String* — указатель на rootVar (корневая переменная) элемента.

Теперь добавим содержимое функции void Brutforce(String*):


/* User functions: */
void Brutforce(String* rootVar) {
  /* make random original key */
  uint8_t okey = random(0, 256); 
  String okeyString = "";
  for (int i = 0; i < 8; i++) {
    okeyString += String((okey >> i) & 1);
  }
  UISet(&UITextbox_sxjzx0g, okeyString); // set value for Textbox

  UIDisable(false, &UIWaitingbar_yksk2w8); // show the Waitingbar

  uint8_t key;

  while (true) {
    key++;
    String keyString = String();
    for (int i = 0; i < 8; i++) {
      keyString += String((key >> i) & 1);
    }
    UISet(&UITextbox_eyar2x, keyString);
    Serial.print(key);
    Serial.print(" ");
    Serial.println(okey);
    if (key == okey) break;
    M5.Speaker.tone(800);
    delay(40);
    M5.Speaker.mute();
  }
  UIDisable(true, &UIWaitingbar_yksk2w8);
  M5.Speaker.tone(600);
  delay(75);
  M5.Speaker.mute();
  M5.Speaker.tone(800);
  delay(75);
  M5.Speaker.mute();
  M5.Speaker.tone(500);
  delay(75);
  M5.Speaker.mute();
}

Для того, чтобы установить значение для элемента используют функцию void UISet(String*, String) или void UISet(String*, int). Где String* — указатель на rootVar (корневая переменная) элемента; String или int — новое значение.

Теперь запустим и посмотрим, что получилось (рис. 5.1).



Рисунок 5.1


Шаг 6. BatteryCheck и Progressbar Σ(O_O)


Давайте сделаем тестер заряда обычных пальчиковых батареек типа A, AA, AAA. Индикацию будем осуществлять с помощью Progressbar в процентном соотношении, а в дополнении внизу с помощью Textbox будем отображать напряжение в мВ (рис. 6).



Рисунок 6.


Здесь мы модифицируем функцию слоя


/* Function for layer default: */
void LayerFunction_default(String* rootVar) {
 /* To open this layer use: */
 ...
 // BattaryCheck
 while (true) {
  int voltage = analogRead(35) * 3400 / 4096;
  int percentage = voltage * 100 / 1600;
  UISet(&UIProgressbar_1mlmhcu, percentage);
  UISet(&UITextbox_gtaetjh, voltage);
  delay(500);
 }
}

Постоянно, каждые 500 мс, в цикле будем снимать показания с АЦП порт 0 (контакт 35).
Затем будем рассчитывать напряжение: 3400 мВ — это опорное напряжение, 4096 — разрешающая способность АЦП. Схема включения приведена на рисунке 6.1.


Обратите внимание — используется M5 Bottom вместо FACES


Рисунок 6.1. Наглядная схема включения встроенного АЦП к пальчиковой батарейке


Устройство работает здорово! Теперь у Вас есть отличный инструмент для проверки батареек (рис. 6.2).



Рисунок 6.2


Шаг 7. LEDshift и Selectbox ( ◡‿◡ *)


Возьмем четыре светодиода (оранжевый, зелёный, красный и жёлтый) подключим их через сдвиговый регистр 74HC595N к M5 по схеме на рисунке 7.



Рисунок 7. Схема подключения светодиодов к M5 с помощью сдвигового регистра


Создадим графический интерфейс с помощью M5 UI Designer, как в предыдущих примерах, и скопируем код (рис. 7.1).



Рисунок 7.1.


Теперь модифицируем код.


Добавим номера контактов на M5 для подключения сдвигового регистра 74HC595N после RootVar's:


/* RootVar's for UI elements (note: not edit manually) */
...
// Shift register pinout
int SH_CP = 17;
int ST_CP = 2;
int DS = 5;

Далее добавим содержимое функции void SelectColor(String*):


/* User functions: */
void SelectColor(String* rootVar) {
  int led = UIOptionValue(&UISelectbox_6foo6h).toInt();
  digitalWrite(ST_CP, LOW);
  shiftOut(DS, SH_CP, MSBFIRST, led);
  digitalWrite(ST_CP, HIGH);
  delay(100);
}

Для того, чтобы получить значение выбранной опции из Selectbox используют функцию String UIOptionValue(String*).

Теперь необходимо наполнить Selectbox опциями. для этого добавим в самое начало функции слоя 5 строк кода:


/* Function for layer default: */
void LayerFunction_default(String* rootVar) {
 // add options to Selectbox
 UIOption("OFF", "0", &UISelectbox_6foo6h);
 UIOption("RED", "17", &UISelectbox_6foo6h);
 UIOption("YELLOW", "3", &UISelectbox_6foo6h);
 UIOption("GREEN", "5", &UISelectbox_6foo6h);
 UIOption("ORANGE", "9", &UISelectbox_6foo6h);
 ...
}

Для того, чтобы добавить опцию в Selectbox используют функцию void UIOption(String, String, String*). Где первый String — подпись опции, которую видит пользователь; второй String — значение опции, которое скрыто от пользователя; String* — указатель на rootVar (корневая переменная) элемента

В завершении добавим три строчки после приведенного ниже комментария. Таким образом мы настроим контакты M5 на вывод:


void setup() {
 ...
 /* Prepare user's I/O. For example pinMode(5, OUTPUT); */
 pinMode(SH_CP, OUTPUT);
 pinMode(ST_CP, OUTPUT);
 pinMode(DS, OUTPUT);
 ...
}

На этом всё! :) (рис. 7.2).



Рисунок 7.2


Шаг 8. SmartDrill и команда из Rangebox, Checkbox, Button (´。• ᵕ •。`)


Что может объединить Rangebox, Checkbox и Button? Верно! — станок для сверления. Возьмём электродвигатель постоянного тока (например, от кассетного плеера), транзистор (чтоб по мощности подходил), резистор чтоб перекрывать ток базы, немного проводов, макетную плату и соберём! Бывает необходимо делать несколько отверстий одно за одним, а бывает необходимо сделать всего одно, поэтому возникает идея выделять некоторое время на работу дрели, а потом отключать её автоматически: тут нам на помощь приходит Checkbox.



Рисунок 8


С помощью Rangebox будем регулировать напряжение питания на электродвигателе. Кнопкой будем запускать и останавливать процесс (рис. 8). Обратите внимание — кнопка с иконкой ;)


Кнопка Enter здесь нам не пригодится, поэтому Enter key для всех элементов останется пустым (нулем).
Взамен мы будем вызывать пользовательскую функцию Drill с помощью свойства Callback для кнопки.


Что у нас по коду? После RootVar's добавим bool startStatus, которая позволит программе понимать запущен ли двигатель или нет.


/* RootVar's for UI elements (note: not edit manually) */
...
// User's variables
bool startStatus = false;

Наполним пользовательскую функцию void Drill(String*):


void Drill(String* rootVar) {
  startStatus = (startStatus) ? false : true;
  if (startStatus)
  {
    int power = UIRangebox_ztj619h.toInt() * 255 / 100;
    dac_out_voltage(DAC_CHANNEL_1, power);
    if (UICheckbox_1n9gs0b == "true")
    {
      UICaption("WAIT", &UIButton_enhu9fc);
      delay(25000);
      Drill(0);
      return;  
    }
    UICaption("STOP", &UIButton_enhu9fc);
  }
  else
  {
    dac_out_voltage(DAC_CHANNEL_1, 0);
    UICaption("START", &UIButton_enhu9fc);
  }
}

Для того, чтобы установить значение на аналоговом порту используют функцию dac_out_voltage(DAC_CHANNEL_1, int). Где DAC_CHANNEL_1 — номер канала (контакт 25), int — значение

;


Для того, чтобы изменить заголовок любого из UI-элементов используют функцию UICaption(String, String). Где String — новый заголовок; String\ — указатель на rootVar (корневая переменная) элемента

Ура! Теперь можно пробовать сверлить (рис. 8.1).



Рисунок 8.1


Шаг 9. Запуск!


В разделе "Ссылки" прилагаются видео с демонстрацией работы. На этом урок завершён.


Ссылки \ Downloads


Теги:
Хабы:
+7
Комментарии 1
Комментарии Комментарии 1

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн