diff --git a/esp32_fw/data/fonts/GillSC16.vlw b/esp32_fw/data/fonts/GillSC16.vlw new file mode 100644 index 00000000..04ebc30c Binary files /dev/null and b/esp32_fw/data/fonts/GillSC16.vlw differ diff --git a/esp32_fw/data/fonts/GillSC20.vlw b/esp32_fw/data/fonts/GillSC20.vlw new file mode 100644 index 00000000..80d75f5e Binary files /dev/null and b/esp32_fw/data/fonts/GillSC20.vlw differ 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;