Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
RSSリーダの実験(ESP-WROOM-32)

概要

Arduino core for the ESP32を使った、RSSリーダの実験です。

簡単にインターネット接続ができるようになったので、とりあえず、ネット上の情報を表示するものとして、RSSリーダのようなものを作ってみました。

有機ELキャラクタディスプレイに表示するので、日本語は表示できませんが、それっぽいものはできました。

RSSドキュメントを解析するために、手抜きのSAX"風"XMLパーサも作ってみました。

Arduino core for the ESP32のインストールのページはこちら

有機ELキャラクタディスプレイの実験のページはこちら

実験

RSSの解析

RSSというか、XMLを解析するために、手抜きのSAX風XMLパーサを作ってみました。SAX"風"なので、実際にはSAXでもなんでもない、いい加減なものです。ヘッダファイルを入れても、400行程度です。"風"なので、APIの名前も、パラメータも異なるものにしています。

CNNとBBCのRSSしか見ていませんが、コメントは入ってなかったので、コメントの処理はできていません。また、タグ名の最大長は32文字(終端ナル文字含む)です(ソースをいじれば、変更可能)。XML文書にエラーがある場合は、どのように動くかわかりません。また、属性も無視しています。

以下のコールバック関数を用意しました。XMLも詳しくはないし、用語の名前とか意味も勘違いしているかもしれません。あくまで、今回の用途には使えるということです。

関数 説明 パラメータ 備考
foundXMLDecl() XMLドキュメントの開始 なし
foudXMLEnd() XMLドキュメントの終了 なし
foundPI Processing Instructionを見つけた。 Processing Instructionの名前
foundSTag 開始タグを見つけた タグ名
foundETag 終了タグを見つけた タグ名
foundSection "<!名称"を見つけた。 名称 試験していません。
foundCharacter 文字を見つけた 文字 タグ外の文字、CDATA内の文字。1文字ごとに通知する。

また、このパーサは、1文字ずつ文字を読み込むようになっています。このための、1文字を読み込む関数も用意する必要があります。最後は、EOFを返す必要があります。今回は、httpGetChar()という関数が該当します。XMLパーサ用のインスタンスを作成するときに、指定します。

parse()というメソッドを呼ぶと、1文字ずつ読み出し、EOFを読み込むまで、処理を続けます。

スケッチ

有機ELキャラクタディスプレイの1行目には固定文字列、2行目は取得した情報を表示するようにしています。情報のバッファリングはほとんどしていない(ディスプレイに表示する20文字分だけ保持)、ウェブサーバに対しては優しくない設計になっています。このため、読み取り中にタイムアウトが発生する可能性もあります。

10行目から22行目あたりを、各自の環境に合わせて、変更してください。contentsToDisplayは、表示したい要素名を指定します。とりあえず、BBCとCNNのRSSは読み込むことができました。

OLED用のライブラリはこちらから、XMLパーサはこちらからダウンロードできます。OLED用のライブラリについては、こちらのページも参照してください。

#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 = "your ssid";
const char *password = "your 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) {
  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 foundCharacter(char c) {
  if ((itemDepth == 1) && (lastTagMatches == 1)) {
    displayPutChar(c);
    printDisplayBuffer();
    delay(200);
  }
}

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:
  delay(5000);

  oled.begin(DISPLAY_WIDTH, DISPLAY_HEIGHT);

  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.foundCharacter = foundCharacter;
}

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);
    }
  }
}

参考サイト

ArduinoでXMLを解析する」を参考にしました。

バージョン

Arduino 1.8.2/Arduino core for the ESP32/ESP-WROOM-32



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2017 garretlab all rights reserved.
inserted by FC2 system