Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2017:e-ink

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

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.

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).


Software

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.

Ovládanie e-ink displeja

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_bufferpre 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=<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;

Č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 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.


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 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.

Zdrojové súbory


Zdroje

2017/e-ink.txt · Poslední úprava: 2018/01/15 02:46 autor: Branislav Michálek