ESP 32 RSS News Feed Ticker


I had some ESP-32 DEVKITs left over and a SH1106 OLED display. I thought wouldn’t be nice to have a news ticker that would be playing away in the back ground.
I am Dutch living in the UK so my feeds are a mixture of Dutch /UK and international news but as long as you can find the RSS feed, you can display anything you like.
The source code is maintained on my GITHub pages https://github.com/japywheel/ESP-32-RSS-News-Feed-Ticker
Prerequisites:
- ESP-32 Devkit or similar
- Wifi internet connection 🙂
- SH1106 OLED screen. (Will work with different different I2C OLEDs but the code needs to be modified
- Arduino IDE
- Hookup wiring or soldering skills

Libraries
- WiFi
- WiFiClientSecure (which I think is part of WiFi library)
- HTTPClient
- ArduinoJson
- U8g2lib
Feel free to have a play
Code :
// newsticker4.ino file
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>
// WiFi credentials
const char* ssid = "Your SSD";
const char* password = "Your WIFI Password";
// RSS Feed URLs - DEFINE YOUR RSS FEEDS HERE"
const char* newsUrls[] = {
"https://www.nu.nl/rss/Algemeen",
"https://feeds.bbci.co.uk/news/rss.xml",
//"feeds.nos.nl/nosnieuwsalgemeen",
"https://feeds.skynews.com/feeds/rss/home.xml",
"https://www.reutersagency.com/feed/?best-types=reuters-news-first&post_type=best"
};
const int numFeeds = 4; // DEFINE THE NUMBER OF LIVE RSS FEEDS
int currentFeed = 0; // START RSS URL
// SH1106 Display
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
String headlines[40];
int numHeadlines = 0;
int xOffset = 128; // Start position off the right side of the display
int speed = 2.5; // Scrolling speed
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected!");
u8g2.begin();
u8g2.setFont(u8g2_font_courR14_tf); // Use font with Latin-1 support
fetchHeadlines();
}
void loop() {
u8g2.clearBuffer();
// Draw all headlines horizontally
int currentX = xOffset;
for (int i = 0; i < numHeadlines; i++) {
u8g2.drawUTF8(currentX, 40, headlines[i].c_str());
currentX += u8g2.getUTF8Width(headlines[i].c_str()) + 10;
}
u8g2.sendBuffer();
xOffset -= speed;
if (xOffset < -getTotalWidth() - 10) { // Check if all headlines have scrolled off
xOffset = 128; // Reset position
currentFeed = (currentFeed + 1) % numFeeds; // Switch feed
fetchHeadlines();
}
delay(1);
}
void fetchHeadlines() {
WiFiClientSecure client;
client.setInsecure(); // Ignore SSL certificate validation
HTTPClient http;
if (http.begin(client, newsUrls[currentFeed])) {
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
parseRSS(payload);
}
http.end();
}
}
void parseRSS(String xml) {
numHeadlines = 0;
int pos = 0;
while ((pos = xml.indexOf("<title>", pos)) >= 0 && numHeadlines < 40) {
int endPos = xml.indexOf("</title>", pos);
if (endPos > pos) {
String headline = xml.substring(pos + 7, endPos);
// Remove CDATA tags
headline.replace("<![CDATA[", "");
headline.replace("]]>", "");
// Ensure UTF-8 encoding (needed for U8g2)
headline = convertToUTF8(headline);
if (numHeadlines > 0) {
headlines[numHeadlines] = "-|- " + headline;
} else {
headlines[numHeadlines] = headline;
}
numHeadlines++;
}
pos = endPos + 8;
}
}
// Function to ensure proper UTF-8 encoding
String convertToUTF8(String str) {
str.replace("£", "\xC2\xA3"); // Pound sign
str.replace("é", "\xC3\xA9"); // é
str.replace("ë", "\xC3\xAB"); // ë
str.replace("ï", "\xC3\xAF"); // ï
return str;
}
// Function to calculate the total width of all headlines combined
int getTotalWidth() {
int totalWidth = 0;
for (int i = 0; i < numHeadlines; i++) {
totalWidth += u8g2.getUTF8Width(headlines[i].c_str()) + 10;
}
return totalWidth;
}