ESP32 と 有機EL SSD1331 で Yahoo News 、 天気予報 、 NTP 時計 Wi-FI ガジェットを作ってみた

ESP32,OLED,ニュース、天気予報写真 ESP32 ( ESP-WROOM-32 )

Arduino IDE スケッチの入力

では、いよいよ以下のスケッチを入力してみてください。
大分簡単なスケッチになっていると思います。
ライブラリ化するのには苦労しました・・・。

ブラウザからスケッチをコピーしたら、必ず一旦保存してください。そうしないと正しく表示されませんので要注意!!

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include "TimeLib.h" //Arduino time library ver1.5
#include "ESP32_SD_SSD1331_Gadgets.h"
#include "ESP32_SD_ShinonomeFNT.h"
#include "ESP32_SD_UTF8toSJIS.h"
#include "ESP32_SSD1331.h"
#include "ESP32_WebGet.h"

const char* ssid = "xxxx"; //ご自分のルーターのSSIDに書き換えてください
const char* password = "xxxx"; //ご自分のルーターのパスワードに書き換えてください

const char* UTF8SJIS_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* Shino_Zen_Font_file = "/font/shnmk16.bdf"; //全角フォントファイル名を定義
const char* Shino_Half_Font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義
const char* MyFont_file = "/font/MyFont.fnt"; //自作フォントファイル名を定義

const uint8_t CS_SD = 5; //SD card CS ( Chip Select )

const uint8_t SCLK_OLED =  14; //SCLK
const uint8_t MOSI_OLED =  13; //MOSI (Master Output Slave Input)
const uint8_t MISO_OLED =  12; //これは実際は使っていない。MISO (Master Input Slave Output)
const uint8_t CS_OLED = 15;
const uint8_t DC_OLED =  16; //OLED DC(Data/Command)
const uint8_t RST_OLED =  4; //OLED Reset

ESP32_SD_SSD1331_Gadgets ESSG;
ESP32_SD_UTF8toSJIS u8ts;

ESP32_SSD1331 _ssd1331(SCLK_OLED, MISO_OLED, MOSI_OLED, CS_OLED, DC_OLED, RST_OLED);
ESP32_SD_ShinonomeFNT _SFR(CS_SD, 24000000);
ESP32_WebGet _EWG;

uint8_t Font_buf[2][16] = {0}; //フォントを格納するためのもの

uint8_t sj_txt[1024]; //Shift_JIS コード文字列を格納
uint16_t sj_length; //Shift_JIS コード文字列長

//-----ニュース記事文字列スクロール変数の初期化--------
uint8_t scl_cnt = 0; //pixelのスクロールカウント数
uint16_t SJ_cnt = 0; //Shift_JISコードのカウント数
uint8_t zen_or_han = 0; //全角の場合2, 半角の場合1
bool fnt_read_ok = true; //フォント読み込みOKかどうかのフラグ
uint32_t scl_LastTime = 0;
uint16_t Scrolle_speed = 10; //文字列スクロールインターバル時間

//-----Yahoo記事取得引数初期化-------------------------
uint64_t NewsGetLastTime = 0;
uint64_t WeatherGetLastTime = 0;
bool News_first_get = true;
bool Weather_first_get = true;

//*****************セットアップ******************************
void setup() {
  delay(1000); //ESP32が起動するまで待つ
  Serial.begin(115200);
  _EWG.EWG_AP_Connect(ssid, password); //Wi-Fi ルーターと接続
  delay(1000);

  _ssd1331.SSD1331_Init(); //OLED SSD1331 初期化

  _ssd1331.CommandWrite(0xAE); //Set Display OFF
  _ssd1331.Brightness_FadeOut(0); //明るさをカットオフ

  for(int i=0; i<63; i=i+5){
    _ssd1331.Drawing_Line(95-i, 0, 0, i, 0, i, 31);
    _ssd1331.Drawing_Line(i, 63, 95, 63-i, 0, i, 31);
  }

  _SFR.SD_Shinonome_Init3F(UTF8SJIS_file, Shino_Half_Font_file, Shino_Zen_Font_file); //ライブラリ初期化。3ファイル同時に開く
  ESSG.Gadgets_MyFont_Init(MyFont_file);
  delay(10);
  uint8_t Red = 7; //0-7
  uint8_t Green = 0; //0-7
  uint8_t Blue = 0; //0-3
  uint8_t Sino_font_buf[16][16] = {0}; //東雲(しののめ)フォントバッファ
  sj_length = _SFR.StrDirect_ShinoFNT_readALL("ESP32 日本語", Sino_font_buf);
  _ssd1331.SSD1331_8x16_Font_DisplayOut(12, 0, 32, Red, Green, Blue, Sino_font_buf);

  _ssd1331.CommandWrite(0xAF); //Set Display On
  delay(110);
  _ssd1331.Brightness_FadeIn(5); //明るさ5msずつフェードイン

  _EWG.EWG_NTP_init(9, "time.windows.com"); //NTPサーバーから時刻を取得

  setSyncProvider(EWG_Get_Ntp_Time);
  setSyncInterval(1800); //補正間隔を秒単位で決める
  delay(2000); //時刻取得後、動作安定させるためにしばらく待つ
  NTP_OtherServerSelect(9, 300); //時刻取得できなかった場合他のサーバーを試す関数

  _ssd1331.Brightness_FadeOut(5); //明るさ5msずつフェードアウト
  _ssd1331.Display_Clear(0, 0, 95, 63);
  _ssd1331.Brightness_FadeIn(0); //明るさ最大値でカットイン

  NewsGetLastTime = millis();
  WeatherGetLastTime = millis();
  scl_LastTime = millis();
}
//************* メインループ ****************************************

void loop() {
  YahooNewsGadget(0, 600000, 0, 7, 0); //600秒(10分毎)にニュース記事取得
  YahooWeatherGadget(48, 333000); //333秒毎に天気取得
  YMDW_Gadget(16, 3, 3, 3, 7, 7, 3);
  NTPwatch_Gadget(32, 50, 15, 7, 0, 1); //この関数は最後に置いた方が初回起動時がスムース
}

//************** Yahoo RSS ニュースガジェット ************************
void YahooNewsGadget(uint8_t y0, uint32_t get_interval, uint8_t Red, uint8_t Green, uint8_t Blue){
  if((News_first_get == true) || ((millis() - NewsGetLastTime) > get_interval)){
    char Web_h[3], Web_m[3];
    sprintf(Web_h, "%02d", hour());//ゼロを空白で埋める場合は%2dとする
    sprintf(Web_m, "%02d", minute());
    String news_str = "◆ " + String(Web_h) + ":" + String(Web_m) + " ";
    news_str += _EWG.EWG_https_Web_Get("news.yahoo.co.jp", "/rss/topics/top-picks.xml", '\n', "</rss>", "<title>", "</title>", "◆ ");
    Serial.print("News Get = "); Serial.println( news_str );

    sj_length = u8ts.UTF8_to_SJIS(news_str, sj_txt);
    scl_cnt = 0; //pixelのスクロールカウント数
    SJ_cnt = 0; //Shift_JISコードのカウント数
    NewsGetLastTime = millis();
    News_first_get = false;
  }

  if(millis() - scl_LastTime > Scrolle_speed){
    if(fnt_read_ok == true){
      zen_or_han = _SFR.Sjis_inc_FntRead(sj_txt, sj_length, &SJ_cnt, Font_buf);
    }
    fnt_read_ok = _ssd1331.Scroller_8x16_RtoL4line(y0, 0, zen_or_han, Font_buf, Red, Green, Blue);
    scl_LastTime = millis();
  }
}
//************** 年月日、自作フォント曜日表示ガジェット ***************
void YMDW_Gadget(uint8_t y0, uint8_t YMD_red, uint8_t YMD_green, uint8_t YMD_blue, uint8_t week_red, uint8_t week_green, uint8_t week_blue){
  ESSG.ShinonomeClock_YMD(0, y0, YMD_red, YMD_green, YMD_blue);
  ESSG.MyFontClock_Weekday(80, y0, week_red, week_green, week_blue);
}
//************** NTP時計表示ガジェット ********************************
void NTPwatch_Gadget(uint8_t y0, uint8_t HM_interval, uint8_t S_interval, uint8_t red, uint8_t green, uint8_t blue){
  ESSG.MyFont_HM_Clock(0, y0, 'H', 'V', 'H', 'V', 0, 0, 1, 1, HM_interval, red, green, blue);
  ESSG.Shinonome_Sec_Clock(80, y0, 'H', 'V', 0, 0, S_interval, red, green, blue);
}
//************** Yahoo RSS 天気予報ガジェット *************************
void YahooWeatherGadget(uint8_t y0, uint32_t get_interval){
  if((Weather_first_get == true) || ((millis() - WeatherGetLastTime) > get_interval)){
    String weather_str = _EWG.EWG_https_Web_Get("rss-weather.yahoo.co.jp", "/rss/days/4410.xml",  '>', "</rss", "】 ", " - ", "|");
    Serial.print("Weather forecast = "); Serial.println(weather_str);
    ESSG.YahooJ_Weather_TodayTomorrow(y0, weather_str);
    delay(100);
    WeatherGetLastTime = millis();
    Weather_first_get = false;
  }
}
//************ NTP server 取得出来ない場合、別サーバーを選ぶ **********
void NTP_OtherServerSelect(uint8_t timezone, uint32_t set_interval){
  if(year() < 2017){
    const char *ntpServerName[6] = {
      "time.nist.gov",
      "time-a.nist.gov",
      "time-b.nist.gov",
      "time-nw.nist.gov",
      "time-a.timefreq.bldrdoc.gov",
      "time.windows.com"
    };
    for( int i=0; i<6; i++ ){
      if(year() >= 2017) break;
      _EWG.EWG_NTP_init(timezone, ntpServerName[i]);
      setSyncProvider(EWG_Get_Ntp_Time);
      setSyncInterval(set_interval); //補正間隔を秒単位で決める
      delay(2000);
    }
    if(year() < 2017){
      Serial.println("------ ALL NTP Server Disconnection");
    }
  }
}

【解説】

●1行目:
Arduino IDE 標準 Time ライブラリです。2017/6/8時点でver 1.5 になっています。
hour(); minute(); などの関数が使えるようになります。

●2-6行目:
私の自作ライブラリのインクルードです。
ここまで全部インクルードしなくても良いのですが、何のライブラリを使っているかが一目で分かるために敢えてインクルードしています。
2重インクルード防止策は施しています。

●8-9行目:
ここのxxxx のところを、ご自分の Wi-Fi ルーターの SSID とパスワードに書き換えてください。

●11-14行:
micro SDHC カードの /font/ フォルダにコピーしたフォントファイル類を定義します。

●16-23行:
micro SDHC カードスロットと 有機EL ( OLED ) SSD1331 のGPIO 設定です。
micro SDHC カードスロットは VSPI 接続。
OLED SSD1331 は HSPI 接続です。
HSPI と VSPI については以下の記事を参照してください。

ESP32 の SPI_MODE が修正。HSPI , VSPI , 複数SPIデバイス制御 , SPI高速化などについて

●25-26 行:
自作ライブラリのクラス名を宣言します。
ESSG や u8ts は好きな名前に設定できます。

●28-30行:
ここは重要です。
ガジェットライブラリを作るにあたって、ライブラリソースファイルとArduino IDE スケッチのソースファイルとで、クラス名を共有する必要が出てきたため、以下のクラス名は変えないでください。

_ssd1331
_SFR
_EWG

これらは、ライブラリ内で extern を指定して、外部ファイルでも共有できるようにしています。
もっと良い方法があるかも知れませんので、もし、アイデアがあったらコメント等でご連絡いただけると幸いです。

●34行:
Web から取得したニュース記事文字列は UTF-8 コードなので、それを Shift_JIS に変換した文字列をここに格納します。

●38-43行:
ニュース記事を電光掲示板風スクロールするための変数初期化です。

●46-49行:
ニュース記事を取得するタイミングを設定する変数初期化です。

●55行:
ここで、Wi-Fi ルーター(アクセスポイント)と接続します。
_EWG という共通クラス名がついているので、注意してください。

●58行:
有機EL ( OLED ) SSD1331 の初期化です。
_ssd1331 という共通クラス名が付いているので、注意してください。

●60-61行:
60行のコマンドで SSD1331 ディスプレイをOFF にして、61行でディスプレイの明るさをカットオフします。
何故かというと、明るさを最少にしても薄ら点灯しているためです。
これは、後でフェードインするためにそうしています。

●63-66行:
ディスプレイに直線を描くデモです。

●68行:
ここで、UTF8Sjis テーブルファイルと東雲フォントファイル2つ、合計3つのファイルを開きます。

●69行:
ここで MyFont ファイルを開きます。
この時点で ESP32 で同時に開けるファイル数の最大値4に達しているので、それを頭に入れておくことが大事です。
今回のライブラリバージョンアップで、UTF8Sjis テーブルファイルのファイルハンドル名 _UtoS がライブラリソースファイル内で共通化できたので、ファイルを閉じたり開いたりする必要が無くなりました。

●71-76行:
ここで、OLED SSD1331 に文字列を1行表示させますが、ディスプレイはOFF になっているので、文字は現れません。

●78-80行:
ここで、SSD1331 をON にして、SSD1331 のBrightness を5ms 毎に明るくしていくと、フェードインしているように見えます。

●82行:
NTP タイムサーバーを東京のタイムゾーンを設定します。
time.windows.com が一番つながりやすかったです。
NTP サーバー取得関数群はサンプルスケッチを参照してライブラリ化しました。

●84-85行:
ここで、Time ライブラリ関数の setSyncProvider を使い、ESP32 のシステム時刻を補正します。
EWG_Get_Ntp_Time は自作ライブラリ関数です。
85行の setSyncInterval 関数でNTPサーバー時刻取得間隔を決めます。
ここでは 1800 秒(30分)としています。

●87行:
153-174行で関数化していて、NTPサーバー時刻取得失敗した場合、他のサーバーのコネクションを試します。

●89-91行:
ここで有機EL ( OLED ) SSD1331 をフェードインします。

●100-103行:
Verry Simple! です。
ディスプレイに4行表示させるガジェットでたった4つの関数だけで済んでいます。
ただ、注意していただきたいのは、ニュースや天気予報記事を取得する関数が最初に持ってきた方が、初回立ち上がりのディスプレイ表示はキレイです。

●107-130行:
Yahoo! Japan RSS ニューストピックストップ記事を取得し、文字列をスクロールさせる関数です。
記事を取得する部分は108-121行で、スクロールする部分は123-129行です。
get_interval で記事を取得する間隔を決め、Scrolle_speed でスクロールする速さを決めます。
前回の記事でも述べたように、スクロールさせる関数群が格段に簡単になりました。

●132-135行:
年月日を東雲フォントで表示し、曜日を自作フォントで表示させる関数です。

●137-140行:
自作フォントのNTP 時計を関数化しました。
‘H’ が Horizontal (水平)スクロール、 ‘V’ がVirtical (垂直)スクロールです。
その後の数値が0 または 1 にすることによって、スクロールする方向を決めます。
スクロールする速さは HM_interval で決めます。

●142-151行:
天気予報を表示させる関数です。
144行で Yahoo! Japan RSS 天気予報の週間天気予報を取得し、146行で今日と明日の天気予報に分類して、有機EL ( OLED ) SSD1331 に表示させます。
ここでは、地域は東京で設定していますが、“/rss/days/4410.xml” のところをご自分の地域に設定できます。

こちらのページを開いて各地域をクリックして、ブラウザのURL入力欄アドレスを確認してください。

https://weather.yahoo.co.jp/weather/rss/

●153-174行:
NTP サーバー時刻補正が出来なかった場合、他のサーバーをチェックして取得できるかどうか試す関数です。
time.windows.com が一番取得しやすいようです。

コンパイル実行する

ブラウザからスケッチをコピーしたら、必ず一旦保存してください。そうしないと正しく表示されませんので要注意!!

では、コンパイル書き込み、実行する前に、Wi-Fi ルーターを起動して、5分以上経ってから、コンパイル実行書き込みしてみてください。

最初の動画のように表示されていればOKです。

ちなみに、天気予報の今日と明日の境目は下図のようになっています。
16×16ドットフォントは6文字しか表示できないので、こうなってしまいました。
自分が分かりゃ~イイんです・・・。

シリアルモニターを 115200bps で起動すると、こんな感じに表示されます。

なお、このライブラリやプログラムはまだ発展途上なので、しばらく時間が経過するとフリーズすることがあります。
この原因はまだ分かりません。
分かり次第お知らせします。

以上です。

まとめ

いかがでしたでしょうか。
うまく動きましたでしょうか?

ESP8266 ではここまでスムースに動かなかったでしょう。
CPU速度と、メモリの大きさで、ESP32はいろいろなことができますね。

先にも述べましたが、このガジェットライブラリはまだ発展途上なので、時間が経つとフリーズしてしまうことがあります。
停止するところは、決まって Arduino core for the ESP32の WiFi ライブラリの client.connect 関数です。
この関数が動く度に電流を消費して電圧ドロップが生じてしまうのか、はたまた自作ライブラリが悪いのか、まだ原因不明です。
ESP8266 でも同じようなフリーズがありましたが、いったい何でしょうねぇ・・・。
いずれにしても、私は基本的に素人のアマチュアで、さらに別の仕事が忙しくて原因追及ができずにいます。

2017/8/2 に Arduino core for ESP32 が大幅アップデートされ、フリーズしなくなりました。

このブログ運営と維持もとても辛くなってきました・・・。
(その割に長文記事を書いてますが・・・。)
なかかな精神的にも辛いものがありますね。

と、泣き言ばかり言ってますが、次回はこれをスマホで操作したりしてみたいと思います。

ではまた・・・。

コメント

  1. 太田 耕正 より:

    フリーズとは関係有りませんが、IDE1.8.2 起動中にスケッチのソースコードを変更して、(rss等 4410 を 5010 「静岡」)に変更して、書き込みを行うと 75行の ”ESP32  日本語”の表示が ”ESP {“ と表示され 天気予報アイコンが表示されません。
    シリアルモニターの表示が
    WEB HTTP Response
    Client Stop
    Weathen forecast =
    Weathen Today =
    Weathen Tomorrow =
    = が空白です。
    スケッチのソースコードを変更した状態で、保存 終了して。
    IDEを再起動して書き込むと 75行や天気予報アイコンも正常に表示されます。シリアルモニターにも状況が表示されます。
    Arduino core for the ESP32 は最新版を使用、ライブラリも最新版です。
    ESP32 – DevKitC ( ESP-WROOM-32 開発ボード )やESP32 単体で行っても同じ結果が出ました。
    書き込み速度は 921600 と 115200ともエラー無く書き込み出来ます。
    文字のスクロールの早さに感激です。

    • mgo-tec mgo-tec より:

      太田さん

      いつも当ブログにお越しいただき、そしてコメントいただき、ありがとうございます。
      m(_ _)m

      私の環境ではそういう現象が起こらないので、あくまで予想ですが、ブラウザからソースコードをクリップボードへコピーした時には Shift_JIS 等の文字コードになっていて、Arduino IDE のスケッチを保存しないままコンパイルした場合は、日本語漢字や全角の”】” などの文字コードが正しく変換されなかったと思われます。
      ですから、一旦、Arduino IDE でスケッチを保存すると、UTF-8 コードで保存されるので、正しい文字コードになると思われます。

      MAC は使ったことが無いので分かりませんが、古い Windows を使っていたり、ブラウザが Google Chrome 以外を使うとそういう現象が起こる可能性があります。
      私の場合は、Windows10 で、ブラウザは Google Chrome を使っています。
      文字変換は Microsoft IME を使っています。
      日本語変換ソフトが ATOK だったりした場合はまた違う結果になるかもしれません。
      申し訳ないのですが、Windows の文字コード設定はどこでやるのか良く分かりません。

      いずれにしても、ブラウザからスケッチをコピーした場合は、必ず Arduino IDE で一旦保存して、再起動するということが確実そうですね。
      この件はトラブルシューティングに掲載させていただこうと思います。
      他の方が違う環境で実行してくださると、バグ等の解明が速いですね。
      いつも当ブログのスケッチを実行していただき、ほんとに感謝いたします。
      ありがとうございました。
      m(_ _)m

    • mgo-tec mgo-tec より:

      太田さん、すみません。

      たった今、同じ現象が私の環境でも起こりました。
      失礼しました。

      おそらく、ブラウザからクリップボードへコピーしただけでは、日本語全角文字 “◆” や “】” が正しく変換されないようです。
      一旦スケッチを保存することによって、正しい文字コードに変換されるようです。
      Arduino IDE 1.8.2 からそういうモードになったのかもしれません。

      ただ、Arduino IDE を再起動しなくても、保存するだけ、その後コンパイルすれば良いことは分かりました。

      ご指摘いただかないと分かりませんでした。
      今まで、他の方も同じ症状でうまく動かなかった可能性がありますね。

      たいへん貴重な情報ありがとうございました。
      重ねてお礼申し上げます。
      m(_ _)m

  2. 太田 耕正 より:

    スケッチを保存してから書き込みを行うとOKでした。
    IDE1.8.3 でのテストも同じ状態ですね。

    • mgo-tec mgo-tec より:

      そうですね。
      スケッチを保存してから書き込みですね。
      それぞれの記事に追加コメント入れました。
      ありがとうございます。

      それと、1.8.3 がリリースされていたんですね。
      知らなかった・・・。

      重ね重ね、情報ありがとうございます。
      m(_ _)m

  3. KATO より:

    こんにちわ
    いつも参考にさせていただいています。
    今回も参考にさせていただき、ブログの通りにコンパイル書き込みまで終えたのですが、OLED1331に何も表示されません、
    考えられる原因があれば教えてください。

    • mgo-tec mgo-tec より:

      KATOさん

      記事をご覧いただき、ありがとうございます。

      この記事は随分昔に書いたもので、現在はいろいろと環境も変わってしまいました。
      動作しない原因を追究するにはあまりにも情報が少なすぎるので、以下の動作環境を教えてください。

      1.ESP32開発ボードは何を使っていますでしょうか?
      2.シリアルモニターにはどのように表示されている内容をできるだけ詳しく教えてください。
      その場合、SSID 等の情報は洩れないように隠してください。
      3.「ツール」の「Core Debug Level」を「Verbose」にしてコンパイルし直して、シリアルモニターに表示される情報も教えてください。
      4.Arduino core for the ESP32 のバージョンを教えてください。
      5.USBハブは使っていませんでしょうか?
      6.良質で太く短いUSBケーブルを使っていますでしょうか?
      7.micro SDHC カードは大手メーカー製を使っていますでしょうか?

      • KATO より:

        せっかくのご返信に対して、大変失礼ですが、いろいろ調整した結果無事表示されました。

        しかし、HOSTに接続できませんと表示が出ます。

        • mgo-tec mgo-tec より:

          KATO さん

          表示されたのは良かったですね。
          あとはシリアルモニターの表示をできるだけ詳しく、できれば丸ごと教えていただけると助かります。

タイトルとURLをコピーしました