Arduino UNO i proste menu użytkownika wykonane z wykorzystaniem modułu LCD z magistralą szeregową i2c
Stronę tą wyświetlono już: 10387 razy
Same Arduino nie wiele znaczy bez dodatkowych elementów. W tym przypadku postanowiłem stworzyć coś na kształt interfejsu użytkownika w postaci menu wyświetlanego na module LCD wyposażonym w magistralę szeregową i2c, która umożliwia podłączenie i komunikowanie się za pomocą zaledwie czterech przewodów:
- GND - uziemnienie;
- VCC - 5+;
- SDA - linia przesyłanych danych;
- SDL - linia zegara
Połączenie układu pokazane zostało na poniższym rysunku. Ważne jest, aby zdawać sobie sprawę, że w Arduino UNO piny A4 i A5 mają specjalne przeznaczenie i umożliwiają komunikowanie się z urządzeniami poprzez magistralę szeregową i2c. W tymże układzie w zależności od ustawienia rezystancji rezystora wyświetlana będzie pozycja menu.

Poniżej zamieszczam dokładny opis magistrali szeregowej i2c wraz z modułem LCD. Każda magistrala szeregowa i2c ma przypisany adres, po którym urządzenie komunikuje się z Arduino. Takie rozwiązanie umożliwia podłączenie kilku różnych elementów sterowanych za pomocą magistrali szeregowej i2c pod warunkiem, że każda z nich ma przypisany inny adres. W przypadku poniższego modułu piny A1, A2 i A3 umożliwiają zmianę adresu magistrali. W moim przypadku adres mojej magistrali szeregowej i2c to 0x3F (zapis szesnastkowy), ale zdarzają się też moduły z ustawionym adresem na 0x27.

- piny sterujące i zasilające moduł;
- zworki umożliwiające zmianę adresu magistrali szeregowej i2c;
- rezystor regulujący kontrast wyświetlacza (jeżeli moduł nie wyświetla danych możliwe, że rezystor ten jest ustawiony w złym położeniu);
- zworka włączająca podświetlenie wyświetlacza LCD;
- dioda sygnalizująca podłączenie modułu do zasilania;
Jeżeli nie znasz adresu swojej magistrali szeregowej i2c poniższy kod umożliwi wykrycie tego adresu pod warunkiem, że do Arduino podpięte zostanie tylko jedno takie urządzenia.
- #include <Wire.h>
- void setup()
- {
- Wire.begin();
- Serial.begin(9600);
- while (!Serial); // Leonardo: wait for serial monitor
- Serial.println("nI2C Scanner");
- }
- void loop()
- {
- byte error, address;
- int nDevices;
- Serial.println("Scanning...");
- nDevices = 0;
- for(address = 1; address < 127; address++ )
- {
- // The i2c_scanner uses the return value of
- // the Write.endTransmisstion to see if
- // a device did acknowledge to the address.
- Wire.beginTransmission(address);
- error = Wire.endTransmission();
- if (error == 0)
- {
- Serial.print("I2C device found at address 0x");
- if (address<16)
- Serial.print("0");
- Serial.print(address,HEX);
- Serial.println(" !");
- nDevices++;
- }
- else if (error==4)
- {
- Serial.print("Unknown error at address 0x");
- if (address<16)
- Serial.print("0");
- Serial.println(address,HEX);
- }
- }
- if (nDevices == 0)
- Serial.println("No I2C devices foundn");
- else
- Serial.println("donen");
- delay(5000); // wait 5 seconds for next scan
- }
Zasada działania menu jest prosta, zmiana położenia pokrętła rezystora regulowanego powoduje zmianę odczytanego napięcia na pinie analogowym A0 i w zależności od jego wartości zmienia się pozycja wyświetlanego menu (co można zobaczyć na poniżej zamieszczonym filmie).
Do obsługi wyświetlacza konieczne jest załączenie następujących bibliotek:
- #include <Wire.h> // standardowa biblioteka Arduino
- #include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD
Bibliotekę New LiquidCrystal można pobrać z strony tutaj.
Inicjalizacja obiektu klasy LiquidCrystal_I2C odpowiedzialnego za komunikację z modułem LCD wygląda następująco:
- LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Sama konfiguracja wyświetlacza w funkcji setup wygląda następująco:
- lcd.begin(16,2); // liczba znaków w jednej linii, liczba linii tekstu
- lcd.setCursor(0,0);
- lcd.print("obliczeniowo.com");
- lcd.setCursor(0,1);
- lcd.print(".pl");
- lcd.setCursor(0,0);
Zadeklarowane przeze mnie klasy, które wykorzystuję do obsługi wyświetlania menu to:
- MenuItem - reprezentująca pojedynczy obiekt pozycji menu;
- Menu - reprezentująca menu i realizująca zadania związane z przewijaniem pozycji menu;
Klasa MenuItem jest swego rodzaju uproszczoną listą dwukierunkową. Oto deklaracje wyżej wymienionych klas:
- class MenuItem{
- MenuItem* before;
- MenuItem* after;
- String text;
- public:
- MenuItem(String text, MenuItem* before = NULL, MenuItem* after = NULL) : text(text), before(before), after(after){};
- ~MenuItem(){
- if(after){
- delete after;
- }
- }
- void addMenuPos(String text){
- if(after){
- after->addMenuPos(text);
- }else{
- after = new MenuItem(text, this);
- }
- }
- byte count(){
- if(after){
- return 1 + after->count();
- }
- return 1;
- }
- String getItemText(byte index){
- if(index && after){
- return after->getItemText(index - 1);
- }
- return text;
- }
- };
- class Menu{
- MenuItem *items;
- byte menuPos;
- public:
- Menu() : items(NULL), menuPos(0){};
- ~Menu() {
- delete items;
- }
- void addMenuPos(String text){
- if(items)
- items->addMenuPos(text);
- else
- items = new MenuItem(text);
- };
- void setMenuPos(byte menuPos, LiquidCrystal_I2C &lcd){
- if(this->menuPos != menuPos){
- lcd.clear();
- lcd.print(items->getItemText(menuPos));
- this->menuPos = menuPos;
- }
- }
- void menuLoop(LiquidCrystal_I2C &lcd){
- if(items){
- byte count = items->count();
- int maximum = 240;
- int minimum = 0;
- int dx = (maximum - minimum) / count;
- byte i = 0;
- int a0_read = analogRead(A0);
- for(int mp = maximum - dx; mp > minimum - dx; mp -= dx){
- if(mp <= a0_read){
- setMenuPos(i, lcd);
- break;
- }
- i++;
- }
- }
- }
- };
Globalnie utworzony obiekt klasy Menu:
- Menu menu;
Tworzenie menu w funkcji setup jest banalnie proste:
- menu.addMenuPos("Temperatura");
- menu.addMenuPos("Cisnienie");
- menu.addMenuPos("Naswietlenie");
- menu.addMenuPos("Pogoda");
- menu.addMenuPos("Czas");
- menu.addMenuPos("Napiecie");
- menu.addMenuPos("Menu 1");
- menu.addMenuPos("Menu 2");
- menu.addMenuPos("Menu 3");
Dodatkowo konieczne jest podciągnięcie rezystora pod pin A0:
- pinMode(A0, INPUT_PULLUP);
Oraz wywołanie pętli menu w funkcji loop:
- menu.menuLoop(lcd);
- delay(100);
Oczywiście powyższy przykład jest okrojoną wersją, albowiem w rzeczywistości często potrzebne są takie rozwiązania, które umożliwiają wywołanie konkretnej funkcji lub uruchomienie podmenu.

Tytuł:
TinyML. Wykorzystanie TensorFlow Lite do uczenia maszynowego na Arduino i innych mikrokontrolerach
Autor:
Pete Warden, Daniel Situnayake

Tytuł:
Arduino dla początkujących. Kolejny krok. Wydanie II
Autor:
Simon Monk

Tytuł:
Arduino dla początkujących. Podstawy i szkice. Wydanie II
Autor:
Simon Monk

Tytuł:
Elektronika z wykorzystaniem Arduino i Rapsberry Pi. Receptury
Autor:
Simon Monk

Tytuł:
Arduino dla dzieci. Poznaj świat elektroniki i programowania. Poznaj świat elektroniki i programowania
Autor:
Żarowska-Mazur Alicja, Mazur Dawid

Tytuł:
Roboty JavaScript od podstaw. Projekty NodeBots dla platformy Johnny-Five z wykorzystaniem płytek Raspberry Pi, Arduino oraz BeagleBone
Autor:
Rick Waldron, Backstop Media (lista współautorów w uwagach dodatkowych)

Tytuł:
Arduino dla początkujących. Kolejny krok
Autor:
Simon Monk

Tytuł:
Wprowadzenie do Arduino
Autor:
Massimo Banzi

Tytuł:
Projekty elektroniczne na iPhone i iPad. Niekonwencjonalne gadżety z technologią Arduino i techBASIC
Autor:
Mike Westerfield

Tytuł:
Arduino i projekty Lego. Zadziwiające projekty LEGO sterowane przez Arduino
Autor:
Lazar Jon