W tym chciałbym opisać szybki projekt w Arduino na ESP32.
[Źródło: http://paulobrien.co.nz/2017/03/16/esp32-programming-with-arduino-on-windows/]
Opis:
ESP32 ma za zadanie:
- Odczytać dane z czterech czujników temperatury. Czujniki zostały podłączone na osobnych pinach w celu łatwiejszego określenia który czujnik jest podłączony do której linii. Dodatkowo zwiększa to odporność na błędy w połączeniu.
- Zapis odczytanych informacji z czujników na karcie SD z datą oraz godziną dokonania pomiarów.
- Wyświetlenie danych na serwerze WWW, gdzie ESP jest podłączone do lokalnej sieci WIFI.
- Pobranie aktualnego czasu z serwera NTP w celu inicjalizacji i utrzymywania aktualnej daty oraz godziny w zegarku RTC PCF. Wybrałem zewnętrzny moduł zegara bo ma już baterię z podtrzymaniem czasu w przypadku gdy ESP nie będzie miało połączenia z Internetem.
Podłączenie:
Dla ESP32 jest kilka pinów których należy unikać dokładniejszy opis znajduje się pod tym linkiem.
W skrócie, najprościej omijać piny 0, 1, 3, 6, 7, 8, 9, 10, 11, 12 (nie można wgrać programu do ESP gdy wykorzystywany jako input). Piny 34, 35, 36 ,39 mogą być wykorzystywane tylko jako input.
Projekt wykonałem z gotowych modułów. Ponieważ przy małej ilości potrzebnych urządzeń, jak tym przypadku jedno. Jest to najefektywniejszy sposób na bardzo szybkie przygotowanie gotowego produktu.
RTC komunikuje się przez I2C. Moduł PCF8583 zakłada podłączenie 4 pinów:
- VCC - 3V3;
- GND - GND;
- SDA - GPIO 21;
- SCL - GPIO 22;
Moduł karty SD komunikuje się przez interfejs SPI:
- VCC - 3V3;
- GND - GND;
- MISO - GPIO19;
- MOSI - GPIO23;
- SCLK - GPIO18;
- CS - GPIO5;
Wyświetlacz Nextion:
- VCC - VIN (5V)
- GND - GND
- RX - GPIO17; (TX UART2)
- TX - GPIO16; (RX UART2)
Czujniki DS18B20:
- VCC - 3V3
- GND - GND
- DS1 - GPIO_15
- DS2 - GPIO_13
- DS3 - GPIO_26
- DS4 - GPIO_27
Nextion:
Program dla wyświetlacza wykonałem w oparciu o Nextion Editor, który w łatwy sposób pozwala na dostosowanie ona wyświetlacza.
Dodatkowo dodałem obsługę dwóch zdarzeń kliknięcia ekranu, które pozwolą na zatrzymanie wykonywania pomiarów.
W związku tym, że wyświetlacze firmy Nextion mają tendencję do wypalania należy zastosować jakiś rodzaj wygaszania ekranu. Ja zdecydowałem na 2 sekundowe wyświetlenie czarnego tła co 200 pomiarów. Pozwoli to na wydłużenia żywotności matrycy wyświetlacza.
Gdy projektujemy jakiekolwiek GUI do wyświetlacza/strony internetowej itp itd. Bardzo ważnym elementem jest dobranie koloru tekstu czy przycisków pod kolor tła. Tak aby dane były maksymalnie dobrze czytelne. Dosyć dobry opis można znaleźć pod tym linkiem.
Mój ekran posiada czarne tło. W związku z tym zdecydowałem się wykorzystać w projekcie 5 kolorów czcionki.
- Żółty - #FFFF00;
- Zielony - #00FF00;
- Cyjan - #00FFFF;
- Magenta - #FF00FF;
- Biały - #FFFFFF;
Program:
W związku z tym, że odpytywanie i obrabianie wyników z czterech czujników zajmuje trochę czasu, wykorzystuje odczytywanie danych z czujników w osobnej pętli. Tak aby częściej była wykonywany zapis danych na kartę niż do serwera WWW.
Dodatkowo z powodów, których nie sprawdzałem. W przypadku gdy często wykorzystuje zapis danych do serwera WWW to procesor wykonuje reset przez wejście w panic mode. Optymalnym rozwiązaniem wydaje się zastosowanie czasu wynoszącego 30 sekund. Nie wyeliminowało to całkowicie występującego problemu, natomiast dosyć mocno go ograniczyło. W przypadku obsługi projektu z wyłączeniem części Web serwera problemu nie udało mi się zaobserwować.
Stronę internetową wykonałem w oparciu o przykład udostępniony na stronie randomnerdtutorials.
W projekcie korzystam z następujących bibliotek
- //DS18B20
- #include <OneWire.h>
- #include <DallasTemperature.h>
- //Wifi Web server
- #include <WiFi.h>
- #include <ESPAsyncWebServer.h>
- #include <AsyncTCP.h>
- //RTC - biblioteka zglasza bledy ale kod w esp dziala poprawnie
- #include <Rtc_Pcf8563.h>
- //Wyswietlacz Nextion
- #include "Nextion.h"
- //Karta SD
- #include "FS.h"
- #include "SD.h"
- #include "SPI.h"
Lista kontrolek obsługiwanych przez program jest następująca:
- NexPage p0 = NexPage(0,0,"temp_window");
- NexPage p1 = NexPage(1,0,"info_window");
- NexPage p2 = NexPage(2,0,"pause");
- NexPage p3 = NexPage(3,0,"clear_screen");
- NexText p0_ip_t0 = NexText(0,0,"t0");
- NexText p0_temp1_t1 = NexText(0,0,"t1");
- NexText p0_temp2_t2 = NexText(0,0,"t2");
- NexText p0_temp3_t3 = NexText(0,0,"t3");
- NexText p0_temp4_t4 = NexText(0,0,"t4");
- NexText p0_temp1_min_t5 = NexText(0,0,"t5");
- NexText p0_temp1_max_t6 = NexText(0,0,"t6");
- NexText p0_temp2_min_t7 = NexText(0,0,"t7");
- NexText p0_temp2_max_t8 = NexText(0,0,"t8");
- NexText p0_temp3_min_t9 = NexText(0,0,"t9");
- NexText p0_temp3_max_t10 = NexText(0,0,"t10");
- NexText p0_temp4_min_t11 = NexText(0,0,"t11");
- NexText p0_temp4_max_t12 = NexText(0,0,"t12");
- NexText p0_time_t13 = NexText(0,0,"t13");
- NexText p0_sd_t14 = NexText(0,0,"t14");
- NexText p1_start_info_t0 = NexText(0,0,"t0");
- NexText p1_sd_card_t1 = NexText(0,0,"t1");
- NexText p1_wifi_t2 = NexText(0,0,"t2");
- NexText p1_status_t3 = NexText(0,0,"t3");
- NexText p2_pause_t0 = NexText(0,0,"t0");
Poniżej część setup:
- void setup() {
- InitStructureData(&Temp_Struct, &ds_sensor_connection_stat);
- nexInit();
- Serial.begin(115200); //DEBUG
- Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);
- Nextion_Attach_Push();
- Nextion_Start();
- initWiFi();
- EnableDS18B20_Sensors();
- rtc.initClock(); //Czyści ustawione dane w RTC. Najlepiej ją pominąć.
- InitSDCardData();
- EnableWebServer_GetTime();
- fileName = PrepareFileNameString();
- p1_status_t3.setText("Status : OK");
- delay(2000); //Small delay for seeing start up info
- p0.show();
- displayInfoDataInWindow0();
- }
Idąc od początku:
- Ustawiam dane dla czujników DS18B20;
- Inicjalizuje bibliotekę wyświetlacza;
- Uruchamiam port szeregowy dla wiadomości debug,
- Uruchamiam port szeregowy Serial2 do komunikacji z wyświetlaczem,
- Podpinam funkcję obsługujące przycisków,
- Uruchamiam odpowiednie okno na wyświetlaczu,
- Uruchamiam WIFI,
- Uruchamiam zmienne dla bibliotek DS18B20,
- Uruchamiam RTC PCF8574,
- Uruchamiam kartę SD,
- Ustawiam serwer WWW oraz pobieram czas z serwera NTP,
- Przygotowuję nazwę pliku bazując na aktualnej dacie,
- Wyświetlam informację na wyświetlaczu o poprawnej informacji,
- Ładuję ekran z informacjami z czujników,
- Wyświetlam ip, status karty SD oraz czas na górze wyświetlacza;
Poniżej pętla loop:
- void loop() {
- nexLoop(nex_listen_list);
- if(pauseReadData == false) {
- if((millis() - lastTime_readSensors) > TIMEOUT_READ_SENSORS) {
- getSensorReadings(&Temp_Struct, &ds_sensor_connection_stat);
- print_sensor_data();
- print_time_from_rtc();
- printLocalTime();
- if(sdCardStatus == true) {
- dataToSave = PrepareDataInLineToSave();
- SaveDataToSDCard(SD, fileName, dataToSave);
- }
- PassSensorDataToDisplay();
- numberOfReadingsToClearScreen++;
- lastTime_readSensors = millis();
- }
- if ((millis() - lastTime_updateETH) > TIMEOUT_UPDATE_ETH) {
- if(wifiConnectedStatus == true) { UpdateDataInWebPage(); }
- lastTime_updateETH = millis();
- }
- }
- ChangeScreen();
- CheckIfEnableScreenSaver(&numberOfReadingsToClearScreen);
- }
Powyższy kod wykonuje następujące operacje:
- Pobranie danych od przycisków. Sprawdzenie czy nastąpiło kliknięcie przycisku.
- Sprawdzenie czy odczyt danych został zapauzowany;
- Jeśli zadany czas przeminął to nastąpi odczyt danych z czujników i ich aktualizacja na karcie SD oraz na wyświetlaczu.
- Jeśli kolejny czas przeminął to następuje aktualizacja danych na stronie internetowej.
- Po za warunkiem sprawdzany jest konieczność zmiany okna na wyświetlaczu oraz uruchomienia wygaszania.
Większość elementów programu została przeniesiona do podfunkcji w celu zminimalizowania ilości danych w głównych częściach oraz łatwiejszej modyfikacji.
Strona WWW z wyświetlonymi danymi wygląda następująco:
Na powyższym zrzucie czujnik 1 jest odłączony. Co powoduje wyświetlenie kresek zamiast informacji z czujnika.
Pełny projekt można pobrać z dysku Google pod tym linkiem.