Electronic 20 Juin 2024

[Cristal] How to display live weather on a I2C screen ?

Introduction

Together we will see how to display live weather based on GPS coordinates on an I2C screen. To obtain the information, we will use the open-meteo API system.

Programming

We will use the module ArduinoJson, so you need to install it on your project whether you are using the Arduino IDE or PlatformIO.

#ifndef METEO_H_
#define METEO_H_

#include <Arduino.h>

String init_http(void);
float get_temp(void);
int get_WMO(void);
int get_day_night(void);

#endif
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <open-meteo.h>

String url = "https://api.open-meteo.com/v1/forecast?latitude=<your-latitude>&longitude=<your-longitude>&current=temperature_2m,is_day,weather_code&forecast_days=1";

String init_http(void){
    // Utilisation de la bibliothèque HTTPClient pour effectuer une requête GET
    HTTPClient http;
    http.begin(url); // Début de la requête HTTP

    int code = http.GET(); // Effectuer la requête GET
    
    if (code > 0) { // Vérifier si la requête a réussi
    // Obtenir le contenu de la réponse
        String payload = http.getString();
        http.end();
        return payload;
    } else {
        Serial.println("Erreur lors de la requête HTTP !");
        http.end();
        return "error";
    }

}

float get_temp(void){    
    String req_rep = init_http();

    DynamicJsonDocument doc(1024); // Taille du document JSON

    // Désérialiser le JSON
    DeserializationError error = deserializeJson(doc, req_rep);

    // Vérifier s'il y a une erreur lors de la désérialisation
    if (!error) {
      // Extraire la valeur de "temperature_2m"
      float temperature = doc["current"]["temperature_2m"];  
      return temperature;
    } else {
      Serial.println("Erreur lors de la désérialisation du JSON !");
    }
}

int get_WMO(void){    
    String req_rep = init_http();

    DynamicJsonDocument doc(1024); // Taille du document JSON

    // Désérialiser le JSON
    DeserializationError error = deserializeJson(doc, req_rep);

    // Vérifier s'il y a une erreur lors de la désérialisation
    if (!error) {
      // Extraire la valeur de "weather_code"
      int wmo = doc["current"]["weather_code"];  
      return wmo;
    } else {
      Serial.println("Erreur lors de la désérialisation du JSON !");
    }
}

int get_day_night(void){    
    String req_rep = init_http();

    DynamicJsonDocument doc(1024); // Taille du document JSON

    // Désérialiser le JSON
    DeserializationError error = deserializeJson(doc, req_rep);

    // Vérifier s'il y a une erreur lors de la désérialisation
    if (!error) {
      // Extraire la valeur de "is_day"
      int dn = doc["current"]["is_day"];  
      return dn;
    } else {
      Serial.println("Erreur lors de la désérialisation du JSON !");
    }
}

We can now use these functions to display the weather interactively on the 128 x 64 screen.

#include <Arduino.h>
#include <U8g2lib.h>
#include <stdio.h>
#include <Wire.h>
#include <open-meteo.h>
#include <WiFi.h>

#define I2C_SDA 27
#define I2C_SCL 22

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE);

// 'soleil', 32x32px
const unsigned char epd_bitmap_soleil [] PROGMEM = {
	0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 
	0xf0, 0xc0, 0x03, 0x0f, 0xf0, 0xc1, 0x83, 0x0f, 0xf0, 0x01, 0x80, 0x0f, 0xf0, 0x03, 0xc0, 0x0f, 
	0xe0, 0xf3, 0xcf, 0x07, 0x80, 0xfc, 0x3f, 0x01, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 
	0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 
	0x3f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 
	0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x80, 0xfd, 0xbf, 0x01, 0xe0, 0xf3, 0xcf, 0x07, 
	0xf0, 0x03, 0xc0, 0x0f, 0xf0, 0x01, 0x80, 0x0f, 0xf0, 0xc1, 0x83, 0x0f, 0xf0, 0xc0, 0x03, 0x0f, 
	0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00
};

// 'ciel-clair', 32x32px
const unsigned char epd_bitmap_ciel_clair [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x7f, 0x00, 0xfc, 0xff, 0xff, 0x03, 0xfe, 0xff, 0xff, 0x07, 
	0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 
	0xff, 0xff, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0x07, 0xfc, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x73, 
	0xf0, 0xff, 0xff, 0x79, 0xf0, 0xff, 0xff, 0x19, 0xf0, 0xff, 0xff, 0x03, 0xf0, 0xff, 0xff, 0x03, 
	0xe0, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0xf7, 0x00, 0xf0, 0xff, 0xf3, 0x00, 0xf0, 0xff, 0x03, 
	0x00, 0xf0, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x03, 0x00, 0xee, 0xff, 0x39, 0x80, 0xcf, 0xff, 0x78, 
	0x80, 0x03, 0x7f, 0x70, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x03, 0x00, 0x70, 0x8c, 0x03, 
	0x00, 0x30, 0x0c, 0x03, 0x00, 0x30, 0x0c, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'nuage', 32x32px
const unsigned char epd_bitmap_nuage [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x7f, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 
	0xf8, 0xff, 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0x1f, 0xf0, 0xff, 0xff, 0x07, 
	0xe0, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0x07, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xf0, 0xff, 0x03, 
	0x00, 0xf0, 0xff, 0x01, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'brumeux', 32x32px
const unsigned char epd_bitmap_brumeux [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x78, 0xfe, 0xff, 0x00, 0xf8, 0xfe, 0xff, 0x00, 0x78, 0xfe, 0xff, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 
	0xf8, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 
	0x80, 0xff, 0xff, 0xf1, 0xe0, 0xff, 0xff, 0xf7, 0xf0, 0xff, 0xff, 0xef, 0xf8, 0xff, 0xff, 0x1f, 
	0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 
	0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, 
	0xf0, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0x07, 0x00, 0xf0, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x01, 
	0x00, 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'pluie-abondante', 32x32px
const unsigned char epd_bitmap_pluie_abondante [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xc7, 0x01, 0x00, 0x1c, 0xc7, 0x01, 0x00, 0x1c, 0xc7, 0x01, 
	0x00, 0x1c, 0xc7, 0x01, 0xc0, 0x71, 0x1c, 0x07, 0xc0, 0x71, 0x1c, 0x07, 0xc0, 0x71, 0x1c, 0x07, 
	0xc0, 0x71, 0x1c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x0f, 
	0xf8, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 
	0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 
	0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x1f, 
	0xfc, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0x00, 0xfc, 0xff, 0x03, 
	0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'flocon-de-neige', 32x32px
const unsigned char epd_bitmap_flocon_de_neige [] PROGMEM = {
	0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xdc, 0x3b, 0x00, 0x00, 0xfc, 0x3f, 0x00, 
	0xc0, 0xfd, 0xbf, 0x03, 0xc0, 0xf9, 0x9f, 0x03, 0xc0, 0xe1, 0x87, 0x03, 0xdc, 0xc1, 0x83, 0x3b, 
	0xfc, 0xc3, 0xc3, 0x3f, 0xfc, 0xc3, 0xc3, 0x3f, 0xf8, 0xc3, 0xc3, 0x1f, 0xf8, 0xcf, 0xf3, 0x1f, 
	0xfe, 0xff, 0xff, 0x7f, 0x7e, 0xff, 0xff, 0x7e, 0x1e, 0xfc, 0x3f, 0x78, 0x00, 0xf0, 0x0f, 0x00, 
	0x00, 0xf0, 0x0f, 0x00, 0x1e, 0xfc, 0x3f, 0x78, 0x7e, 0xff, 0xff, 0x7e, 0xfe, 0xff, 0xff, 0x7f, 
	0xf8, 0xcf, 0xf3, 0x1f, 0xf8, 0xc3, 0xc3, 0x1f, 0xfc, 0xc3, 0xc3, 0x3f, 0xfc, 0xc3, 0xc3, 0x3f, 
	0xdc, 0xc1, 0x83, 0x3b, 0xc0, 0xe1, 0x87, 0x03, 0xc0, 0xf9, 0x9f, 0x03, 0xc0, 0xfd, 0xbf, 0x03, 
	0x00, 0xfc, 0x3f, 0x00, 0x00, 0xdc, 0x3b, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00
};

// 'orage', 32x32px
const unsigned char epd_bitmap_orage [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 
	0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x1f, 0x00, 
	0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0xc0, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x1f, 
	0xf8, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 
	0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 
	0xfc, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x03, 
	0xe0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x00, 0xc0, 0xff, 0x39, 0x00, 
	0x80, 0xff, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'lunes', 32x32px
const unsigned char epd_bitmap_lune [] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xff, 0x3f, 0x00, 
	0x80, 0xff, 0xff, 0x00, 0xe0, 0xff, 0xff, 0x01, 0xf0, 0xff, 0xff, 0x03, 0xf8, 0xff, 0xff, 0x07, 
	0xf8, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xff, 0x1f, 
	0x1c, 0xf0, 0xff, 0x1f, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0x3f, 
	0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 
	0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf0, 0x1f, 
	0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xf8, 0x03, 
	0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

class Meteo_aff {
  public :
  int wmo_code, dn_cond;
  float temp;
  mutable const char* location;

  void print_4user(){
    u8g2.begin();
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_helvB18_te);
    u8g2.setFontDirection(2);
    u8g2.enableUTF8Print();
    char buffer_te[8];
    snprintf(buffer_te, sizeof(buffer_te), "%.1f°C", temp);
    u8g2.drawUTF8(70,5,buffer_te);

    u8g2.setFont(u8g2_font_helvB14_te);
    u8g2.drawUTF8(125,40,location);

    u8g2.setBitmapMode(1);

    if (wmo_code == 0 && dn_cond == 1){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_soleil);
    }
    else if ((wmo_code == 0 || wmo_code == 1 || wmo_code == 2) && dn_cond == 0){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_lune);
    }
    else if ((wmo_code == 1 || wmo_code == 2) && dn_cond == 1){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_ciel_clair);
    }
    else if (wmo_code == 3){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_nuage);
    }
    else if (wmo_code == 45 || wmo_code == 48 || wmo_code == 51 || wmo_code == 53 || wmo_code == 55 || wmo_code == 56 || wmo_code == 57){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_brumeux);
    }
    else if (wmo_code == 61 || wmo_code == 63 || wmo_code == 65 || wmo_code == 66 || wmo_code == 67 || wmo_code == 80 || wmo_code == 81 || wmo_code == 82){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_pluie_abondante);
    }
    else if (wmo_code == 71 || wmo_code == 73 || wmo_code == 75 || wmo_code == 77 || wmo_code == 85 || wmo_code == 86){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_flocon_de_neige);
    }
    else if (wmo_code == 95 || wmo_code == 96 || wmo_code == 99){
      u8g2.drawXBM(85, 2, 32, 32, epd_bitmap_orage);
    }

    u8g2.sendBuffer();
  }

  void setLocation(const char* newLocation) const {
    location = newLocation;
  }
};

Meteo_aff m1;
m1.temp =  get_temp();
m1.dn_cond = get_day_night();
m1.wmo_code = get_WMO();
m1.setLocation("Brest");
m1.print_4user();
delay(10000);