RSS Reader
ESP32

Introduction

This is an experiment of RSS reader using Arduino core for the ESP 32 and OLED character display.

Using ESP-WROOM-32, it is very easy to connect to the Internet. I made an RSS reader, which displays information to an OLED character display.

To analyze an RSS document, I made a shoddy SAX-“like” XML parser, it’s shoddy…

Experiment

Analyzing RSS

I made a shoddy SAX-“like” XML parser. As it is shoddy, it is not a SAX.

The maximum length of tag name is 32, this can be configured. And if the XML document contains error, I am not sure how it works.

I prepared callback functions below. I am not an XML expert, terminology and/or meaning of them may be wrong.

Function Description Parameter Remarks
foundXMLDecl() Beginning of an XML document. none
foudXMLEnd() End of an XML document. none
foundPI() Found a Processing Instruction. Name of Processing Instruction.
foundSTag() Found a start tag. Name of tag, number of attributes, attributes
foundETag() Found an end tag. Name of tag
foundEmptyElemTag() Found an empty element tag. Name of tag, number of attributes, attributes
foundSection() Found “<!name”. Name not tested
beginCharacter() Beginning of characters. none
foundCharacter() Found a character. Character A character outside of tag or inside of CDATA. This is called by each character.
endCharacter() End of Characters. none

As this parser reads one character at a time, we need to make a function to return a character. At the end of document, it needs to return EOF. The httpGetChar() function below implements it.

Calling a parser() method, it continues to read each one character until it reads EOF, before the parser() function returns.

Sketch

At the first row a fixed string is displayed and at the second row information is displayed. As few buffering is made(only 20 characters to display), sometimes a timeout may occusr while connecting to the web site.

Please edit lines 10 to 22 to fit your environment. The sketch below read CNN(title) and BBC(description)

The OLED character display I used is here(in Japanese).

You can download OLED library here and XML parser here.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "SO2002A_I2C.h"
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include "shoddyxml.h"
 
#define DISPLAY_WIDTH 20
#define DISPLAY_HEIGHT 2
 
const char *ssid = "YOURSSID";
const char *password = "PASSWORD";
const struct site_t {
  char *title;
  char *url;
  char *contentsToDisplay;
} sites[] = {
  {"CNN.com", "http://rss.cnn.com/rss/edition.rss", "title"},
  {"BBC News", "http://feeds.bbci.co.uk/news/rss.xml", "description"},
};
const int delayPerCharacter = 200;
const int delayPerArticle = 1000;
const int delayPerRSS = 10000;
const char label = 0xfc;
 
int itemDepth = 0;
int lastTagMatches = 0;
char displayBuffer[DISPLAY_WIDTH + 1];
char *contentsToDisplay;
 
int httpGetChar();
 
WiFiMulti wifiMulti;
HTTPClient http;
WiFiClient *stream;
SO2002A_I2C oled(0x3c);
shoddyxml x(httpGetChar);
 
void clearDisplayBuffer() {
  for (int i = 0; i < DISPLAY_WIDTH + 1; i++) {
    displayBuffer[i] = ' ';
  }
  displayBuffer[DISPLAY_WIDTH - 1] = label;
}
 
void displayPutChar(char c) {
  displayBuffer[DISPLAY_WIDTH] = c;
  for (int i = 0; i < DISPLAY_WIDTH; i++) {
    displayBuffer[i] = displayBuffer[i + 1];
  }
}
 
void printDisplayBuffer() {
  for (int i = 0; i < DISPLAY_WIDTH; i++) {
    oled.setCursor(i, 1);
    oled.print(displayBuffer[i]);
  }
}
 
void foundXMLDeclOrEnd() {
 
}
 
void foundPI(char *s) {
 
}
 
void foundSTag(char *s, int numAttributes, attribute_t attributes[]) {
  if (strcmp(s, "item") == 0) {
    itemDepth++;
  }
 
  if (strcmp(s, contentsToDisplay) == 0) {
    lastTagMatches = 1;
  } else {
    lastTagMatches = 0;
  }
}
 
void foundETag(char *s) {
  if ((itemDepth == 1) && (strcmp(s, contentsToDisplay) == 0)) {
    for (int i = 0; i < DISPLAY_WIDTH; i++) {
      displayPutChar(' ');
      printDisplayBuffer();
      delay(delayPerCharacter);
    }
 
    clearDisplayBuffer();
    delay(delayPerArticle);
  }
  if (strcmp(s, "item") == 0) {
    itemDepth--;
  }
}
 
void foundEmptyElemTag(char *s, int numAttributes, attribute_t attributes[]) {
 
}
 
void foundCharacter(char c) {
  if ((itemDepth == 1) && (lastTagMatches == 1)) {
    displayPutChar(c);
    printDisplayBuffer();
    delay(200);
  }
}
 
void foundElement(char *s) {
 
}
 
int httpGetChar() {
  if (http.connected()) {
    if (stream->available()) {
      return stream->read();
    } else {
      return 0;
    }
  }
  return EOF;
}
 
void setup() {
  // put your setup code here, to run once:
  oled.begin(DISPLAY_WIDTH, DISPLAY_HEIGHT);
  oled.clear();
 
  /*
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
  */
 
  wifiMulti.addAP(ssid, password);
 
  clearDisplayBuffer();
 
  x.foundXMLDecl = foundXMLDeclOrEnd;
  x.foundXMLEnd = foundXMLDeclOrEnd;
  x.foundPI = foundPI;
  x.foundSTag = foundSTag;
  x.foundETag = foundETag;
  x.foundEmptyElemTag = foundEmptyElemTag;
  x.foundCharacter = foundCharacter;
  x.foundElement = foundElement;
}
 
void loop() {
  for (int i = 0; i < sizeof(sites) / sizeof(struct site_t); i++) {
    if ((wifiMulti.run() == WL_CONNECTED)) {
      itemDepth = 0;
      lastTagMatches = 0;
 
      oled.clear();
      oled.setCursor(0, 0);
      oled.print(sites[i].title);
      contentsToDisplay = sites[i].contentsToDisplay;
      http.begin(sites[i].url);
      int httpCode = http.GET();
      if (httpCode > 0) {
        if (httpCode == HTTP_CODE_OK) {
          stream = http.getStreamPtr();
          x.parse();
        }
      }
      http.end();
      delay(delayPerRSS);
    } else {
      wifiMulti.addAP(ssid, password);
    }
  }
}

Version

Hardware:ESP-WROOM-32
Software:Arduino 1.8.4/Arduino core for the ESP32

Last Update

November 26, 2022

inserted by FC2 system