Создание меню для Ардуино с энкодером и ЖКИ Nokia 5110

AD267 2 300x300В предыдущей статье мы рассмотрели, как создать меню для вашего проекта Arduino на ЖК-дисплее Nokia 5110 с кнопками для навигации по нему. В этой статье мы сделаем модифицированную версию меню, которая будет использовать энкодер вместо кнопок для навигации.

 Меню является одним из самых простых способов, с помощью которых пользователи могут взаимодействовать с устройствами с различными характеристиками. От смартфонов до ПК и даже телевизоров, меню используется почти в каждом электронном устройстве с экраном, а навигация по нему обычно выполняется нажатием определенных кнопок для перемещения вверх/вниз, вправо/влево и выбора. Однако, в некоторых приборах, или для эстетики или для улучшения форм-фактора, для навигации использован подход, похожий на ручку регулировки. В сегодняшнем уроке мы объясним, как создать меню с управлением в стиле ручки регулировки, используя вращающийся энкодер.

Энкодеры использованы во многих системах, где необходимы точность и обратная связь при работе с понятиями вращательного движения или углового положения . Поворачивая вал вправо или влево, мы либо получаем увеличение, либо уменьшение какого-либо значения. Одно из главных преимуществ энкодеров это то, что их вращение безгранично. Если достигнуто максимальное положение (которое составляет 20 для данного конкретного поворотного энкодера, используемого в этой статье), устройство начинает отсчет положения снова и снова, в то время как значение, связанное с текущей регулировкой, продолжает увеличиваться/уменьшаться с каждым поворотом ручки в том же направлении. Еще одна хорошая функция, которая будет удобна в данном случае, заключается в том, что в его составе есть кнопка, поэтому ее можно щелкнуть, нажав на ручку, и она распознается Arduino так же, как и любая другая кнопка или переключатель.

Необходимые компоненты

Для построения этого проекта требуются следующие компоненты;

  • Arduino Uno или аналогичная плата
  • энкодер с нажимной кнопкой
  • Nokia 5110 LCD
  • макетная плата
  • перемычки
  • внешний аккумулятор


Схема подключения энкодера к Ардуино

Схема для сегодняшнего проекта почти аналогична схеме из предыдущей статьи. Мы уберемтри кнопки и добавим вращающийся энкодер. Соедините компоненты, как показано на схеме ниже.

Uno and Rotary encoder bb 768x577

Используемый энкодер является аналоговым устройством, и по этой причине все его три вывода подключены аналоговые пины на Arduino.

Для того чтобы сделать соединения более легким следовать, карта Штыря между компонентами обеспечена ниже.

LCD – Arduino
Reset - D3
pin2 - D4
pin3 - D5
pin4 - D11
pin5 - D13
VCC - 3.3v
Backlight - D7
GND - GND

 

Энкодер-Arduino
GND - GND
VCC - VCC
SW - A2
DT - A1
CLK - A0

Код

Чтобы упростить / уменьшить объем работы, мы будем использовать четыре библиотеки. Две из библиотек: Adafruit GFX library и Nokia 5110 LCD library, будут использоваться для взаимодействия с дисплеем, в то время как другие две: Encoder Library и TimerOne library, позволят уменьшить количество кода для взаимодействия с энкодером. Каждую из библиотек можно загрузить по ссылкам, прикрепленным к ним, или установить через менеджер библиотек Arduino.

Код проекта мало чем отличается от предыдущей статьи, поэтому по частям мы его разбирать не будем, а приведем сразу весь:

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <ClickEncoder.h>
#include <TimerOne.h>
int menuitem = 1;
int frame = 1;
int page = 1;
int lastMenuItem = 1;
String menuItem1 = "Contrast";
String menuItem2 = "Volume";
String menuItem3 = "Language";
String menuItem4 = "Difficulty";
String menuItem5 = "Light: ON";
String menuItem6 = "Reset";
boolean backlight = true;
int contrast=60;
int volume = 50;
String language[3] = { "EN", "ES", "EL" };
int selectedLanguage = 0;
String difficulty[2] = { "EASY", "HARD" };
int selectedDifficulty = 0;
boolean up = false;
boolean down = false;
boolean middle = false;
ClickEncoder *encoder;
int16_t last, value;
Adafruit_PCD8544 display = Adafruit_PCD8544( 5, 4, 3); //Download the latest Adafruit Library in order to use this constructor
void setup() {
  pinMode(7,OUTPUT);
  turnBacklightOn();
 
  encoder = new ClickEncoder(A1, A0, A2);
  encoder->setAccelerationEnabled(false);
   
  display.begin();      
  display.clearDisplay();
  setContrast();  
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);
 
  last = encoder->getValue();
}
void loop() {
  drawMenu();
  readRotaryEncoder();
   ClickEncoder::Button b = encoder->getButton();
   if (b != ClickEncoder::Open) {
   switch (b) {
      case ClickEncoder::Clicked:
         middle=true;
        break;
    }
  }    
 
  if (up && page == 1 ) {
     
    up = false;
    if(menuitem==2 && frame ==2)
    {
      frame--;
    }
     if(menuitem==4 && frame ==4)
    {
      frame--;
    }
      if(menuitem==3 && frame ==3)
    {
      frame--;
    }
    lastMenuItem = menuitem;
    menuitem--;
    if (menuitem==0)
    {
      menuitem=1;
    }
  }else if (up && page == 2 && menuitem==1 ) {
    up = false;
    contrast--;
    setContrast();
  }
  else if (up && page == 2 && menuitem==2 ) {
    up = false;
    volume--;
  }
  else if (up && page == 2 && menuitem==3 ) {
    up = false;
    selectedLanguage--;
    if(selectedLanguage == -1)
    {
      selectedLanguage = 2;
    }
  }
    else if (up && page == 2 && menuitem==4 ) {
    up = false;
    selectedDifficulty--;
    if(selectedDifficulty == -1)
    {
      selectedDifficulty = 1;
    }
  }
  if (down && page == 1) //We have turned the Rotary Encoder Clockwise
  {
    down = false;
    if(menuitem==3 && lastMenuItem == 2)
    {
      frame ++;
    }else  if(menuitem==4 && lastMenuItem == 3)
    {
      frame ++;
    }
     else  if(menuitem==5 && lastMenuItem == 4 && frame!=4)
    {
      frame ++;
    }
    lastMenuItem = menuitem;
    menuitem++;  
    if (menuitem==7)
    {
      menuitem--;
    }
 
  }else if (down && page == 2 && menuitem==1) {
    down = false;
    contrast++;
    setContrast();
  }
  else if (down && page == 2 && menuitem==2) {
    down = false;
    volume++;
  }
   else if (down && page == 2 && menuitem==3 ) {
    down = false;
    selectedLanguage++;
    if(selectedLanguage == 3)
    {
      selectedLanguage = 0;
    }
  }
  else if (down && page == 2 && menuitem==4 ) {
    down = false;
    selectedDifficulty++;
    if(selectedDifficulty == 2)
    {
      selectedDifficulty = 0;
    }
  }
 
  if (middle) //Middle Button is Pressed
  {
    middle = false;
   
    if (page == 1 && menuitem==5) // Backlight Control
    {
      if (backlight)
      {
        backlight = false;
        menuItem5 = "Light: OFF";
        turnBacklightOff();
        }
      else
      {
        backlight = true;
        menuItem5 = "Light: ON";
        turnBacklightOn();
       }
    }
    if(page == 1 && menuitem ==6)// Reset
    {
      resetDefaults();
    }
    else if (page == 1 && menuitem<=4) {
      page=2;
     }
      else if (page == 2)
     {
      page=1;
     }
   }   
  }
 
  void drawMenu()
  {
    
  if (page==1)
  {    
    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);
    display.setCursor(15, 0);
    display.print("MAIN MENU");
    display.drawFastHLine(0,10,83,BLACK);
    if(menuitem==1 && frame ==1)
    {   
      displayMenuItem(menuItem1, 15,true);
      displayMenuItem(menuItem2, 25,false);
      displayMenuItem(menuItem3, 35,false);
    }
    else if(menuitem == 2 && frame == 1)
    {
      displayMenuItem(menuItem1, 15,false);
      displayMenuItem(menuItem2, 25,true);
      displayMenuItem(menuItem3, 35,false);
    }
    else if(menuitem == 3 && frame == 1)
    {
      displayMenuItem(menuItem1, 15,false);
      displayMenuItem(menuItem2, 25,false);
      displayMenuItem(menuItem3, 35,true);
    }
     else if(menuitem == 4 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,false);
      displayMenuItem(menuItem3, 25,false);
      displayMenuItem(menuItem4, 35,true);
    }
      else if(menuitem == 3 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,false);
      displayMenuItem(menuItem3, 25,true);
      displayMenuItem(menuItem4, 35,false);
    }
    else if(menuitem == 2 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,true);
      displayMenuItem(menuItem3, 25,false);
      displayMenuItem(menuItem4, 35,false);
    }
    
    else if(menuitem == 5 && frame == 3)
    {
      displayMenuItem(menuItem3, 15,false);
      displayMenuItem(menuItem4, 25,false);
      displayMenuItem(menuItem5, 35,true);
    }
    else if(menuitem == 6 && frame == 4)
    {
      displayMenuItem(menuItem4, 15,false);
      displayMenuItem(menuItem5, 25,false);
      displayMenuItem(menuItem6, 35,true);
    }
    
      else if(menuitem == 5 && frame == 4)
    {
      displayMenuItem(menuItem4, 15,false);
      displayMenuItem(menuItem5, 25,true);
      displayMenuItem(menuItem6, 35,false);
    }
      else if(menuitem == 4 && frame == 4)
    {
      displayMenuItem(menuItem4, 15,true);
      displayMenuItem(menuItem5, 25,false);
      displayMenuItem(menuItem6, 35,false);
    }
    else if(menuitem == 3 && frame == 3)
    {
      displayMenuItem(menuItem3, 15,true);
      displayMenuItem(menuItem4, 25,false);
      displayMenuItem(menuItem5, 35,false);
    }
        else if(menuitem == 2 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,true);
      displayMenuItem(menuItem3, 25,false);
      displayMenuItem(menuItem4, 35,false);
    }
    else if(menuitem == 4 && frame == 3)
    {
      displayMenuItem(menuItem3, 15,false);
      displayMenuItem(menuItem4, 25,true);
      displayMenuItem(menuItem5, 35,false);
    }   
    display.display();
  }
  else if (page==2 && menuitem == 1)
  {    
   displayIntMenuPage(menuItem1, contrast);
  }
  else if (page==2 && menuitem == 2)
  {
   displayIntMenuPage(menuItem2, volume);
  }
   else if (page==2 && menuitem == 3)
  {
   displayStringMenuPage(menuItem3, language[selectedLanguage]);
  }
  else if (page==2 && menuitem == 4)
  {
   displayStringMenuPage(menuItem4, difficulty[selectedDifficulty]);
  }
  else if (page==2 && menuitem == 4)
  {
   displayStringMenuPage(menuItem4, difficulty[selectedDifficulty]);
  }
 
  }
  void resetDefaults()
  {
    contrast = 60;
    volume = 50;
    selectedLanguage = 0;
    selectedDifficulty = 0;
    setContrast();
    backlight = true;
    menuItem5 = "Light: ON";
    turnBacklightOn();
  }
  void setContrast()
  {
    display.setContrast(contrast);
    display.display();
  }
  void turnBacklightOn()
  {
    digitalWrite(7,LOW);
  }
    void turnBacklightOff()
  {
    digitalWrite(7,HIGH);
  }
  void timerIsr() {
  encoder->service();
}
void displayIntMenuPage(String menuItem, int value)
{
    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);
    display.setCursor(15, 0);
    display.print(menuItem);
    display.drawFastHLine(0,10,83,BLACK);
    display.setCursor(5, 15);
    display.print("Value");
    display.setTextSize(2);
    display.setCursor(5, 25);
    display.print(value);
    display.setTextSize(2);
    display.display();
}
void displayStringMenuPage(String menuItem, String value)
{
    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(BLACK, WHITE);
    display.setCursor(15, 0);
    display.print(menuItem);
    display.drawFastHLine(0,10,83,BLACK);
    display.setCursor(5, 15);
    display.print("Value");
    display.setTextSize(2);
    display.setCursor(5, 25);
    display.print(value);
    display.setTextSize(2);
    display.display();
}
void displayMenuItem(String item, int position, boolean selected)
{
    if(selected)
    {
      display.setTextColor(WHITE, BLACK);
    }else
    {
      display.setTextColor(BLACK, WHITE);
    }
    display.setCursor(0, position);
    display.print(">"+item);
}
void readRotaryEncoder()
{
  value += encoder->getValue();
 
  if (value/2 > last) {
    last = value/2;
    down = true;
    delay(150);
  }else   if (value/2 < last) {
    last = value/2;
    up = true;
    delay(150);
  }
}

Полный код для проекта  также прилагается для загрузки в конце статьи.

Демонстрация

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

Screen Shot 2019 04 27 at 1 46 57 AM 1 1024x638

Вот и все.

Скачать полный проект в архиве.

 

Загрузка...