Navrhněte zařízení sloužící k zobrazování informací na e-ink displeji Waveshare. Informace ke zobrazení se budou přenášet z PC pomocí zvoleného komunikačního rozhraní. Pokuste se dosáhnout co nejnižší spotřeby celého zařízení.
Elektronický papier (ePaper) alebo e-ink displej je plochá zobrazovacia jednotka, ktorá odráža svetlo ako normálny papier a je schopná uchovať zobrazenie bez spotreby elektrickej energie. Pre svoju nízku energetickú náročnosť je vhodný pre použitie v batériovo napájaných vstavaných zariadeniach prepojených s internetom IoT (angl. Internet of Things - internet vecí).
Cieľom tohto projektu je navrhnúť jednoduchý informačný panel s e-ink displejom pre zobrazovanie informácií, ako napr.:
ESP32-DevKitC je malá vývojová doska založená na obvode ESP32 firmy Espressif. Ide o cenovo dostupný SoC obvod, ktorý okrem iných obsahuje: dvojjadrový 32-bitový Xtensa LX6 mikroprocesor, Ultra-Low-Power koprocesor, 520k RAM, 34 GPIOs, 7 ADC, WiFi 802.11b/g/n/e/i, Bluetooth LE a. i. Vývojová doska ESP32-DevKitC navyše obsahuje prevodník USB/sériová linka CP2102 pre programovanie a komunikáciu s počítačom.
Ide o modul s 4,2„ e-ink displejom a vstavaným COG kontrolérom. Jeho kľúčové vlastnosti sú:
Softwareu pre obvod ESP32 bol realizovaný vo vývojovom prostredí Visual Studio Code s nainštalovaným cross-platformovým IDE PlatformIO, ktorého súčasťou je framework esp-idf dodávaný firmou Espressif.
Knižnica funkcií epd4in2.h
pre ovládanie e-ink displeja bola vytvorená na základe knižnice v demo aplikácii, ktorú však bolo potrebné vo veľkej miere prispôsobiť obvodu ESP32. Jej hlavné funkcie sú:
// Funkcia pre inicializáciu displeja void EPD_Init(); // Funkcia, kt. čaká, pokiaľ je v aktívnej úrovni BUSY signál displeja void EPD_WaitUntilIdle(); // Funkcia pre zaslanie frameu a jeho zobrazenie (aktualizácia displeja) void EPD_DisplayFrame(const uint8_t *frame); // Funkcia pre uvedenie displeja do režimu hlbokého spánku void EPD_Sleep();
Buffer frame_buffer
pre dáta k zobrazeniu na displeji je potrebné alokovať hneď na začiatku hlavnej funkcie app_main()
. Parameter MALLOC_CAP_DMA
zabezpečuje, že alokovaný pamäťový priestor je dostupný cez DMA, ktorú využíva SPI driver obvodu ESP32.
frame_buffer = pvPortMallocCaps((EPD_WIDTH * EPD_HEIGHT / 8), MALLOC_CAP_DMA);
Aktuálne počasie program sťahuje pomocou http GET requestu zo servera http://openweathermap.org. Využíva sa lwIP stack a sieľový socket.
#define WEB_SERVER "api.openweathermap.org" #define WEB_PORT (80) #define WEB_URL "http://api.openweathermap.org/data/2.5/weather?q=Brno,cz&units=metric&appid=<API_KEY>" static const char *REQUEST = "GET " WEB_URL " HTTP/1.0\r\n" "Host: "WEB_SERVER"\r\n" "User-Agent: esp-idf/1.0 esp32\r\n" "Connection: close\r\n" "\r\n";
Odpoveď zo servera sú dáta o aktuálnom počasí pre požadované mesto vo formáte JSON. Príklad:
{„coord“:{„lon“:16.61,“lat„:49.19},“weather„:[{„id“:804,“main„:„Clouds“,“description„:„overcast clouds“,“icon„:„04n“}],“base„:„stations“,“main„:{„temp“:-2.53,“pressure„:1027,“humidity„:86,“temp_min„:-3,“temp_max„:-2},“visibility„:10000,“wind„:{„speed“:5.1,“deg„:70},“clouds„:{„all“:90},“dt„:1515949200,“sys„:{„type“:1,“id„:5899,“message„:0.0074,“country„:„CZ“,“sunrise„:1515912197,“sunset„:1515943367},“id„:3078610,“name„:„Brno“,“cod„:200}
Pre ďalšie spracovanie je potrebné tieto dáta rozparsovať. Za týmto účelom bola s použitím knižnice cJSON.h
vytvorená funkcia parseWeatherData
, ktorá zo vstupného JSON reťazca vyextrahuje údaje o teplote, atmosférickom tlaku, relatívnej vlhkosti, rýchlosti vetra a relatívnej oblačnosti.'
/* Read HTTP response */ bzero(recv_buf, sizeof(recv_buf)); r = read(s, recv_buf, sizeof(recv_buf) - 1); recv_buf[r] = '\0'; ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d\r\n\r\n", r, errno); close(s); char *weather_json = (char *)strstr(recv_buf, "{"); if (parseWeatherData(weather_json, &weather)) { ESP_LOGE(TAG, "Failed to parse weather JSON...\r\n"); };
int parseWeatherData(const char * const weatherString, weather_data_t * const parsedWeather) { const cJSON *mainObj = NULL; const cJSON *temperature = NULL; const cJSON *pressure = NULL; const cJSON *humidity = NULL; const cJSON *wind = NULL; const cJSON *speed = NULL; const cJSON *clouds = NULL; const cJSON *all = NULL; cJSON *weather_json = cJSON_Parse(weatherString); if (weather_json == NULL) { const char *error_ptr = cJSON_GetErrorPtr(); if (error_ptr != NULL) { printf("Error before: %s\r\n", error_ptr); return 1; }; }; mainObj = cJSON_GetObjectItem(weather_json, "main"); temperature = cJSON_GetObjectItem(mainObj, "temp"); pressure = cJSON_GetObjectItem(mainObj, "pressure"); humidity = cJSON_GetObjectItem(mainObj, "humidity"); wind = cJSON_GetObjectItem(weather_json, "wind"); speed = cJSON_GetObjectItem(wind, "speed"); clouds = cJSON_GetObjectItem(weather_json, "clouds"); all = cJSON_GetObjectItem(clouds, "all"); parsedWeather->temp = temperature->valuedouble; parsedWeather->pressure = (uint16_t)pressure->valueint; parsedWeather->humidity = (uint8_t)humidity->valueint; parsedWeather->windSpeed = speed->valuedouble; parsedWeather->clouds = all->valueint; cJSON_Delete(weather_json); return 0;
Aktuálny čas a dátum, určený pre zobrazenie poslednej aktualizácie displeja, program nastavuje pomocou protokolu SNTP.
static void initialize_sntp(void) { ESP_LOGI(TAG, "Initializing SNTP"); sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_init(); } static int obtain_time(void) { int res = 1; xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); initialize_sntp(); // wait for time to be set int retry = 0; const int retry_count = 30; time(&time_now); tm_info = localtime(&time_now); while(tm_info->tm_year < (2016 - 1900) && ++retry < retry_count) { ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); vTaskDelay(500 / portTICK_RATE_MS); time(&time_now); tm_info = localtime(&time_now); } if (tm_info->tm_year < (2016 - 1900)) { ESP_LOGI(TAG, "System time NOT set."); res = 0; } else { ESP_LOGI(TAG, "System time is set."); } return res; }
K formátovaniu výstupov na displej bola využitá knižnica epdpaind.h
z demo aplikácie. Táto knižnica poskytuje základné funkcie pre vykreslenie základných geometrických tvarov a textu s využitím jednoduchých fontov z knižnice fonts.h
. Väčšie možnosti poskytuje napr. v závere projektu objavená knižnica ESP32_ePaper_example.
sprintf(tmp_buff, "Waiting for weather..."); Paint_DrawStringAt(&paint, 20, 60, tmp_buff, &Font16, UNCOLORED); sprintf(tmp_buff, "Current Weather in Brno:"); Paint_DrawStringAt(&paint, 20, 120, tmp_buff, &Font20, COLORED); sprintf(tmp_buff, "Temperature: %2.2f *C", weather.temp); Paint_DrawStringAt(&paint, 20, 140, tmp_buff, &Font16, COLORED); sprintf(tmp_buff, "Pressure: %4d hPa", weather.pressure); Paint_DrawStringAt(&paint, 20, 160, tmp_buff, &Font16, COLORED); sprintf(tmp_buff, "Humidity: %3d %%", weather.humidity); Paint_DrawStringAt(&paint, 20, 180, tmp_buff, &Font16, COLORED); sprintf(tmp_buff, "Wind speed: %2.2f m/s", weather.windSpeed); Paint_DrawStringAt(&paint, 20, 200, tmp_buff, &Font16, COLORED); sprintf(tmp_buff, "Cloudiness: %3d %%", weather.clouds); Paint_DrawStringAt(&paint, 20, 220, tmp_buff, &Font16, COLORED); time(&time_now); tm_info = localtime(&time_now); sprintf(tmp_buff, "Last update: Date: %02d.%02d.%04d at %02d:%02d:%02d ", tm_info->tm_mday, tm_info->tm_mon + 1, tm_info->tm_year + 1900, tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec ); Paint_DrawStringAt(&paint, 20, 281, tmp_buff, &Font12, COLORED); EPD_DisplayFrame(frame_buffer); EPD_WaitUntilIdle();
Po aktualizácii zobrazenia je možné displej aj obvod ESP32 uviesť do režimu hlbokého spánku. U displeja sa toho dosiahne odoslaním potrebných príkazov volaním funkcie EPD_Sleep
. U obvodu ESP32 bolo v pláne využiť funkciu esp_deep_sleep
, ktorá obvod uvedie do režimu hlbokého spánku na stanovenú dobu, po ktorej sa opäť automaticky preberie a vykoná svoju funkciu.
Výsledkom riešenia projektu je informačný panel, ktorý zobrazuje informácie o aktuálnom počasí v užívateľom definovanom meste. Dáta vo formáte JSON sú získavané prostredníctvom http protokolu zo servera openweathermap.org
. Tie sú následne rozparsované a naformátované do výstupného frameu, ktorý je odoslaný do e-ink displeja prostredníctvom SPI rozhrania a zobrazený. Funkčnosť je možné overiť vo videoukážke.
Po úspešnom zobrazení aktuálneho počasia je displej uvedený do režimu hlbokého spánku, nie však obvod ESP32, nakoľko po prebudení z režimu hlbokého spánku (po volaní funkcie esp_deep_sleep
) došlo k nešpecifikovanej chybe vedúcej k zlyhaniu programu, ktorá pre časovú tieseň v závere projektu dosiaľ nebola odladená
Definíciu mesta, pre ktoré sa má zobrazovať počasie, ako aj prístupových údajov do WiFi siete, do ktorej má byť obvod pripojený, API kľúča pre OpenWeatherMap a iných parametrov, je nutné vykonať priamo v zdrojovom kóde programu obvodu. Z dôvodu nutnosti zoznámiť sa s novým obvodom a jeho funkciami, vývojovým prostredím a preštudovať pomerne veľké množstvo dokumentácie, nezostal v rámci projektu čas na pôvodný zámer, ktorým bolo vykonávať všetku konfiguráciu prostredníctvom web stránky (bootstrap). Toto, spolu s ďalšími vecami, ako napr. rozšírenie zobrazenia o predpoveď počasia, jednoduché piktogramy, emailové notifikácie a pod., zostáva námetom pre ďalšiu prácu.