From 577b2e541a5cfcef4551fc69a15213b0614a9890 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Fri, 10 Feb 2023 18:14:39 +0100 Subject: [PATCH] added rss feed to content --- esp32_fw/data/fonts/GillSC16.vlw | Bin 0 -> 6726 bytes esp32_fw/data/fonts/GillSC20.vlw | Bin 0 -> 8940 bytes esp32_fw/data/www/index.html | 1 + esp32_fw/data/www/main.js | 3 +- esp32_fw/include/contentmanager.h | 10 ++++ esp32_fw/platformio.ini | 2 + esp32_fw/src/contentmanager.cpp | 89 ++++++++++++++++++++++++++---- 7 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 esp32_fw/data/fonts/GillSC16.vlw create mode 100644 esp32_fw/data/fonts/GillSC20.vlw diff --git a/esp32_fw/data/fonts/GillSC16.vlw b/esp32_fw/data/fonts/GillSC16.vlw new file mode 100644 index 0000000000000000000000000000000000000000..04ebc30c1c9263ee1d70a8355e4fcdb475532093 GIT binary patch literal 6726 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 literal 0 HcmV?d00001 diff --git a/esp32_fw/data/www/index.html b/esp32_fw/data/www/index.html index d9080f3e..0cfbbcfa 100644 --- a/esp32_fw/data/www/index.html +++ b/esp32_fw/data/www/index.html @@ -31,6 +31,7 @@ + diff --git a/esp32_fw/data/www/main.js b/esp32_fw/data/www/main.js index ad46320f..377cfa9f 100644 --- a/esp32_fw/data/www/main.js +++ b/esp32_fw/data/www/main.js @@ -1,6 +1,6 @@ const $ = document.querySelector.bind(document); -const contentModes = ["Static image", "Current date", "Counting days", "Counting hours", "Current weather", "Firmware update", "Memo text", "Image url", "Weather forecast"]; +const contentModes = ["Static image", "Current date", "Counting days", "Counting hours", "Current weather", "Firmware update", "Memo text", "Image url", "Weather forecast","RSS feed"]; const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"]; const contentModeOptions = []; contentModeOptions[0] = ["filename","timetolive"]; @@ -12,6 +12,7 @@ contentModeOptions[5] = ["filename"]; contentModeOptions[6] = ["text"]; contentModeOptions[7] = ["url","interval"]; contentModeOptions[8] = ["location"]; +contentModeOptions[9] = ["title", "url", "interval"]; const imageQueue = []; let isProcessing = false; diff --git a/esp32_fw/include/contentmanager.h b/esp32_fw/include/contentmanager.h index 0a81963a..ef6a41d0 100644 --- a/esp32_fw/include/contentmanager.h +++ b/esp32_fw/include/contentmanager.h @@ -6,6 +6,15 @@ #include "tag_db.h" #include +struct contentTypes { + uint16_t id; + String name; + uint16_t tagTypes; + void (*functionname)(); + String description; + String optionList; +}; + void contentRunner(); void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo); bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin); @@ -17,6 +26,7 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo); void drawForecast(String &filename, String location, tagRecord *&taginfo); void drawIdentify(String &filename, tagRecord *&taginfo); bool getImgURL(String &filename, String URL, time_t fetched); +bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo); char *formatHttpDate(time_t t); String urlEncode(const char *msg); int windSpeedToBeaufort(float windSpeed); diff --git a/esp32_fw/platformio.ini b/esp32_fw/platformio.ini index 77108edd..e92c1d78 100644 --- a/esp32_fw/platformio.ini +++ b/esp32_fw/platformio.ini @@ -24,5 +24,7 @@ lib_deps = bblanchon/ArduinoJson bodmer/TFT_eSPI https://github.com/Bodmer/TJpg_Decoder.git + https://github.com/garretlab/shoddyxml2 + https://github.com/Bodmer/U8g2_for_TFT_eSPI upload_port = COM12 monitor_port = COM12 diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp index 380415e3..a5d0fdf4 100644 --- a/esp32_fw/src/contentmanager.cpp +++ b/esp32_fw/src/contentmanager.cpp @@ -6,8 +6,10 @@ #include "newproto.h" #include #include +#include #include +#include "U8g2_for_TFT_eSPI.h" #include "commstructs.h" #include "makeimage.h" #include "web.h" @@ -22,6 +24,7 @@ enum contentModes { Memo, ImageUrl, Forecast, + RSSFeed, }; @@ -126,7 +129,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Forecast: drawForecast(filename, cfgobj["location"], taginfo); - taginfo->nextupdate = now + 3600; + taginfo->nextupdate = now + 3 * 3600; updateTagImage(filename, mac, 15); break; @@ -164,6 +167,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { taginfo->nextupdate = now + 300; } break; + + case RSSFeed: + + if (getRSSfeed(filename, cfgobj["url"], cfgobj["title"], taginfo)) { + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()); + updateTagImage(filename, mac, cfgobj["interval"].as()); + } else { + taginfo->nextupdate = now + 300; + } + break; } taginfo->modeConfigJson = doc.as(); @@ -333,8 +346,8 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) { initSprite(spr, 296, 128); - drawString(spr, location, 10, 10, "fonts/bahnschrift30"); - drawString(spr, String(wind), 275, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_BLACK)); + drawString(spr, location, 5, 5, "fonts/bahnschrift30"); + drawString(spr, String(wind), 280, 5, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_BLACK)); char tmpOutput[5]; dtostrf(temperature, 2, 1, tmpOutput); @@ -467,7 +480,7 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) { for (uint8_t dag = 0; dag < 5; dag++) { time_t weatherday = doc["daily"]["time"][dag].as(); struct tm *datum = localtime(&weatherday); - drawString(spr, String(weekday_name[datum->tm_wday]), dag * 59 + 30, 20, "fonts/twbold20", TC_DATUM, TFT_BLACK); + drawString(spr, String(weekday_name[datum->tm_wday]), dag * 59 + 30, 18, "fonts/twbold20", TC_DATUM, TFT_BLACK); uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); if (weathercode > 40) weathercode -= 40; @@ -479,11 +492,11 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) { spr.setTextColor(TFT_BLACK, TFT_WHITE); } spr.setTextDatum(TL_DATUM); - spr.setCursor(12 + dag * 59, 55); + spr.setCursor(12 + dag * 59, 58); spr.printToSprite(weatherIcons[weathercode]); spr.setTextColor(TFT_BLACK, TFT_WHITE); - spr.setCursor(17 + dag * 59, 24); + spr.setCursor(17 + dag * 59, 27); spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag])); spr.unloadFont(); @@ -491,10 +504,10 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) { int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as()); uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as()); - spr.loadFont("fonts/tw20", LittleFS); - drawString(spr, String(tmin) + " ", dag * 59 + 30, 105, "", TR_DATUM, (tmin < 0 ? TFT_RED : TFT_BLACK)); - drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 105, "", TL_DATUM, (tmax < 0 ? TFT_RED : TFT_BLACK)); - drawString(spr, String(" ") + String(wind), dag * 59 + 30, 40, "", TL_DATUM, (wind > 5 ? TFT_RED : TFT_BLACK)); + spr.loadFont("fonts/GillSC20", LittleFS); + drawString(spr, String(tmin) + " ", dag * 59 + 30, 108, "", TR_DATUM, (tmin < 0 ? TFT_RED : TFT_BLACK)); + drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 108, "", TL_DATUM, (tmax < 0 ? TFT_RED : TFT_BLACK)); + drawString(spr, String(" ") + String(wind), dag * 59 + 30, 43, "", TL_DATUM, (wind > 5 ? TFT_RED : TFT_BLACK)); spr.unloadFont(); if (dag>0) { for (int i = 20; i < 128; i+=3) { @@ -561,6 +574,62 @@ bool getImgURL(String &filename, String URL, time_t fetched) { return (httpCode == 200 || httpCode == 304); } +rssClass reader; + +bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo) { + // https://github.com/garretlab/shoddyxml2 + + // http://feeds.feedburner.com/tweakers/nieuws + // https://www.nu.nl/rss/Algemeen + + Serial.println("RSS feed"); + struct tm timeInfo; + char header[32]; + getLocalTime(&timeInfo); + sprintf(header, "%02d-%02d-%04d %02d:%02d", timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900, timeInfo.tm_hour, timeInfo.tm_min); + + const char *url = URL.c_str(); + const char *tag = "title"; + const int rssArticleSize = 128; + const int rssNumArticle = 8; + + TFT_eSPI tft = TFT_eSPI(); + TFT_eSprite spr = TFT_eSprite(&tft); + U8g2_for_TFT_eSPI u8f; + u8f.begin(spr); + + if (taginfo->hwType == SOLUM_29_033) { + initSprite(spr, 296, 128); + if (title=="" || title=="null") title="RSS feed"; + drawString(spr, title, 5, 3, "fonts/bahnschrift20", TL_DATUM, TFT_RED); + + u8f.setFont(u8g2_font_glasstown_nbp_tr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall + u8f.setFontMode(0); + u8f.setFontDirection(0); + u8f.setForegroundColor(TFT_BLACK); + u8f.setBackgroundColor(TFT_WHITE); + u8f.setCursor(220, 20); + u8f.print(header); + + // u8g2_font_nine_by_five_nbp_tr + // u8g2_font_7x14_tr + // u8g2_font_crox1h_tr + // u8g2_font_miranda_nbp_tr + u8f.setFont(u8g2_font_glasstown_nbp_tr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall + + int n = reader.getArticles(url, tag, rssArticleSize, rssNumArticle); + for (int i = 0; i < n; i++) { + u8f.setCursor(5, 34+i*13); // start writing at this position + u8f.print(reader.itemData[i]); + } + } + + spr2grays(spr, spr.width(), spr.height(), filename); + spr.deleteSprite(); + + return true; +} + char *formatHttpDate(time_t t) { static char buf[40]; struct tm *timeinfo;