====== Zadanie ======
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í.
====== Úvod ======
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.:
* Počasie, kalendár...
* Emailové notifikácie, notifikácie sociálnych sietí...
* Akýkoľvek užívateľom zvolený informačný text/obrázok.
----
====== Vývojový hardware ======
===== ESP32-DevKitC =====
[[http://espressif.com/en/products/hardware/esp32-devkitc/overview|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.
{{:2017:e-ink:esp32_features.jpg?400 |}}{{:2017:e-ink:esp32-devkitc.jpg?400 |}}
===== Waveshare 4.2inch e-Paper Module =====
Ide o modul s 4,2" e-ink displejom a vstavaným COG kontrolérom. Jeho kľúčové vlastnosti sú:
* Rozlíšenie: 400*300 pixelov.
* Farebné rozlíšenie: čiernobiele (BW).
* Komunikačné rozhranie: 3-wire SPI/4-wire SPI.
* Bez možnosti čiastočnej obnovy zobrazenia (//partial refresh//).
{{:2017:e-ink:4.2inch-e-paper-module-5.jpg?400 |}}
----
====== Software ======
Softwareu pre obvod ESP32 bol realizovaný vo vývojovom prostredí Visual Studio Code s nainštalovaným cross-platformovým IDE [[http://platformio.org|PlatformIO]], ktorého súčasťou je framework [[https://github.com/espressif/esp-idf/releases|esp-idf]] dodávaný firmou Espressif.
==== Ovládanie e-ink displeja ====
Knižnica funkcií ''epd4in2.h'' pre ovládanie e-ink displeja bola vytvorená na základe knižnice v [[https://www.waveshare.com/wiki/File:4.2inch_e-paper_module_code.7z|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);
----
==== Počasie z OpenWeatherMap ====
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="
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;
----
===== Čas a dátum pomocou SNTP =====
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;
}
----
===== Formátovanie výstupu na displej =====
K formátovaniu výstupov na displej bola využitá knižnica ''epdpaind.h'' z [[https://www.waveshare.com/wiki/File:4.2inch_e-paper_module_code.7z|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 [[https://github.com/loboris/ESP32_ePaper_example|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.
----
====== Záver ======
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 [[https://drive.google.com/open?id=1Qaca2lGZD5fkr57FlzeoJnUczxQI-132|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.
[[https://drive.google.com/file/d/1Pg3luJ6izvvT_yll_Dd6A-uz28w3Sixp/view?usp=sharing|Zdrojové súbory]]
----
====== Zdroje ======
KOLBAN, Niel. //Kolban's book on ESP32// [[https://leanpub.com/ESP8266_ESP32]]\\
[[https://github.com/loboris/ESP32_ePaper_example]]\\
[[https://openweathermap.org/current]]\\
[[https://www.waveshare.com/wiki/4.2inch_e-Paper_Module]]