E-Ink Weather Display

An E-Ink display is perfect for displaying information that does not change quickly. Displaying weather data and perhaps even a weather forecast is something that usually does not require an update every second.

Where to find weather data

Weather data can be retrieved by openweathermap for example. All it needs is an account. The retrieval of current weather data and a five day / three hour forecast is for free within certain limits. Limit means that the calls per minute of the service may not exceed 60.

Openweathermap provides an API that can be used in various programming languages. In the end an URL is used to fetch the desired data. The format of the result can be selected. The default is the JSON format which can be relatively simple parsed on Arduino using libraries such as ArduinoJson. Still I found the summary of the typical pitfalls helpful.

The same principle can be used with a tailor made outdoor weather sensor in combination with a custom HTTP web server that delivers the requested data in the desired format. But that is a different blog post.

Example URL

To retrieve the weather data from openweathermap a simple URL is required. This URL may contain the city ID which can be found here, the desired unit system and the API key that can be generated after sign up on openweathermap. Other parameters to adjust the resulting weather data can be added optionally.

The weather data for the desired location can be accessed in several ways: by city name, by zip code, by geographic location, … . However, openweathermap recommends to use the ID of the location. This list contains the IDs for the available locations.

This example of an URL will work in a browser as well (using a valid API key!):


Example Result in JSON format

Below is an exemplary result string:

"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],
"base":"cmc stations",

How to retrieve the current weather data is explained in more detail on openweathermap/current.

Components used & Wiring

The hardware setup is the same as in the previous blog post.


The Arduino sketch to retrieve and process weather data from openweathermap is based on an example from the ArduinoJson library. This example was extended to retrieve and process the weather data from openweathermap and to display the information on the E-Ink display.

// based on: https://github.com/bblanchon/ArduinoJson/blob/master/examples/JsonHttpClient/JsonHttpClient.ino

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
#include <epd.h>

const char* ssid = "SSID";
const char* password = "WIFIPASSWORD";

const char* server = "api.openweathermap.org"; // server's address
const int port = 80;
const char* resource = "/data/2.5/weather?id=2643743&units=metric&APPID=YourAPIKey"; // http resource

const unsigned long BAUD_RATE = 115200; // serial connection speed
const unsigned long HTTP_TIMEOUT = 10000; // max respone time from server
const size_t MAX_CONTENT_SIZE = 1024; // max size of the HTTP response

/* example URL
example result in JSON format:
"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],
"base":"cmc stations",

// weather data type
struct WeatherData {
char cityName[20];
char nowDescription[50];
char temperature[6];
char humidity[3];
char pressure[5];
char iconCode[4];

WiFiClient client;
boolean debug=true;

void initSerial();
void initializeEInkDisplay();
void connectWiFi();
bool connect(const char* hostName);
void disconnect();
void wait();
bool sendRequest(const char* host, const char* resource);
bool skipResponseHeaders();
void readReponseContent(char* content, size_t maxSize);
void printWeatherData(const struct WeatherData* weatherData);
bool parseWeatherData(char* content, struct WeatherData* weatherData);

void setSmallText(String text, int x, int y);
void setMediumText(String text, int x, int y);
void setLargeText(String text, int x, int y);

void updateDisplay(struct WeatherData* weatherData);
String selectWeatherIcon(struct WeatherData* weatherData);

void setup() {

void loop() {
if( connect(server) ) {
if( sendRequest(server, resource) && skipResponseHeaders() ) {

char jsonResult[MAX_CONTENT_SIZE];
readReponseContent(jsonResult, sizeof(jsonResult));

WeatherData weatherData;
if( parseWeatherData(jsonResult, &weatherData) ) {

void initSerial() {
while (!Serial) {
; // wait for serial port to initialize
if( debug ) Serial.println("Serial ready");

void initializeEInkDisplay() {
epd_set_memory(MEM_TF); // MEM_NAND=internal memory; MEM_TF=sd card
epd_set_color(BLACK, WHITE);

void connectWiFi() {
// connect to the WiFi network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
if( debug ) Serial.print(".");
if( debug ) {
Serial.println("WiFi connected");
Serial.print("IP address: ");

bool connect(const char* hostName) {
if( debug ) {
Serial.print("Connect to ");
bool ok = client.connect(hostName, port);
if( debug ) Serial.println(ok ? "Connected" : "Connection Failed!");
return ok;

bool sendRequest(const char* host, const char* resource) {
if( debug ) {
Serial.print("GET ");
client.print("GET ");
client.println(" HTTP/1.1");
client.print("Host: ");
client.println("Connection: close");
return true;

bool skipResponseHeaders() {
// HTTP headers end with an empty line
char endOfHeaders[] = "\r\n\r\n";
bool ok = client.find(endOfHeaders);
if (!ok) {
if( debug ) Serial.println("No response or invalid response!");
return ok;

void readReponseContent(char* content, size_t maxSize) {
size_t length = client.readBytes(content, maxSize);
content[length] = 0;
if( debug ) {

bool parseWeatherData(char* content, struct WeatherData* weatherData) {
// find first and last curly bracket of JSON result
String cStr = String(content);
int firstCurlyBracket = cStr.indexOf('{');
int lastCurlyBracket = cStr.lastIndexOf('}');
String c = cStr.substring(firstCurlyBracket,lastCurlyBracket+1);

/*if( debug ) {
Serial.println("parseWeatherData: ");
Serial.print("content length: ");

c.toCharArray(content, MAX_CONTENT_SIZE);

/*if( debug ) {
Serial.println("parseWeatherData: ");

StaticJsonBuffer<MAX_CONTENT_SIZE> jsonBuffer;

// find fields in JSON object
JsonObject& root = jsonBuffer.parseObject(content);
if (!root.success()) {
if( debug ) Serial.println("parsing JSON Object() failed");
return false;

strcpy(weatherData->cityName, root["name"]);
strcpy(weatherData->nowDescription, root["weather"][0]["description"]);
strcpy(weatherData->temperature, root["main"]["temp"]);
strcpy(weatherData->humidity, root["main"]["humidity"]);
strcpy(weatherData->pressure, root["main"]["pressure"]);
strcpy(weatherData->iconCode, root["weather"][0]["icon"]);
return true;

void printWeatherData(const struct WeatherData* weatherData) {
if( debug ) {
Serial.print("city name = ");
Serial.print("temperature = ");
Serial.println(" *C");
Serial.print("humidity = ");
Serial.println(" %");
Serial.print("pressure = ");
Serial.println(" hPa");

void disconnect() {
if( debug ) Serial.println("Disconnect from HTTP server");

void wait() {
if( debug ) Serial.println("Wait 30 seconds");

//-------- e-ink display code ----------
void setSmallText(String text, int x, int y) {
epd_disp_string(text.c_str(), x, y);

void setMediumText(String text, int x, int y) {
epd_disp_string(text.c_str(), x, y);

void setLargeText(String text, int x, int y) {
epd_disp_string(text.c_str(), x, y);

void updateDisplay(struct WeatherData* weatherData) {
int distStart=50;
setLargeText(String(weatherData->cityName), 50, distStart);
distStart += 100;
epd_draw_line(10, distStart, 500, distStart); // horizontal line
distStart += 20;
setMediumText(String(weatherData->nowDescription), 50, distStart);
distStart += 60;
epd_draw_line(10, distStart, 500, distStart); // horizontal line

distStart += 50;
setMediumText(String("temperature: ") + String(weatherData->temperature) + String(" *C"), 50, distStart);
distStart += 60;
setMediumText(String("humidity: ") + String(weatherData->humidity) + String(" %"), 50, distStart);
distStart += 60;
setMediumText(String("pressure: ") + String(weatherData->pressure) + String(" hPa"), 50, distStart);
distStart += 60;

epd_draw_line(10, distStart, 500, distStart); // horizontal line

epd_draw_line(510, 10, 510, 590); // vertical line

// display weather icon
String weatherIcon = selectWeatherIcon(weatherData);
epd_disp_bitmap(weatherIcon.c_str(), 560, 100);


String selectWeatherIcon(struct WeatherData* weatherData) {
String iconCode = String(weatherData->iconCode);
if( debug ) {
Serial.print("selectWeatherIcon: ");
if( iconCode.startsWith("01") ){
if( debug ) Serial.println("SUN.BMP");
return String("SUN.BMP");
} else if( iconCode.startsWith("02") ){
if( debug ) Serial.println("CLOUDY.BMP");
return String("CLOUDY.BMP");
} else if( iconCode.startsWith("03") ){
if( debug ) Serial.println("CLOUD.BMP");
return String("CLOUD.BMP");
} else if( iconCode.startsWith("04") ){
if( debug ) Serial.println("CLOUD.BMP");
return String("CLOUD.BMP");
} else if( iconCode.startsWith("09") ){
if( debug ) Serial.println("RAIN.BMP");
return String("RAIN.BMP");
} else if( iconCode.startsWith("10") ){
if( debug ) Serial.println("UNSETTLED.BMP");
return String("UNSET.BMP");
} else if( iconCode.startsWith("11") ){
if( debug ) Serial.println("THUNDER.BMP");
return String("THUND.BMP");
} else if( iconCode.startsWith("23") ){
if( debug ) Serial.println("SNOW.BMP");
return String("SNOW.BMP");
} else {
if( debug ) Serial.println("UNSETTLED.BMP");
return String("UNSET.BMP");


To get a quick overview off the weather or the forecast an icon does the trick better than pure text. One can design own weather icons or search thenounproject for beautiful examples. The icons have to be uploaded first to the micro SD card of the E-Ink display in the appropriate format. The manufacturer of the display explains in his wiki how to prepare the images and how to upload them on the micro SD card.

The weather icon codes returned by openweathermap are listed in this table. To display the appropriate image the codes only need to be translated.

The result can look like this:

e-ink weather display

e-ink weather display

In the end this weather display hack is a prototype which can be easily extended.









4 Gedanken zu “E-Ink Weather Display

  1. I cannot get the images to display from the sd card. I have converted them to 2bpp using the waveshare utility and copied them to the sd card, but they are not displayed. Do they need to go in a particular folder on the sd card? I have put them in root.

    Gefällt mir

    1. Have you considered the file names of the 2bpp images? I had the impression that the filenames had to be capitalised, e.g. IMG.BMP. It is possible that this is not visible using Windows Explorer.
      The length of the filenames is limited to 10 characters in total.
      Another issue is the parameter for initialisation of the e-ink display library’s method „epd_set_memory(MEM_TF)“. For usage of data from the SD card it should be set to MEM_TF.
      The images can be put directly at root level of the SD card, not necessarily in subfolders.

      Gefällt mir

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:


Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )


Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )


Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )


Verbinde mit %s