summaryrefslogtreecommitdiff
path: root/src/weather.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/weather.cpp')
-rw-r--r--src/weather.cpp226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/weather.cpp b/src/weather.cpp
new file mode 100644
index 0000000..e38e44f
--- /dev/null
+++ b/src/weather.cpp
@@ -0,0 +1,226 @@
+#include <GxEPD2_BW.h>
+#include <U8g2_for_Adafruit_GFX.h>
+#include <HTTPClient.h>
+#include <ArduinoJson.h>
+
+#include "weather-icons.h"
+#include "secrets.h"
+
+/* Position for Durlach */
+#define lat 48.999
+#define lon 8.474
+
+#define FONTSIZE 10
+#define SPACING 5
+
+#define DIM(w, h) (((w) << 16) | (h))
+#define GETW(dim) ((dim) >> 16)
+#define GETH(dim) ((dim) & 0xffff)
+
+extern GxEPD2_GFX_BASE_CLASS &get_display();
+extern U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
+
+static StaticJsonDocument<16000> weather_json;
+static DeserializationError json_state = DeserializationError::IncompleteInput;
+
+void fetch_weather() {
+ /* use HTTP intentionally for simplicity
+ don't use on insecure networks */
+ String url = "http://api.openweathermap.org/data/2.5/onecall?units=metric&lang=de&exclude=minutely,hourly";
+ url += "&appid=" + String(openweathermap_apikey);
+ url += "&lat=" + String(lat);
+ url += "&lon=" + String(lon);
+
+ HTTPClient http;
+ http.begin(url);
+ int status = http.GET();
+
+ String weather_info = "";
+ if (status == HTTP_CODE_OK) {
+ weather_info = http.getString();
+ } else {
+ Serial.println("http error\r\n");
+ }
+ http.end();
+ json_state = deserializeJson(weather_json, weather_info);
+ if (json_state != DeserializationError::Ok) {
+ Serial.println("json deserialization error\r\n");
+ }
+}
+
+static uint32_t draw_icon(int16_t x, int16_t y, const unsigned char *image, uint16_t width, uint16_t height) {
+ get_display().drawXBitmap(x, y, image, width, height, GxEPD_BLACK);
+ return DIM(width, height);
+}
+
+static uint32_t draw_weather_icon(int16_t x, int16_t y, int code) {
+ const unsigned char *image = NULL;
+ uint16_t width = 0, height = 0;
+
+ int icon_group = code / 100;
+ switch (icon_group) {
+ case 2:
+ image = thunderstorm_bits;
+ width = thunderstorm_width;
+ height = thunderstorm_height;
+ break;
+ case 3:
+ case 5:
+ image = rain_bits;
+ width = rain_width;
+ height = rain_height;
+ break;
+ case 6:
+ image = snow_bits;
+ width = snow_width;
+ height = snow_height;
+ break;
+ case 7:
+ image = fog_bits;
+ width = fog_width;
+ height = fog_height;
+ break;
+ case 8:
+ image = cloudy_bits;
+ width = cloudy_width;
+ height = cloudy_height;
+ break;
+ }
+ if (code == 800 || !image) {
+ /* special case, clear sky / no clouds */
+ image = sunny_bits;
+ width = sunny_width;
+ height = sunny_height;
+ }
+
+ return draw_icon(x, y, image, width, height);
+}
+
+static uint32_t draw_temperature(int16_t x, int16_t y, float temp, float temp_max = 0.0) {
+ uint16_t w = thermometer_width, h = thermometer_height;
+ int text_width = 0;
+ draw_icon(x, y, thermometer_bits, w, h);
+ u8g2Fonts.setCursor(x + w + SPACING, y + h/2 + FONTSIZE/2);
+ if (temp_max > 0) {
+ u8g2Fonts.printf("%.1f - %.1f °C", temp, temp_max);
+ text_width = u8g2Fonts.getUTF8Width("88.8 - 88.8 °C");
+ } else {
+ u8g2Fonts.printf("%.1f °C", temp);
+ text_width = u8g2Fonts.getUTF8Width("88.8 °C");
+ }
+
+ return DIM(w + SPACING + text_width, h);
+}
+
+static uint32_t draw_humidity(int16_t x, int16_t y, int humidity) {
+ uint16_t w = humidity_width, h = humidity_height;
+ draw_icon(x, y, humidity_bits, w, h);
+ u8g2Fonts.setCursor(x + w + SPACING, y + h/2 + FONTSIZE/2);
+ u8g2Fonts.printf("%d %%", humidity);
+
+ return DIM(w, h);
+}
+
+static uint32_t draw_rain_prob(int16_t x, int16_t y, uint8_t rain) {
+ uint16_t w = raindrops_width, h = raindrops_width;
+ draw_icon(x, y, raindrops_bits, w, h);
+ u8g2Fonts.setCursor(x + w + SPACING, y + h/2 + FONTSIZE/2);
+ u8g2Fonts.printf("%d %%", rain);
+ int text_width = u8g2Fonts.getUTF8Width("88 %");
+
+ return DIM(w + SPACING + text_width, h);
+}
+
+static uint32_t draw_pressure(int16_t x, int16_t y, int pressure) {
+ uint16_t w = barometer_width, h = barometer_height;
+ draw_icon(x, y, barometer_bits, w, h);
+ u8g2Fonts.setCursor(x + w + SPACING, y + h/2 + FONTSIZE/2);
+ u8g2Fonts.printf("%d hPa", pressure);
+
+ return DIM(w, h);
+}
+
+static uint32_t draw_clouds(int16_t x, int16_t y, int clouds) {
+ uint16_t w = cloud_width, h = cloud_height;
+ draw_icon(x, y, cloud_bits, w, h);
+ u8g2Fonts.setCursor(x + w + SPACING, y + h/2 + FONTSIZE/2);
+ u8g2Fonts.printf("%d %%", clouds);
+ int text_width = u8g2Fonts.getUTF8Width("88 %");
+
+ return DIM(w + SPACING + text_width, h);
+}
+
+static int draw_weather_forecast(int16_t x, int16_t y, const char *title, const JsonObject &json) {
+ auto temp_min = json["temp"]["min"].as<float>();
+ auto temp_max = json["temp"]["max"].as<float>();
+ auto pop = (uint8_t) (100 * json["pop"].as<float>());
+ auto cloudiness = json["clouds"].as<int>();
+ auto description = json["weather"][0]["description"].as<char *>();
+
+ /* line 1 */
+ u8g2Fonts.setFont(u8g2_font_helvB10_tf);
+ u8g2Fonts.setCursor(x, y);
+ u8g2Fonts.print(title);
+ u8g2Fonts.setFont(u8g2_font_helvR10_tf);
+ u8g2Fonts.print(" ");
+ u8g2Fonts.print(description);
+
+ /* line 2 */
+ uint32_t dim = 0;
+ y += SPACING;
+ dim = draw_temperature(x, y, temp_min, temp_max);
+ x += GETW(dim) + SPACING;
+ dim = draw_clouds(x, y, cloudiness);
+ x += GETW(dim) + SPACING;
+ dim = draw_rain_prob(x, y, pop);
+
+ return DIM(0, FONTSIZE + GETH(dim) + 2 * SPACING);
+}
+
+void draw_weather(int16_t x0, int16_t y0) {
+ if (json_state != DeserializationError::Ok)
+ return;
+
+ /* current weather */
+ auto current_temp = weather_json["current"]["temp"].as<float>();
+ auto current_weather_id = weather_json["current"]["weather"][0]["id"].as<int>();
+ auto current_weather_desc = weather_json["current"]["weather"][0]["description"].as<char *>();
+ auto current_clouds = weather_json["current"]["clouds"].as<int>();
+ auto current_humidity = weather_json["current"]["humidity"].as<int>();
+ auto current_pressure = weather_json["current"]["pressure"].as<int>();
+
+ if (!current_weather_desc)
+ current_weather_desc = "N/A";
+
+ u8g2Fonts.setFont(u8g2_font_helvR10_tf);
+
+ uint32_t dim = 0;
+ int16_t x1 = x0, y1 = y0;
+ dim = draw_weather_icon(x0, y0, current_weather_id);
+ x1 += GETW(dim) + SPACING;
+ y1 += GETH(dim) + SPACING;
+
+ /* line 1 */
+ int16_t x = x1, y = y0 + 2 * SPACING;
+ uint32_t temp_dim = draw_temperature(x, y, current_temp);
+ x += GETW(temp_dim) + 2 * SPACING;
+ dim = draw_clouds(x, y, current_clouds);
+ y += GETH(dim);
+
+ /* line 2 */
+ x = x1;
+ dim = draw_humidity(x, y, current_humidity);
+ x += GETW(temp_dim) + 2 * SPACING;
+ dim = draw_pressure(x, y, current_pressure);
+ y += GETH(dim);
+
+ /* forecasts */
+ x = x0;
+ y = y1;
+ dim = draw_weather_forecast(x, y, "Heute:", weather_json["daily"][0]);
+ y += GETH(dim);
+ dim = draw_weather_forecast(x, y, "Morgen:", weather_json["daily"][1]);
+ y += GETH(dim);
+ dim = draw_weather_forecast(x, y, "Übermorgen:", weather_json["daily"][2]);
+ y += GETH(dim);
+}