ESP-WROOM-02 と 極小OLED (有機EL) で Yahooニュース電光掲示板とNTP時計を表示させてみた。もうIoTですね

記事公開日:2016年4月22日
最終修正日:2016年6月13日

スポンサーリンク

こんばんは。

熊本地震などがあり、記事をアップする気になれなくて、しばらく滞っておりました。
自分の力では何もできなくて本当に心苦しい限りです。
明日は我が身です。
普段から心と体と物品の備えをしておくことが大事なんだとつくづく思いました。
この経験をしっかりと記憶にとどめて、将来のために役立てることが必要ですね。

ということで、電子工作の話に戻りまして、滞っていた記事をアップしました。
今回は 前回の記事 をさらに応用して、LEDドットマトリックスを極小の有機EL (OLED) に替えて、Yahooニュースのトップ記事タイトルとNTP自動同期時計を表示させてみました。
もちろん、Arduino化したESPr Developer ( ESP-WROOM-02 開発ボード )を使用してます。
そして、スマホのブラウザとの設定コントロール通信はWebSocket通信を使用してます。

使うパーツは最少部品点数だと思います。
ESPr Developer (ESP-WROOM-02 ( ESP8266 ) 開発ボード)とOLED ( SSD1306 )モジュールの2点のみ。
あとはブレットボードやジャンパワイヤーだけ。
Very Simple !!

動作状況の動画はこれをご覧ください。

どうでしょうか?
タッチペンの反応が悪くてスイマセン。手でやった方が全然イイです。
OLEDの画像のちらつきはデジカメとのフレッシュレートが合わないので動画ではしょうがないです。実際はクッキリ表示されてますのでご安心を。
ニュース記事を取得する場合はスクロールが停止してしまうのはしょうがないです。停止させたくない場合は電光掲示表示と記事取得を別のCPUで制御しなければなりません。

ちなみに、モバイルバッテリーを使用すると完全無線化できますね。
5年前にコンビニで買った単3乾電池を使ったバッテリーでもできました。
こんな感じです。
ws_OLED_SSD1306_10
ただ、これだとあまり長く持ちません。3時間くらいでしょうか・・・(結局8時間ちょいしか持ちませんでした)
リチウムイオンの方が長く持つと思います。
ESP-WROOM-02をディープスリープモードにするともっと持つかもしれませんね。いつか試してみたいと思います。

フォントは8×8ドットのフリーの美咲フォント を使用してます。しかし、そのまま表示させるとあまりにも小さい文字だったので、ドットを2倍大きくしました。
それでもやはり複雑な漢字は読みにくいのですが、逆に前後の文脈からだいたいの漢字は推察できると思います。だいたいは読み取り可能だと思います。
昔のファミコン字体みたいで、想像力を鍛えられると思います。
もっと判別したいならば詳細な漢字が表示できる16×16ドットのフォントにするしかありません。いつかそれはやってみたいと思っています。

前回の記事 では全角4文字のスクロールでしたが、今回は全角8文字のスクロールです。
しかも、2行別々の記事を異なる速度でスクロールできますので、勝手が良いと思います。
そして、NTPサーバー自動同期時計では年月日および曜日を常時表示できますので、さらに実用性が高いのではないかと思います。

この最少部品点数だと、バッテリーを除くとFriskケースに入れられそうです。
バッテリーをうまく工夫すれば、腕時計っぽく作ることもできそうです。

では、これの作り方を説明します。

2016/6/2アップした記事ではさらに詳細な漢字フォント16x16東雲フォントを使って表示させています。こちらの方が鮮明に読み取れてキレイですよ!!
Shinonome18

1.準備するもの

● ESPr Developer ( ESP-WROOM-02 開発ボード ) (スイッチサイエンス製)
Amazonさんではこんな感じで販売してます。
これには2.54mmピンヘッダが付属しておりませんので、別途購入が必要です。

i2c OLEDモジュール ディスプレイ 0.96インチ ブルー&イエロー
有機ELとディスプレイドライバ SSD1306 を使った極小の128×64ドットのI2C通信ディスプレイです。
これは上部横16ドット列が予め黄色になっていて、それ以下は全てブルーというものです。
今回はAmazonさんで販売しているこちらのものを使用しました。
(※当方で購入したものはDIYMALLさんで販売していたものを使用しております。記事を上げた当初は別の販売店のものを紹介しており、訂正しました。大変失礼いたしました。
別の販売店のものでも使用は問題ないと思います。が、SPI接続は使用できません。必ずi2C接続のものを使用してください。2016/4/28)
因みに、こちらは2.54mmピンヘッダが予めハンダ付けされておりました。

これが在庫切れの場合、色は単色ですが、以下のものでもOKです。

●ブレッドボード、ジャンパワイヤー、2.54mmピンヘッダ
ジャンパワイヤーは通常のオス-オスでも良いのですが、オス-メスジャンパワイヤーで直接OLEDに接続しても良いと思います。
過去の記事のこちらのページにあるようにQIコネクターを自作しても良いと思います。
または以下のようにAmazonさんでもオス-メスのものが売っております。

2.接続する

ESPr Developer ( ESP-WROOM-02 開発ボード )の詳細な使い方やピンヘッダハンダ付け方法はこちらのページをご覧ください。

有機EL(OLED) SSD1306 は表面はこんな感じです。
ws_OLED_SSD1306_03

裏面にはディスプレイドライバSSD1306 のI2C通信用アドレスが記載されています。
これは7bit数値で0x78 とありますので、実際にArduino IDEでプログラミングするにはそれを1bit右へシフトした0x3Cというアドレスになります。
ws_OLED_SSD1306_02

このOLEDの使い方に関しての詳細は過去の記事も参照してみてください。

あとはブレッドボード上で以下のようにジャンパワイヤーで接続するだけです。
有機EL(OLED) SSD1306 とはI2C通信接続します。
I2C通信ではプルアップ抵抗が必要ですが、このOLEDモジュールには予め埋め込んであるようなので、直付けしてます。
ws_OLED_SSD1306_01

実際の接続した写真はこんな感じです。
ws_OLED_SSD1306_04

3.Arduino IDE と ESP-WROOM-02 の設定

●Arduino IDE はver 1.6.8 のインストール
1.6.5でも、1.6.8でも動作します。
1.6.5 を使っていた方はこちらの記事を参照してアンインストールして、Arduino15フォルダを削除して、1.6.8を再インストールしてください。

●Arduino IDE の最新版ESP8266ボードインストール
これは、ESP-WROOM-02 ( ESP8266 )をArduino IDEで開発するためのプラグインです。
インストール方法はこちらの記事を参照してください。
2016/4/19現在ではStable版2.2.0になってます。これでも問題なく動作しました。

●プラグインのSPIFFSファイルシステムのインストール
これは、スケッチフォルダの中のdataフォルダの中にあるファイルをArduino IDE を使ってESP-WROOM-02 のフラッシュメモリにアップロードするためのプラグインツールです。
インストール方法はこちらの記事を参照してください。

4.ライブラリのインストール

前回の記事のドットマトリックス電光掲示板を試された方は今回はOLED_SSD1306ライブラリだけインストールすれば良いのですが、初めて試される方のために全て載せておきます。
以下のライブラリをArduino IDE の libraries フォルダにフォルダ毎コピーしてください。

●当方自作の、OLED_SSD1306ライブラリのインストール
これは今回の記事で初登場です。
有機EL(OLED) SSD1306 をI2C通信で制御する自作ライブラリです。
現在、Beta 1.0 です。
GitHubのこちらのページ からZIPファイルをダウンロードして解凍して、OLED_SSD1306-masterフォルダの中の更にその中のOLED_SSD1306-masterフォルダをIDEのlibrariesフォルダにフォルダごとコピーしてください。

●自作ライブラリ、EasyWebSocket Beta 1.35 をインストール

スマートフォンのブラウザとリアルタイム通信のWebSocketを行うための自作ライブラリです。
こちらのページを参照していただき、EasyWebSocketサンプルスケッチを別名で保存して、サンプルスケッチの中のdataフォルダの中の spiffs_01.txt ファイルのローカルIPアドレスをご自分のルーターで割り当てたアドレスに変更して保存しておいてください。
サンプルスケッチのプログラムは全て消去して空のファイルにしてOKです。そこに後で述べるソースコードを入力します。
このライブラリの解説はこちらのページを参照してください。

●当方自作の、UTF8 to Shift_JIS 変換ライブラリのインストール
これは文字コードのUTF-8 を Shift_JIS に変換するための私の自作のライブラリです。
ESP-WROOM-02専用です。
現在、Beta version 1.0 です。
GitHubのこちらのページ からZIPファイルをダウンロードして解凍して、UTF8SJIS_for_ESP8266-masterフォルダの中の更にその中のUTF8SJIS_for_ESP8266-masterフォルダをIDEのlibrariesフォルダにフォルダごとコピーしてください。
このライブラリの解説はこちらのページを参照してください。

●当方自作の、MisakiFNT_read_for_ESP8266 ライブラリのインストール
これはフリーの日本語8×8ドット 美咲フォント をESP-WROOM-02のフラッシュメモリから呼び出すためのライブラリです。
Beta version 1.1 を使用してください。
GitHubのこちらのページ からZIPファイルをダウンロードして解凍して、
MisakiFNT_read_for_ESP8266-masterフォルダの中の更にその中のMisakiFNT_read_for_ESP8266-masterフォルダをIDEのlibrariesフォルダにフォルダごとコピーしてください。
このライブラリの解説はこちらのページを参照してください。

●当方自作の、I2C_Adafruit_8x8_LED_matrixライブラリのインストール
これは本来Adafruit社製の8x8LEDドットマトリックスを操作するためのライブラリだったのですが、電光掲示板としてSSD1306でも使用できるので流用してます。
Beta version 1.1 を使用してください。
GitHubのこちらのページ からZIPファイルをダウンロードして解凍して、
I2C_Adafruit_8x8_LEDmatrix_library-masterフォルダの中の更にその中のI2C_Adafruit_8x8_LEDmatrix_library-masterフォルダをIDEのlibrariesフォルダにフォルダごとコピーしてください。
このライブラリの解説はこちらのページを参照してください。

●Timeライブラリのインストール
Arduino.ccページのこちらにあります。→http://playground.arduino.cc/code/time
このページを開いて下図のようなところをクリックすると
ws_Yahoo_message05

このように表示されますので、下図のようにGithubリンクをクリックしてください。

ws_Yahoo_message06

そうしたら下図のようなところをクリックするとZIPファイルのダウンロードが開始されます。
ws_Yahoo_message07

それを解凍するとTime-masterフォルダがありますので、更にその中のTime-masterフォルダをIDEのlibrariesフォルダにフォルダごとコピーしてください。

5.SPIFFSファイルアップローダーでWROOMのフラッシュに書き込む

EasyWebSocketサンプルスケッチを空にして保存したファイルにdataフォルダがあるので、そこに以下の4つのファイルをコピーしておきます。

①spiffs_01.txt
スマートフォンのブラウザとWebSocket通信するためのHTMLヘッダファイル。
このテキストファイルをメモ帳などのテキストエディタで開き、ws://192.168.0.14  のところをご自分のルーターで割り当てたESP-WROOM-02 のアドレスに書き換えてください。必ず文字コードはUTF-8で保存してください。

②Utf8Sjis.tbl
これは文字コードのUTF-8をShift_JISに変換するテーブルです。これはGitHubのこちらのページにあります。
ZIPファイルをダウンロードして解凍してUtf8Sjis.tblファイルだけdataフォルダにコピーしておいてください。
このファイルについての詳細はこちらのページにありますので参照してみてください。

③MSKG13KU.FNT
これは全角の8×8ドットの美咲フォントです。これはこちらのページにあります→http://www.geocities.jp/littlimi/misaki.htm
PC-E500 SCRNJPN.FNT 形式のものをダウンロードして解凍してMSKG13KU.FNTファイルをdataフォルダにコピーしてください。

④mgotec48.FNT
これは美咲フォントの4X8.FNTフォントの数値部分を私が独自に書き換えたフォントです。
4X8.FNTフォントは半角ですが、数値が全角と合わなくて小さすぎるので改良しました。
このファイルはこちらのページにありますので、ZIPファイルをダウンロードして解凍して、mgotec48.FNTファイルをdataフォルダにコピーしておいてください。

以上の4つのファイルをIDEプラグインのSPIFFSファイルシステムアップローダーでESP-WROOM-02のフラッシュメモリにアップロードします。
下図のようなところをクリックするとアップロードが始まります。終るまで結構時間がかかりますので、気長に待ってください。
EWS_Beta13_08

6.スケッチを入力する

いよいよスケッチです。こんな感じになります。
(※ライブラリのライセンス表記の誤りを訂正しました。2016/5/26)

/* WebSocketリアルタイムコントロール 有機EL(OLED) 電光掲示板サンプルスケッチ
 * Web記事取得、時計機能追加
 * ESP-WROOM-02(ESP8266) および 有機EL SSD1306専用
 * Dual licensed under The MIT License or LGPL v2.1
 * Copyright (c) 2016 MGO-tec 
 * License reference URL --> https://opensource.org/licenses/mit-license.php
 * Includes ESP8266WiFi.h, Copyright (C) 2014 Ivan Grokhotkov. -->LGPL v2 license.
 * Includes Hash.h, Copyright (c) 2015 Markus Sattler. -->LGPL v2 license.
 * License reference URL --> https://opensource.org/licenses/LGPL-2.1
 */
#include <EasyWebSocket.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <Wire.h>
#include <TimeLib.h> //timeライブラリver1.4の場合
#include <WiFiUdp.h>
#include <UTF8toSJIS.h>
#include <MisakiFNT_read.h>
#include <I2C_Adafruit_8x8_LED_matrix.h>
#include <OLED_SSD1306.h>

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

const char* UTF8SJIS_file = "/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* Zen_Font_file = "/MSKG13KU.FNT"; //全角フォントファイル名を定義
const char* Half_Font_file = "/mgotec48.FNT"; //半角フォントファイル名を定義

const uint8_t OLED_Adress = 0x3C; //OLED address 製品に記載の数値は7bitなので、8bitに変換して1bit右へずらした値(78>>3c)

EasyWebSocket ews;
UTF8toSJIS u8ts;
MisakiFNT_read MFR;
I2C_Adafruit_8x8_LED_matrix adaLED;
OLED_SSD1306 oled;

String html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7;

uint16_t PingSendTime = 30000;

String ret_str="";
String scl_txt="";

uint8_t scl_cnt = 0, scl_cnt2 = 0;
uint8_t fnt_cnt = 0, fnt_cnt2 = 0;
bool FntReadOK = true, FntReadOK2 = true;

uint8_t LedDotOut[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t Next_buf[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t tmp_buf[8] = {0,0,0,0,0,0,0,0};
uint8_t tmp_buf_cnv[8] = {0,0,0,0,0,0,0,0};
uint8_t tmp_buf_cnv_2[8] = {0,0,0,0,0,0,0,0};

uint8_t LedDotOut2[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t Next_buf2[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t tmp_buf2[8] = {0,0,0,0,0,0,0,0};
uint8_t tmp_buf_cnv2[8] = {0,0,0,0,0,0,0,0};
uint8_t tmp_buf_cnv2_2[8] = {0,0,0,0,0,0,0,0};

uint8_t sj_txt[2048], sj_txt2[2048];
uint16_t sj_cnt = 0, sj_cnt2 = 0;
uint16_t sj_length, sj_length2;
uint32_t SclTime, SclTime2;
uint16_t SclSpeed = 50, SclSpeed2 = 30;
boolean Scl_Stop = false;
uint8_t Direction = 0;
uint8_t brightness = 0;
int16_t Angle = 0;

boolean sjis_txt_in = false, sjis_txt_in2 = false;
boolean BW_reverse = false, BW_reverse2 = false;

//-------NTPサーバー定義----------------
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov; //(129, 6, 15, 28)time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
const int timeZone = 9;     // Tokyo
WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets
time_t prevDisplay = 0; // when the digital clock was displayed
uint32_t LastTime = 0;

String time_str;
String ymd_str;
String dummy_ymd_str;
uint8_t time_sj_txt[25], ymd_sj_txt[25];
uint16_t time_sj_length, ymd_sj_length;

struct TimeStrData
{ String week_jp; String week_en3; String week_en2; };

struct TimeStrData TSD[7] =
{
  {"日","Sun","Su"},{"月","Mon","Mo"},{"火","Tue","Tu"},{"水","Wed","We"},{"木","Thr","Th"},{"金","Fri","Fr"},{"土","Sat","Sa"}
};

//-----News & Weather-----------------------------------------------
uint8_t web_get = 0, web_get2 = 0;
uint32_t Web_time = 0;
boolean first_get = false;

//-----OLED-----------------------------------------------
uint8_t OLED_tmp_buf_cnv2[8] = {0,0,0,0,0,0,0,0};
uint8_t OLED_DotOut[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t OLED_DotOut2[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t OLED_DotOut3[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t OLED_Next_buf[8][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};
uint8_t tmp_OLED_buf_cnv2[8] = {0,0,0,0,0,0,0,0};
uint8_t tmp_OLED_buf_cnv3[8] = {0,0,0,0,0,0,0,0};

//***********セットアップ***************************************************
void setup() 
{
  Wire.begin(); // I2C initialise the connection
  Wire.setClock(400000L); //クロックはMax 400kHz

  oled.setup_OLED_SSD1306(OLED_Adress);
  
  delay(300);
  //セットアップで全角を一旦表示させることが重要。半角だとなぜかSPIFFSファイル読み込みエラーになってしまうため。
  u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, "WAIT・・・・", sj_txt, &sj_length);
  for(uint8_t i=0; i<8; i++) MFR.Sjis_To_MisakiFNT_DotRead(Zen_Font_file, Half_Font_file, 0, 0, sj_txt[i*2], sj_txt[i*2+1], LedDotOut[i]);
  for(uint8_t i=0; i<8; i++) adaLED.LED_Dot_Rotation(90, LedDotOut[i], OLED_DotOut2[i]);
  oled.OLED_2X2_Display_Out_16x127(OLED_Adress, 0, 2, OLED_DotOut2[7], OLED_DotOut2[6], OLED_DotOut2[5], OLED_DotOut2[4], OLED_DotOut2[3], OLED_DotOut2[2], OLED_DotOut2[1], OLED_DotOut2[0]);
  for(uint8_t i=0; i<8; i++) {
    for(uint8_t j=0; j<8; j++){
      OLED_DotOut2[i][j] = 0; LedDotOut[i][j] = 0; //一旦初期化しておく
    }
  }
  
  html_str1 = "<body style='background:#ddddff; color:#000;'>\r\n";
  html_str1 += ews.EWS_TextBox_Send("txt1", "1行目任意テキスト送信、半角カタカナもOK!","送信");
  html_str1 += "<br>\r\n";
  html_str1 += ews.EWS_TextBox_Send("Txt2", "2行目任意text送信、半角カタカナもOK!","送信");
  html_str1 += "<br><br>\r\n";
  html_str2 = "Scrolle Speed 1\r\n";
  html_str2 += ews.EWS_Canvas_Slider_T("Speed",200,40,"#777777","#77aaff");
  html_str2 += "<br><br>\r\n";
  html_str2 += "Scrolle Speed 2\r\n";
  html_str2 += ews.EWS_Canvas_Slider_T("speed2",200,40,"#777777","#77aaff");
  html_str3 = "<br><br>\r\n";
  html_str3 += ews.EWS_On_Momentary_Button("_SclStop", "Scrole Stop", 100,25,15,"#ffffff","#777777");
  html_str3 += "<br><br>1行目白黒反転 \r\n";
  html_str4 = ews.EWS_On_Momentary_Button("(rev", "Normal", 70,25,15,"#000000","#ffffff");
  html_str4 += "<span>  </span>\r\n";
  html_str4 += ews.EWS_On_Momentary_Button(")rev", "Reverse", 70,25,15,"#ffffff","#0000ff");
  html_str4 += "<br>2行目白黒反転 \r\n";
  html_str4 += ews.EWS_On_Momentary_Button("[rev", "Normal", 70,25,15,"#000000","#ffffff");
  html_str4 += "<span>  </span>\r\n";
  html_str4 += ews.EWS_On_Momentary_Button("]rev", "Reverse", 70,25,15,"#ffffff","#0000ff");
  html_str4 += "<br><br>\r\n";
  html_str5 = "<div style='width:330px; height:140px; text-align:center; padding:10px; background:#ccccff; border:#0000ff solid 1px;'>\r\n";
  html_str5 += ews.EWS_On_Momentary_Button("webON", "Web GET ON", 110,25,15,"#000000","#ff77ff");
  html_str5 += "※ここボタンを押すとWebSocketは切断されます。再操作する場合はWS-Reconnectボタンを押してください<br>1行目 \r\n";
  html_str5 += ews.EWS_On_Momentary_Button("News", "Top NEWS", 100,25,15,"#000000","#ffffff");
  html_str5 += "<span>  </span>\r\n";
  html_str5 += ews.EWS_On_Momentary_Button("Cnews", "PC NEWS", 110,25,15,"#000000","#ffffff");
  html_str5 += "<br><br>2行目 \r\n";
  html_str5 += ews.EWS_On_Momentary_Button("ENews", "芸能News", 110,25,15,"#000000","#ffffff");
  html_str5 += "<span>  </span>\r\n";
  html_str5 += ews.EWS_On_Momentary_Button("Weather", "Weather", 110,25,15,"#000000","#ffffff");
  html_str5 += "</div><br><br>\r\n";
  html_str6 = ews.EWS_Status_Text(20,"RED");
  html_str6 += "<br>\r\n";
  html_str6 += "<br><br>\r\n";
  html_str6 += ews.EWS_WebSocket_Reconnection_Button("WS-Reconnect", 200, 40, 17);
  html_str6 += "<br><br>\r\n";  
  html_str6 += ews.EWS_Close_Button("WS CLOSE",150,40,17);
  html_str6 += ews.EWS_Window_ReLoad_Button("ReLoad",150,40,17);
  html_str6 += "</body></html>\r\n";
  html_str7 = "";
  
  ews.AP_Connect(ssid, password);

  //NTPサーバーでタイムを取得
  Udp.begin(localPort);
  setSyncProvider(getNtpTime);
  delay(1000);
  
  SclTime = millis();
  LastTime = millis();
  prevDisplay = now();
}
//**************メインループ*************************************************
void loop() {
  //WebSocket ハンドシェイク関数
  ews.EWS_HandShake(html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7);
  
  uint8_t i, br;

  ret_str = ews.EWS_ESP8266CharReceive(PingSendTime); //ブラウザからのWebSocketデータ受信

  if(ret_str != "_close"){
    if(ret_str != "\0"){
      if(ret_str != "Ping"){
        if(ret_str[0] != 't' && ret_str[0] != 'T'){
          switch(ret_str[4]){
            case 'S':
              SclSpeed = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30);
              SclSpeed = 200-SclSpeed;
              Scl_Stop = false;
              break;
            case 's':
              SclSpeed2 = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30);
              SclSpeed2 = 200-SclSpeed2;
              Scl_Stop = false;
              break;
            case '_':
              Scl_Stop = true;
              break;
            case '(':
              BW_reverse = false;
              break;
            case ')':
              BW_reverse = true;
              break;
            case '[':
              BW_reverse2 = false;
              break;
            case ']':
              BW_reverse2 = true;
              break;
            case 'w':
              web_get = 1; web_get2 = 1; first_get = true;
              break;
            case 'N':
              web_get = 1;
              sjis_txt_in = false; first_get = true;
              break;
            case 'C':
              web_get = 2;
              sjis_txt_in = false; first_get = true;
              break;
            case 'E':
              web_get2 = 1;
              sjis_txt_in2 = false; first_get = true;
              break;
            case 'W':
              web_get2 = 2;
              sjis_txt_in2 = false; first_get = true;
              break;
          }
        }else if(ret_str[0] == 't' || ret_str[0] == 'T' ){
          scl_txt = ret_str.substring(ret_str.indexOf('|')+1, ret_str.length()-1);
          scl_txt += String(" ") + String("\0");
          switch(ret_str[0]){
            case 't':
              u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, scl_txt, sj_txt, &sj_length);
              fnt_cnt = 0; scl_cnt = 0; sj_cnt = 0; web_get = 0;
              FntReadOK = true; sjis_txt_in = true; Scl_Stop == false;
              FntReadOK2 = false; sjis_txt_in2 = false;
              break;
            case 'T':
              u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, scl_txt, sj_txt2, &sj_length2);
              fnt_cnt2 = 0; scl_cnt2 = 0; sj_cnt2 = 0; web_get = 0;
              FntReadOK2 = true; sjis_txt_in2 = true; Scl_Stop == false;
              FntReadOK = false; sjis_txt_in = false;
              break;
          } 
          Serial.println();
          for (int iiii=0; iiii<sj_length; iiii++) Serial.write(sj_txt[iiii]);
          Serial.println();
        }
        ret_str = "";
      }else{
        ret_str = "";
      }
    }
    
    if(sjis_txt_in == true || sjis_txt_in2 == true){ //OLED1行目電光掲示板スクロール
      if(millis() - SclTime > SclSpeed){
        if(sj_cnt >= sj_length){
          sj_cnt = 0;
        }
        if(FntReadOK==true){
          uint8_t cp = MFR.Sjis_To_MisakiFNT_DotRead(Zen_Font_file, Half_Font_file, Direction, Angle, sj_txt[sj_cnt], sj_txt[sj_cnt+1], tmp_buf_cnv);
          FntReadOK = false;
          sj_cnt = sj_cnt + cp;
          adaLED.LED_Black_White_Reversal(BW_reverse, tmp_buf_cnv, tmp_buf_cnv);
          adaLED.LED_Dot_Rotation(Angle, tmp_buf_cnv, tmp_buf_cnv_2);
        }

        if(Scl_Stop == false){
          for(i=0; i<8; i++) adaLED.LED_Dot_Rotation(90, LedDotOut[i], OLED_DotOut2[i]);
          oled.OLED_2X2_Display_Out_16x127(OLED_Adress, 0, 6, OLED_DotOut2[0], OLED_DotOut2[1], OLED_DotOut2[2], OLED_DotOut2[3], OLED_DotOut2[4], OLED_DotOut2[5], OLED_DotOut2[6], OLED_DotOut2[7]);
          adaLED.Scroller_Dot_Replace( Direction, Next_buf[0], LedDotOut[0], tmp_buf_cnv_2);
          for(i=0; i<7; i++) adaLED.Scroller_Dot_Replace( Direction, Next_buf[i+1], LedDotOut[i+1], Next_buf[i]);
          scl_cnt++;
        }
        
        if(scl_cnt == 8){
          scl_cnt = 0;
          FntReadOK = true;
        }
        SclTime = millis();
      }

      if(millis() - SclTime2 > SclSpeed2){ //OLED2行目電光掲示板スクロール
        if(sj_cnt2 >= sj_length2){
          sj_cnt2 = 0;
        }
        if(FntReadOK2==true){
          uint8_t cp2 = MFR.Sjis_To_MisakiFNT_DotRead(Zen_Font_file, Half_Font_file, Direction, Angle, sj_txt2[sj_cnt2], sj_txt2[sj_cnt2+1], tmp_buf_cnv2);
          FntReadOK2 = false;
          sj_cnt2 = sj_cnt2 + cp2;
          adaLED.LED_Black_White_Reversal(BW_reverse2, tmp_buf_cnv2, tmp_buf_cnv2);
        }

        if(Scl_Stop == false){
          for(i=0; i<8; i++) adaLED.LED_Dot_Rotation(90, LedDotOut2[i], OLED_DotOut3[i]);
          oled.OLED_2X2_Display_Out_16x127(OLED_Adress, 0, 4, OLED_DotOut3[0], OLED_DotOut3[1], OLED_DotOut3[2], OLED_DotOut3[3], OLED_DotOut3[4], OLED_DotOut3[5], OLED_DotOut3[6], OLED_DotOut3[7]);
          adaLED.Scroller_Dot_Replace( Direction, Next_buf2[0], LedDotOut2[0], tmp_buf_cnv2);
          for(i=0; i<7; i++) adaLED.Scroller_Dot_Replace( Direction, Next_buf2[i+1], LedDotOut2[i+1], Next_buf2[i]);
          scl_cnt2++;
        }
        
        if(scl_cnt2 == 8){
          scl_cnt2 = 0;
          FntReadOK2 = true;
        }
        SclTime2 = millis();
      }
    }
  }else if(ret_str == "_close"){
    ret_str = "";
    scl_txt = "";
  }

  if(now() != prevDisplay){ //ここから時刻表示設定
    char month_chr[3], day_chr[3], h_chr[3], m_chr[3], s_chr[3];
    sprintf(h_chr, "%2d", hour());//ゼロを空白で埋める場合は%2dとすれば良い
    sprintf(m_chr, "%02d", minute());
    sprintf(s_chr, "%02d", second());
    sprintf(month_chr, "%2d", month());
    sprintf(day_chr, "%02d", day());

    time_str = String(h_chr) + ':' + String(m_chr) + ':' + String(s_chr);
    ymd_str = String(year()) + '/' + String(month_chr) + '/' + String(day_chr) + "(" + String(TSD[weekday()-1].week_jp) + ")";
    
    uint8_t time_dot[4][8];    
    u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, time_str, time_sj_txt, &time_sj_length);
    if(Direction != 0) Direction = 0;
    for(i=0; i<4; i++) MFR.Sjis_To_MisakiFNT_DotRead(Zen_Font_file, Half_Font_file, Direction, 0, time_sj_txt[i*2], time_sj_txt[i*2+1], time_dot[i]);
    uint8_t dummy_time_dot[4][8];
    uint8_t OLED_time_dot[4][8];
    for(i=0; i<4; i++) adaLED.LED_Dot_Rotation(Angle, time_dot[i], dummy_time_dot[i]);
    for(i=0; i<4; i++) adaLED.LED_Dot_Rotation(90, time_dot[i], OLED_time_dot[i]);

    uint8_t OLED_ymd_dot[8][8];
    if(dummy_ymd_str != ymd_str){
      u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, ymd_str, ymd_sj_txt, &ymd_sj_length);
      uint8_t ymd_dot[8][8];
      if(Direction != 0) Direction = 0;
      for(i=0; i<8; i++) MFR.Sjis_To_MisakiFNT_DotRead(Zen_Font_file, Half_Font_file, Direction, 0, ymd_sj_txt[i*2], ymd_sj_txt[i*2+1], ymd_dot[i]);
      uint8_t dummy_ymd_dot[8][8];
      for(i=0; i<8; i++) adaLED.LED_Dot_Rotation(Angle, ymd_dot[i], dummy_ymd_dot[i]);
      for(i=0; i<8; i++) adaLED.LED_Dot_Rotation(90, ymd_dot[i], OLED_ymd_dot[i]);
    }
    
    if(Angle == 0 || Angle == 90){
      oled.OLED_2X2_Display_Out_16x64(OLED_Adress, 30, 0, OLED_time_dot[3], OLED_time_dot[2], OLED_time_dot[1], OLED_time_dot[0]);
      if(dummy_ymd_str != ymd_str){
        oled.OLED_2X2_Display_Out_16x127(OLED_Adress, 0, 2, OLED_ymd_dot[7], OLED_ymd_dot[6], OLED_ymd_dot[5], OLED_ymd_dot[4], OLED_ymd_dot[3], OLED_ymd_dot[2], OLED_ymd_dot[1], OLED_ymd_dot[0]);
        dummy_ymd_str = ymd_str;
      }
    }else{
      oled.OLED_2X2_Display_Out_16x64(OLED_Adress, 30, 0, OLED_time_dot[0], OLED_time_dot[1], OLED_time_dot[2], OLED_time_dot[3]);
    }
    prevDisplay = now();
  }

  if(web_get == 0){
    if(millis()-LastTime >= 300000L){//5分毎にNTPサーバーからタイム取得
      setSyncProvider(getNtpTime);
      Serial.println(time_str);
      LastTime = millis();
    }
  }else{
    if(millis()-LastTime >= 240000L){ //Web記事取得時でもNTPサーバからタイムを補正しておく
      setSyncProvider(getNtpTime);
      LastTime = millis();
    }
  }

  if(web_get > 0 || web_get2 > 0){ //Web記事取得
    if(first_get == true || millis()-Web_time > 600000L){ //Web記事を10分毎に取得
      char* news1_1_host = "news.yahoo.co.jp";
      String news1_1_target_ip = "/pickup/rss.xml"; // トップニュースタイトルページ
      char* news1_2_host = "news.yahoo.co.jp";
      String news1_2_target_ip = "/pickup/computer/rss.xml"; //コンピューター系ニュースページ
      String news_str = "";
      char Web_h[3], Web_m[3];
      sprintf(Web_h, "%02d", hour());//ゼロを空白で埋める場合は%2dとする
      sprintf(Web_m, "%02d", minute());
      news_str = String("◆") + String(Web_h) + ":" + String(Web_m) + " ";
      switch(web_get){
        case 1:
          news_str += ews.EWS_Web_Get(news1_1_host, news1_1_target_ip, '\n', "</rss>", "<title>", "</title>", "◆");
          break;
        case 2:
          news_str += ews.EWS_Web_Get(news1_2_host, news1_2_target_ip, '\n', "</rss>", "<title>", "</title>", "◆");
          break;
      }
      news_str.replace("&amp;","&");
      Serial.println();
      u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, news_str, sj_txt, &sj_length);
      news_str ="";
      for(int iy=0; iy<sj_length; iy++){
        Serial.write(sj_txt[iy]);
      }
      Serial.println();
      fnt_cnt = 0; scl_cnt = 0; sj_cnt = 0;
      sjis_txt_in = true;  FntReadOK = true;
      //2つめの記事取得
      char* news2_1_host = "news.yahoo.co.jp";
      String news2_1_target_ip = "/pickup/entertainment/rss.xml"; // 芸能トップニュースタイトルページ
      char* news2_2_host = "rss.weather.yahoo.co.jp";
      String news2_2_target_ip = "/rss/days/4620.xml"; //小田原の天気予報ページ
      String news2_str = "";

      news2_str = String("◆") + String(Web_h) + ":" + String(Web_m) + " ";
      switch(web_get2){
        case 1:
          news2_str += ews.EWS_Web_Get(news2_1_host, news2_1_target_ip, '\n', "</rss>", "<title>", "</title>", "◆");
          break;
        case 2:
          news2_str += ews.EWS_Web_Get(news2_2_host, news2_2_target_ip, '>', "</rss>", "【", " - ", "【");
          break;
      }
      news2_str.replace("&amp;","&");
      Serial.println();Serial.print("WebGet2=");
      u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, news2_str, sj_txt2, &sj_length2);
      news2_str ="";
      for(int iy=0; iy<sj_length2; iy++){
        Serial.write(sj_txt2[iy]);
      }
      Serial.println();
      fnt_cnt2 = 0; scl_cnt2 = 0; sj_cnt2 = 0;
      sjis_txt_in2 = true;  FntReadOK2 = true;
      Web_time = millis();
      first_get = false;
    }
  }
}
//*************************NTP Time**************************************
time_t getNtpTime()
{
  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  sendNTPpacket(timeServer);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}
//*************************NTP Time**************************************
void sendNTPpacket(IPAddress &address)
{// send an NTP request to the time server at the given address
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:                 
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

これもMITライセンスとしました。
配布、商用、改変等は自由。ただし無保証。
ライセンス表記はしてくださいというものです。
ネット社会では自由に配布するためにはライセンス表記しないと逆に自由配布できないそうなので、そうしてます。
ただ、つい最近知ったのですが、Arduino core for ESP8266 wifi chip ライブラリを使っていると、それがGPL v2 ライセンスなので、必然とこれもGPL v2 ライセンスになってしまうらしいです。つまり、この場合はデュアルライセンス?になるらしいです。
まぁ、MIT ライセンスとGPLライセンスは親和性があるので、私としてはどちらでもいいのです。

では、以下はプログラムの解説です。
グローバル変数がめちゃめちゃ多いのですが、ESP-WROOM-02のSRAMが膨大なので、全然余裕です。
常にスマホブラウザからの接続要求を認識できるようにするとこうなってしまったのです。

●22-23行:
ここはご自分のWi-FiルーターのSSIDとパスワードに書き換えてください。

●25-27行:
UTF8→Shift_JIS 変換テーブルと美咲フォントの全角、半角ファイル名を指定してます。
必ず前にスラッシュをつけてください。

●29行:
有機EL(OLED) SSD1306のI2C通信のアドレスを指定します。
このOLED は裏面に0x78 と記載されています。
これは7bit値ですので、8bitにして、右へビットをずらすと0x3Cとなります。

●31-35行:
自作ライブラリのクラス名を指定してます。

●39行:
EasyWebSocketライブラリで、Pingをブラウザに送信するタイミングを指定してます。
ここでは30秒毎にしてます。
これが短いと通信トラフィックに影響が出ますし、長すぎるとWebSocket通信が勝手に切断されてしまいます。それを防ぐために適度な値にしてます。

●73-81行:
NTP通信のサンプルスケッチをそのまま流用しました。

●89-95行:
曜日の漢字や英字を配列にするために、ここでは構造体で定義してます。

●114-115行:
wireライブラリのクラスで、I2C通信のセットアップです。とりあえず、最大の400kHzでも動くようです。SSD1306のI2Cクロックはデータシートを見ても分かりませんでした。

●117行:
今回から登場した自作ライブラリのクラスです。
I2C通信のSSD1306 の初期化です。

oled.setup_OLED_SSD1306(OLEDのI2Cアドレス);

●121-129行:
セットアップ関数内で “Wait...” などとOLEDに一旦全角で表示させます。
そうしないとなぜか半角フォントファイルをSPIFFSファイルシステムで読み込んでくれないんですね。 これは謎です。
124行目は今回から登場したOLED_SSD1306ライブラリのクラスです。
8×8ドットをそのまま表示させると余りにも小さすぎて読み取り辛いので、2倍角にドットを拡張してます。つまり疑似16×16ドットです。

oled.OLED_2X2_Display_Out_16x127(I2Cアドレス,水平開始位置, 垂直開始位置, 8×8ドット[7], 8×8ドット[6], 8×8ドット[5], 8×8ドット[4], 8×8ドット[3], 8×8ドット[2], 8×8ドット[1], 8×8ドット[0]);

水平開始位置は0~127ドットです。右下が原点(0,0)ですので要注意です。
垂直開始位置は0~7ブロックです。つまり、SSD1306のデータシートによると、縦は8ドットで1ページという区分けになってます。
このOLEDは8×8ドットを8つ横に並べれば1列一杯の文字列を表示できますので、8つの8×8ドットをしていすることにしています。
SSD1306の詳細については過去の記事のこちらのページ  こちらのページを参照してください。

●131-171行:
ブラウザに送信するHTMLタグの<body>要素の文字列をString変数へ代入します。
自作ライブラリのEasyWebSocketの関数をふんだんに使ってます。
EWS_Canvas_Slider_T は多量のタグを排出するので、一つのString変数に2つまでしか代入できませんので注意です。7つのString変数に分散してます。

●173行:
ルーターと接続する関数です。

●176-177行:
NTPサーバーと同期します。

●187行:
自作ライブラリのEasyWebSocket関数で、スマホのブラウザとWebSocket接続のハンドシェイクを行います。
メインloop関数内で常にコネクションをチェックしていて、再接続し易いようにしてます。

●191行:
EasyWebSocket関数でブラウザからの送信されてくる文字列を受信しています。

●192-242行:
ブラウザから送信されてくる文字列配列の4番目の文字をキーワードとして、ESP-WROOM-02の行う処理を決めています。
スクロール速度、白黒反転、記事取得などの処理を決定します。
199-200行ではスクロール速度はHTML5のCanvas要素スライダーで200px幅で数値を出しているので、こんな感じになります。

●243-263行:
ブラウザのテキストボックスから送信されてくる文字列をここで抽出しています。
248行では自作ライブラリのUTF8→Shift_JIS変換を使って文字コード変換してます。

●270-324行:
ここでShift_JIS文字列から美咲フォントに変換して、文字をスクロールしています。
OLEDの1行目と2行目のスクロールは別々に計算しなくてはなりません。
271行でスクロール速度をコントロールしています。
276行で2バイトのShift_JISコードから美咲フォントを呼び出し、8×8ドットを呼び出しています。
284行ではドットを90度回転しています。つまり、このOLEDはビット列を縦方向に表示していくので、こうなります。
285行で2倍角に変換してOLEDにI2C通信出力します。
286-287行でドットを1bit左へずらしていきます。これを8回繰り返したら、新たに文字を読み込んでいけばスクロールするということになります。

●329-370行:
ここではNTPサーバーから取得した時刻をOLEDに出力しています。
331-335行がミソで、時刻数値を桁数を合わせて文字列に格納します。

●372-383行:
NTPサーバーと同期するタイミングを決めています。

●385-443行:
ブラウザのニュース記事取得ボタンが押されたら、ここでYahooのRSSサイトへニュース記事をGETリクエストしていきます。

※386行のWEBサイトへのリクエストのタイミングは十分気を付けてください! あまりに早いタイミングでリクエストを送るとDOS攻撃と判断されてアクセス拒否されたり、ひどい場合にはサーバーをダウンしかねません。
自己責任となりますので予めご了承ください。

2行別々のニュースを取得するので、2倍のプログラムが必要になるのは仕方ないですね。
398行のところで、EasyWebSocketライブラリ関数を使用してます。ニュースのホストサーバーアドレスと実際のターゲットページのアドレスを指定して、改行(’¥n’)区切りでテキストを取得して、<title>タグを目安として記事を取得しています。RSSサイトはxml形式でできているので、そのソースコードを取得するとブラウザで見ているものとは違うコードになるので注意が必要です。この部分については前回の記事を参照してください。
404行では、半角の”&”という文字はxml形式のソースコードは”&amp;”となりますので、その文字が来たら全角の”&”に置き換えています。

●446-490行:
Arduino core for ESP8266 WiFi chip のNTPサンプルスケッチをそのまま流用しています。定期的にこの関数を呼んで時刻を補正します。

以上、ソースコードの説明でした。
この解説を記述するのが一番たいへんですね。
これを省いてブログをアップできたらいいのになぁ・・・。

7.コンパイル書込み、実行させる。

では、ルーターの電源を入れて、Arduino IDEのシリアルモニターを起動させた後、コンパイル実行させてみます。

コンパイルに成功するとIDE画面ではこう表示されます。これはIDE1.6.8です。
ws_OLED_SSD1306_06

シリアルモニターではこう表示されます。
ws_OLED_SSD1306_07
上にある動画のように、最初はWAIT・・・と表示されて、ルーターのSSIDとパスワードを通過して、ESP-WROOM-02のローカルIPアドレスと接続されれば、時計表示だけ表示されると思います。

では、スマホのブラウザを起動してください。推奨は最新版Android で最新版Google Chrome です。WebSocket通信が安定して利用できます。
起動したら、URL入力欄にESP-WROOM-02 のローカルIPアドレスを入力してください。

HTMLタグを全て送信し終わるまで10数秒前後お待ちください。
するとシリアルモニターでこう表示されれば、WebSocket通信成功です。
ws_OLED_SSD1306_08

そうしたら、スマホのブラウザ側ではこう表示されていると思います。
ws_OLED_SSD1306_05

そうしたら、ピンク色のWeb GET ON ボタンを押してください。
それから10数秒後にOLED の1行目にYahoo RSS のトップニュースの記事タイトルがスクロールします。
2行目はYahoo RSS のエンターテインメント(芸能)ニュースのトップ記事のタイトルがスクロールします。
ただ、注意してほしいのは、ニュース記事を取得するときにはWebSocket通信を一旦強制切断しないと記事取得できません。ここでは、GETボタンを押すとライブラリ内で強制切断するようにしてます。ニュース記事取得はHTTPリクエストしなければいけないので当然といえば当然です。
ですから、その後ボタンやスライダーで操作する場合は”WS-Reconnect”ボタンを押してWebSocketを再接続しなければいけません。ご注意ください。

スクロール速度もいろいろ変えてみてください。こんな小さいディスプレイで文字が流れるとやっぱりイイですね・・・。
ただ、8×8ドットの倍角ですから、複雑な文字は判読が難しいのは止むをえません。いつか16×16ドットに挑戦してみたいとは思ってます。

白いボタンで記事を変えることもできます。
プログラムを変えれば他の記事を取得することもできると思いますが、ページのソースコードを自分で解析して、キーワードを設定しなければなりません。
このライブラリでは他のサイトの記事では可能かどうかはまだ分かりません。
因みに、Yahoo RSS サイトの記事は個人レベルで使用するには問題ないのですが、商用利用は不可らしいので注意してください。

また、記事は10分毎に取得していますので、その時にスクロールが停止してしまいます。
これを防ぐためには別のArduino などを使って、OELD表示機能と記事取得機能と分けて使うしかないでしょうね。私個人的にはこれで十分だと思っていますが・・・。

以上です。

みなさん、うまく動作しましたでしょうか?

この記事を書くのもホントに大変です。なんとかせねば・・・。

ではまた・・・。

スポンサーリンク

広告 と mgo-tec電子工作 関連コンテンツ
Amazon.co.jp広告




投稿者:

mgo-tec

Arduino, ESP8266, ESP-WROOM-02 等を使って、主にスマホと連携した電子工作やプログラミング記事を書いてます。ライブラリも作ったりしてます。趣味、独学でやってますので、動作保証はしません。 電子回路やプログラミングの専門家ではありません。 畑違いの仕事をしてます。 でも、少しだけ電気の知識が必要な仕事なので、電気工事士や工事担任者等の資格は持ってます。

「ESP-WROOM-02 と 極小OLED (有機EL) で Yahooニュース電光掲示板とNTP時計を表示させてみた。もうIoTですね」への4件のフィードバック

    1. Kasu Kouさん
      コメントありがとうございます。
      こういう声をいただくと、とても励みになります。
      記事をお読みいただいただけでもそれだけで感謝感謝です。
      ありがとうございました!!!

  1. 今後の工作に活用させていただきます。

    これほどの良質なソースコードとノウハウを無償で提供しているとは驚きです。

    ありがとうございました

    1. iectecさん

      当ブログをご覧いただき感謝いたします。
      まだまだ勉強中なので「良質」なんてとんでもないことでございます。
      まだまだ至らないところが有るかも知れませんが、今後とも当ブログをよろしくお願いいたします。

コメントを残す

メールアドレスが公開されることはありません。

*