視認性クッキリ! 大き目ドットの OLED WS0010 で日本語漢字フォント電光掲示板風スクロール&スマホ操作

ESP8266 ( ESP-WROOM-02 )
この記事を書いた当初のESPr Developer(ESP-WROOM-02)のFLASHサイズは4MBでした。
しかし、現在のESPr Developerおよび、ESP-WROOM-02 のFLASHサイズは2MBのものが流通しています。
その場合、東雲フォントなどの1MB以上の大きいサイズのファイルは SPIFFS へアップロードできませんのでご注意ください。
FLASHサイズの調べ方は以下の記事を参照してください。
ESP-WROOM-02 ( ESP8266 ) チップ・メモリ・MACアドレス情報確認方法
(2018/06/22)

 

こんばんは。

前回の記事に引き続き、ストロベリーリナックスさんのキャラクター&グラフィック有機EL ( OLED ) WS0010 を使った記事です。

今回はいよいよ日本語漢字フォントや英文字、半角文字などの文字列を電光掲示板風メッセージボードのようにスクロールさせてみます。
しかも、スマホからリアルタイムに文字列を変更出来て、スクロール速度もリアルタイムに変更できるようにします。
以前の記事でも紹介しましたが、自作ライブラリ EasyWebSocket を使います。

まずは、こちらの動画をご覧ください。

いかがでしょうか。
折角なので、起動直後はキャラクタモードで wait を表示させて、その後グラフィックモードにしてます。

以前の記事でI2C通信の有機ELを使った時と同じ16×16ドットの東雲フォントですが、格段にスクロール速度が速いです。超高速スクロールもできます。

こちらの記事も見ていただければ分かるのですが、SPI通信の高速化を実現した有機ELでもこの速度が限界でした。
しかし、今回のスクロールは更に速度アップが実現できました。
モノクロ有機ELだとしても、ここまでの速さは我ながら驚きです。
シリアルのSPI通信を8bitパラレルに変換した、MCP23S08 のおかげですね。
今までディスプレイをシリアル通信系しか使ったことが無い私にとって、改めて、いかにパラレル通信の方が速いかを実感した次第です。
近いうちに ESP-WROOM-02 の後継機種 ESP-WROOM-32 が出ると思いますが、これならばGPIOでパラレル機器を直接駆動できるかもしれません。 ちょっと期待してます。

さて、話を戻しまして、このグラフィック有機EL の16×16ドット日本語文字列スクロールを解説します。

スポンサーリンク

1.使うもの

前回の記事と同様ですが、はじめて見る方のためにザッと紹介しておきます。

8ビット SPI I/Oエキスパンダ MCP23S08-E/P

秋月電子通商さんで販売。
Microchip製です。
これは5V駆動ですのでご注意ください
SPI通信の最大速度は10MHz です。
ESPr Developer ( ESP-WROOM-02, ESP8266 )ではGPIOピンが少ないので、これを使って増設します。
これで8bitモードパラレルのディスプレイが制御できます。

グラフィック有機ELモジュール100x16

ストロベリーリナックス ( Strawberry Linux )さんで販売しています。
コントローラーは WS0010 を使ってます。
キャラクタモードとグラフィックモードを切り替えできます。

ESPr Developer ( ESP-WROOM-02 開発ボード )
スイッチサイエンス製
Amazon.co.jp

ESP-WROOM-02開発ボード
スイッチサイエンス(Switch Science)

ESPr Developer(ピンソケット実装済)
スイッチサイエンス(Switch Science)

これについては何度も当ブログで紹介しておりますが、日本の電波法をクリアした技適認証済み Wi-Fi モジュールです。ESP-WROOM-02 が安定して動作して、とても使いやすいです。
Arduino IDE で開発ができて、とてもお勧めです。
これの使い方については以下のページを参照してください。
https://www.mgo-tec.com/blog-entry-ss-wroom-howto01.html

0.1uF セラミックコンデンサー
I/Oエクスパンダの MCP23S08 チップの電源ノイズ対策のパスコンです。
積層セラミックで良いです。

サンハヤト製ニューブレッドボード 【SAD-101】
このブレッドボードがお勧めです。

サンハヤト SAD-101 ニューブレッドボード
サンハヤト
¥529(2024/04/27 13:39時点)

その他、ジャンパーワイヤー、パソコン、USBケーブル等

2.接続

※今回の接続は前回とは異なります。
なぜかというと、文字列スクロールを高速化させるために ESP8266 GPIO レジスタ Direct Access を使いましたので、それでは16番ピンが制御できないということがあった為です。
ESP-WROOM-02 ( ESP8266 )のGPIO #16 はDeep-Sleep 時のWake-Upに使用できるピンですが、一応GPIOのINPUT, OUTPUT にも使用できます。
その場合、digitalWrite等を使うしかありませんので、速度に影響を受けないリセット信号線に使う方が良いと思います。

ESPr Developer ( ESP-WROOM-02, ESP8266 )の GPIO #12, #15 は今後 SDカードを使う時の為に空けています。
SDカードを使ったら一杯いっぱいですね。

3.Arduino IDE インストール、 ESP8266ボードの設定、SPIFFSファイルシステムアップローダーのインストール

以下、過去の記事と重複しているところがありますので、わかる方は読み飛ばしてください。

Arduino IDE は現在動作確認がとれているのは、1.6.12 です。
そして、ESP8266 ボードは2.3.0 です。
このインストール方法は以下のページを参照してください。

Arduino IDE に Staging(Stable)版ESP8266 ボードをインストールする方法

ちなみに、古いIDEやESP8266ボードを削除して再インストールする場合は Arduino15フォルダを削除することを忘れないでください。

Arduino IDE のプラグイン、SPIFFSファイルシステムアップローダーのインストール方法は以下のページを参照してください。
Arduino IDE に ESP8266 SPIFFS ファイルシステムアップローダーをインストールする方法

4.Arduino IDE に自作ライブラリをインストール

当方で自作した以下のライブラリをArduino IDEにインストールしていきます。
初めてこのブログを訪れた方はインストールするものが多くて大変かもしれませんね。

EasyWebsocket_SPIFFS ライブラリ

スマホのブラウザ ( Google Chrome や Safari )などでリアルタイムコントロールするためのライブラリです。
WebSocket通信を使います。
2016/10/26現在でBeta ver 1.45 です。
自作ですので、永遠にベータバージョンと思ってください。
このインストール方法は以下のページを参照してください。
自作ライブラリ EasyWebSocket のインストール方法

ShinonomeFONTreadライブラリ

これは、efont さんが保守開発していた、東雲(しののめ)フォントを使う自作ライブラリです。
東雲フォントは16×16ドットの日本語漢字フォントで、JIS第一水準、第二水準を含み、しかも半角カナまで対応している優れものフォントです。
ライセンスはPublic Domain でフリーですので、電子工作にはとても有難い、神様のようなフォントです。
文字コードが Shift_JIS ならばそのまま使用できます。

これは、GitHub のこちらのページにあります。
Beta ver 1.31 (2016/10/26現在) です。

GitHub のZIPファイルをそのまま Arduino IDE へインストール便利な方法は以下のページを参照してください。

GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE )

UTF8toSJISライブラリ

これは、文字コードのUTF-8 形式を Shift_JIS 形式へ変換するライブラリです。
これも自作しました。
これはGitHub のこちらのページにあります。
ZIPファイルのインストール方法は上記と同じです。
これには文字コード変換テーブルファイル、Utf8Sjis.tbl が必要です。
これについては後で述べる、SPIFFSファイルシステムアップロードしていきます。
テーブルファイルはこちらのページにあります。
このライブラリの解説はこちらのページをご覧ください。

5.SPIFFSファイルシステムアップローダーでフォントファイル等をアップロード

まず、以下のファイルを用意してください。

shnm8x16r.bdf (半角東雲フォント) 29KB
shnmk16.bdf (全角ゴシック東雲フォント) 1,110KB
Utf8Sjis.tbl (私の自作、UTF8→Shift_JISコード変換テーブル) 236KB

spiffs_01.txt (EasyWebSocket のHTMLヘッダ)

フォントファイルはShinonomeFONTreadライブラリをインストールすると、サンプルスケッチのOLED_Shinonome の dataフォルダ内に入っています。
もしくは、フォントのオリジナルは/efont/さんのこちらのページにあります。

spiffs_01.txtファイルは先に述べたEasyWebSocketのインストール方法でアップロードしてしまった場合は、フォントファイルとまとめて再度アップロードします。
個々にアップロードはできません。全て一括です。

では、これらをESP8266 のフラッシュにアップロードします。
その方法は以下のページを参照してください。
4M ( 3M SPIFFS ) をシリアルポートでアップロードできない場合のトラブルシューティング( ESP-WROOM-02 ESP8266 )

この3つのファイルサイズは合計で1Mbを超えているので、SPIFFSファイルシステムアップローダーのUSBシリアルではアップロードできません。
OTAでアップロードすることになります。

6.スケッチを入力

では、以下のスケッチを入力してください。

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

/* 
 * ESPr Developer or ESP-WROOM-02 or ESP8266
 * GPIO Expander = MCP23S08 ( Microchip )
 * OLED = WS0010 ( Strawberry Linux )
 * The MIT License (MIT)
 * Copyright (c) 2016 mgo-tec 
 * License reference URL --> https://opensource.org/licenses/mit-license.php
 */
#include <SPI.h>
#include <EasyWebSocket.h>
#include <Hash.h>
#include <UTF8toSJIS.h>
#include <ShinonomeFONTread.h>

//ESP8266 GPIO Register Direct Access 設定
#define PIN_OUT_SET *(volatile uint32_t *)0x60000304 //※PIN_OUTだけの場合は他のピンまで影響を与えるが、PIN_OUT_SETはターゲットピンのみアサインできる
#define PIN_ENABLE_SET *(volatile uint32_t *)0x60000310 //※PIN_ENABLEだけの場合は他のピンまで影響を与えるが、PIN_ENABLE_SETはターゲットピンのみアサインできる
#define PIN(a)  *(volatile uint32_t *)(0x60000328 + (a)*4) //#define設定で引数を使う方法

const char* ssid = "xxxx";
const char* password = "xxxx";

//GPIOエクスパンダ MCP23S08 のSPIインターフェースピン設定
const uint8_t sclk = 14;
const uint8_t mosi =13; //Master Output Slave Input ESP8266=Master,MCP23S08=slave 
const uint8_t MCP_CS = 0;
const uint8_t MCP_RST_pin =  16;
//OLED ピン設定 (8bitモード用)
const uint8_t OLED_RS_pin = 2;
const uint8_t OLED_RW_pin =  5;
const uint8_t OLED_E_pin = 4;

const char* UTF8SJIS_file = "/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* ZenkakuFontFile = "/shnmk16.bdf"; //全角フォントファイル名を定義
const char* HalfFontFile = "/shnm8x16r.bdf"; //半角フォントファイル名を定義

enum { MaxTxt = 512 , DispChar = 12}; //MaxTxt = スクロールする文字列の最大数
                                      //DispChar = ディスプレイに表示できる半角文字の数

uint8_t font_buf[MaxTxt][16];
uint8_t SnnmDotOut[DispChar][16];
uint8_t Next_buf[DispChar][16];
uint8_t dummy_font_buf[16];

UTF8toSJIS u8ts;
ShinonomeFONTread SFR;
EasyWebSocket ews;

String html_str1 = "";
String html_str2 = "";
String html_str3 = "";
String html_str4 = "";
String html_str5 = "";
String html_str6 = "";
String html_str7 = "";

String ret_str;
String txt = "";

int PingSendTime = 30000;

uint8_t sj_txt[MaxTxt];
uint16_t sj_cnt = 0;
uint16_t sj_length;
uint32_t SCLtime;
uint16_t Scrolle_speed = 8;
uint8_t scl_cnt = 0;

char c1[64] = "----- W a i t -----."; //キャラクタモードの場合、1列最大64文字格納。そのうち20文字表示
char c2[17] = "Connecting Wi-Fi"; //キャラクタモードの場合、1列最大64文字格納。そのうち20文字表示

//*************セットアップ***************************
void setup() {
  int i, j;
  Serial.begin(115200);
  Serial.println();
  delay(10);
  //ESP8266 GPIO Register Direct Access 設定。ターゲットピンのみアサイン。
  PIN_OUT_SET = (1<<OLED_RW_pin | 1<<OLED_E_pin | 1<<OLED_RS_pin | 1<<MCP_CS);
  PIN_ENABLE_SET = (1<<OLED_RW_pin | 1<<OLED_E_pin | 1<<OLED_RS_pin | 1<<MCP_CS);
  PIN(MCP_CS) = 0; //HIGH
  PIN(OLED_E_pin) = 1; //LOW
  pinMode(MCP_RST_pin, OUTPUT);
  digitalWrite(MCP_RST_pin, HIGH);

  MCP23S08_Ini();
  OLED_Character_Ini();
  
  for(i=0; i<64; i++){ //1列最大64文字格納。そのうち20文字表示
    OLED_DataWrite(c1[i]);
  }
  for(i=0; i<17; i++){ //1列最大64文字格納。そのうち20文字表示
    OLED_DataWrite(c2[i]);
  }
  ews.AP_Connect(ssid, password);
  delay(1000);

  html_str1 = "<body style='background:#000; color:#fff;'>\r\n";
  html_str1 += "<font size=3>\r\n";
  html_str1 += "ESP-WROOM-02(ESP8266)\r\n";
  html_str1 += "<br>\r\n";
  html_str1 += "EasyWebSocket Beta1.45 Sample\r\n";
  html_str1 += "</font><br><br>\r\n";
  html_str1 += ews.EWS_Status_Text2("WebSocket Status","#555", 20,"#FF00FF");
  html_str1 += "<br><br>\r\n";
  html_str1 += ews.EWS_TextBox_Send("txt1", "Pen Pineapple Apple Pen...","送信");
  html_str1 += "<br>\r\n";

  html_str2 = "<br>Scrolle Speed\r\n";
  html_str2 += ews.EWS_Canvas_Slider_T("SPEED",200,40,"#777777","#0000ff");
  html_str2 += "<br><br><br><br>\r\n";

  html_str3 = ews.EWS_WebSocket_Reconnection_Button2("WS-Reconnect", "grey", 200, 40, "black" , 17);
  html_str3 += "<br><br>\r\n";  
  html_str3 += ews.EWS_Close_Button2("WS CLOSE", "#ccc", 150, 40, "red", 17);
  html_str3 += ews.EWS_Window_ReLoad_Button2("ReLoad", "#ccc", 150, 40, "blue", 17);
  html_str3 += "</body></html>\r\n";

  for(i=0; i<16; i++) { //初期化しておく
    for(j=0; j<16; j++){
      font_buf[i][j] = 0;
      SnnmDotOut[i][j] = 0;
      Next_buf[i][j] = 0;
    }
  }

  OLED_Graphic_Ini();
  
  SCLtime = millis();
}
//*********メインループ********************
void loop() {
  ews.EWS_HandShake(html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7);
  
  int i;

  if(ret_str != "_close"){
    ret_str = ews.EWS_ESP8266CharReceive(PingSendTime);
    if(ret_str != "\0"){
      if(ret_str != "Ping"){
        if(ret_str[0] != 't'){
          int ws_data = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30);
          switch(ret_str[4]){
            case 'S':
              Scrolle_speed = floor((200-ws_data)/(200/40)); //スマホスライダー値(0-200)で、値を(40-0)に変更したい場合
              Serial.println(Scrolle_speed);
              break;
          }
        }else if(ret_str[0] == 't'){
          txt = "  ";
          txt += ret_str.substring(ret_str.indexOf('|')+1, ret_str.length()-1);
          txt += "\0";
          sj_cnt = 0;
          u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, txt, sj_txt, &sj_length);
          SFR.SjisToShinonome16FontRead_ALL(ZenkakuFontFile, HalfFontFile, 0, 0, sj_txt, sj_length, font_buf);
          for(i=0; i<sj_length; i++){
            Serial.write(sj_txt[i]);
          }
          Serial.println();
          for(i=0; i<16; i++) dummy_font_buf[i] = font_buf[0][i];
        }
      }
    }
  }else if(ret_str == "_close"){
    ret_str = "";
  }
  
  if(millis()-SCLtime > Scrolle_speed){
    Scroller_8x16x12_Dot_Replace(DispChar, Next_buf, SnnmDotOut, dummy_font_buf, font_buf, sj_length, &scl_cnt, &sj_cnt);
  
    OLED_8x16x12_Font_DisplayOut(DispChar, SnnmDotOut);
    
    SCLtime = millis();
  }
}
//*********GPIOエキスパンダMCP23S08初期化関数********************
void MCP23S08_Ini(){ 
  SPI.begin();
  SPI.setFrequency(10000000); //MCP23S08 Max 10MHz
  SPI.setDataMode(SPI_MODE0);

  digitalWrite(MCP_RST_pin, HIGH);
  delay(10);
  digitalWrite(MCP_RST_pin, LOW);
  delay(100);
  digitalWrite(MCP_RST_pin, HIGH);
  delay(50);
  
  PIN(MCP_CS) = 1;
    delay(1);
    SPI.write(B01000000);//デバイスオペコードB01000000 & チップaddress 000
    SPI.write(0x06);//MCP設定レジスタIOCON 
    SPI.write(B00100000);//シーケンシャル動作禁止、SDAスルーレート許可、ハードウェアアドレスピン禁止、INT出力LOW、アクティブドライブ
    delay(1);
  PIN(MCP_CS) = 0;
  
  PIN(MCP_CS) = 1;
    delay(1);
    SPI.write(B01000000);//デバイスオペコードB01000000 & チップaddress 000
    SPI.write(0x00);// I/O方向レジスタ(IODIR)設定
    SPI.write(B00000000);//8pin ALL OUTPUT
    delay(1);
  PIN(MCP_CS) = 0;
}
//*********有機ELキャラクタモード初期化関数********************
void OLED_Character_Ini(){ //OLED WS0100 初期化  
  PIN(OLED_RW_pin) = 1;
  PIN(OLED_E_pin) = 1;
  PIN(OLED_RS_pin) = 1;

  //OLEDリセット
  delayMicroseconds(10);
  OLED_CommandWrite(B00000001);//Display Clear
  delay(10);
  OLED_CommandWrite(B00010011);//Character MODE ON, Power is turned OFF
  delay(10); //リセットは一旦電源を切るべし
  OLED_CommandWrite(B00001000);//Display OFF, Cursor OFF, Brinking OFF 
  delay(1000);
  //ここからディスプレイ初期化設定
  OLED_CommandWrite(B00010111);//Character MODE ON, Power is turned ON
  delay(10);
  OLED_CommandWrite(B00111000);//Function Set 8bit-mode 2Line-display
  delay(10);
  OLED_CommandWrite(B00001111);//Display On, Cursor ON, Brinking on 
  delay(10);
  OLED_CommandWrite(B00000001);//Display Clear
  delay(10);
  OLED_CommandWrite(B00000010);//Return HOME
  delay(10);
  OLED_CommandWrite(B00000110);//Entry Mode Set, Increment, No-Shift
  delay(10);
}
//*********有機ELグラフィックスモード初期化関数********************
void OLED_Graphic_Ini(){
  PIN(OLED_E_pin) = 1;
  PIN(OLED_RW_pin) = 1;
  PIN(OLED_RS_pin) = 1;

  //OLEDリセット
  delayMicroseconds(10);
  OLED_CommandWrite(B00000001);//Display Clear
  delay(10);
  OLED_CommandWrite(B00010011);//Power is turned OFF, Character MODE ON
  delay(10); //リセットは一旦電源を切るべし
  OLED_CommandWrite(B00001000);//Display OFF, Cursor OFF, Brinking OFF 
  delay(1000);
  //ここからOLED初期化設定
  OLED_CommandWrite(B00011111);//Graphic MODE ON, Power is turned ON
  delay(20);
  OLED_CommandWrite(B00111000);//Function Set 8bit-mode 2Line-display
  delay(10);
  OLED_CommandWrite(B00001100);//Display On, Cursor OFF, Brinking OFF
  delay(10);
  OLED_CommandWrite(B00000001);//Display Clear
  delay(10);
  OLED_CommandWrite(B00000110);//Entry Mode Set インクリメント&シフトなし
  delay(10);
}
//*********電光掲示板風スクロール関数 8x16ドット 12文字用********************
void Scroller_8x16x12_Dot_Replace(uint8_t Disp_char, uint8_t next_buff1[][16], uint8_t scl_buff1[][16], uint8_t* Orign_buff1, uint8_t font_buf1[][16], uint16_t Length, uint8_t* scl_cnt1, uint16_t* sj_cnt1){
  int8_t i, j;
  
  if(*scl_cnt1 == 8){
    *scl_cnt1 = 0;
    (*sj_cnt1)++;
    if(*sj_cnt1 >= Length){
      *sj_cnt1 = 0;
    }
    for(i=15; i>=0; i--) Orign_buff1[i] = font_buf1[*sj_cnt1][i];
  }

  for(i=15 ; i>=0 ; i--){ //まず、一番左側文字をビットシフト
    next_buff1[Disp_char-1][i] = ( next_buff1[Disp_char-1][i] | ( scl_buff1[Disp_char-1][i] & B10000000 ));
    scl_buff1[Disp_char-1][i] = scl_buff1[Disp_char-1][i]<<1;
    scl_buff1[Disp_char-1][i] = ( scl_buff1[Disp_char-1][i] | (( Orign_buff1[i] & B10000000 )>>7));
    Orign_buff1[i] = Orign_buff1[i]<<1;
  }
  for(i=Disp_char-2 ; i>=0 ; i--){ //次にその他文字をビットシフト
    for(j=15 ; j>=0 ; j--){
      next_buff1[i][j] = ( next_buff1[i][j] | ( scl_buff1[i][j] & B10000000 ));
      scl_buff1[i][j] = scl_buff1[i][j]<<1;
      scl_buff1[i][j] = ( scl_buff1[i][j] | (( next_buff1[i+1][j] & B10000000 )>>7));
      next_buff1[i+1][j] = next_buff1[i+1][j]<<1;
    }
  }
  (*scl_cnt1)++;
}
//*********有機EL 漢字フォント表示出力関数********************
void OLED_8x16x12_Font_DisplayOut(uint8_t len, uint8_t buf[][16]){
  uint8_t i, j, k;
  uint8_t Rbuf[len][16];
    
  for(k=0; k<len; k++){ //-90 Dot Rotation
    for(j=0; j<8; j++){
      for(i=0; i<8; i++){
        bitWrite(Rbuf[k][j], i,bitRead(buf[k][i], 7-j));
      }
    }
    for(j=8; j<16; j++){
      for(i=8; i<16; i++){
        bitWrite(Rbuf[k][j], i-8,bitRead(buf[k][i], 15-j));
      }
    }
  }
 
  for(j=0; j<len; j++){ //OLED Display OUT
    OLED_XYset(j*8, 0);
    for(i=0; i<8; i++){
      OLED_DataWrite(Rbuf[j][i]);
    }
    OLED_XYset(j*8, 1);
    for(i=8; i<16; i++){
      OLED_DataWrite(Rbuf[j][i]);
    }
  }
}
//*********有機EL XY設定関数********************
void OLED_XYset(uint8_t x, uint8_t y){
   OLED_CommandWrite(0x80 + x);  
   OLED_CommandWrite(0x40 + y);
}
//*********有機EL 設定コマンド送信関数********************
void OLED_CommandWrite(uint8_t b){
  PIN(OLED_RS_pin) = 1; //1=LOW
  MCP23S08_SpiCommandWrite(b);
  PIN(OLED_E_pin) = 0;//EピンをHIGH→LOWへ変化させると書き込み完了
  PIN(OLED_E_pin) = 1;
}
//*********有機EL データ送信関数********************
void OLED_DataWrite(uint8_t b){
  PIN(OLED_RS_pin) = 0; //0=HIGH
  MCP23S08_SpiCommandWrite(b);
  PIN(OLED_E_pin) = 0;//EピンをHIGH→LOWへ変化させると書き込み完了
  PIN(OLED_E_pin) = 1;
}
//*********GPIOエキスパンダMCP23S08コマンド設定関数********************
void MCP23S08_SpiCommandWrite(uint8_t b){
  PIN(MCP_CS) = 1; //1=LOW
    SPI.write(B01000000);
    SPI.write(0x0A);
    SPI.write(b);
  PIN(MCP_CS) = 0; //0=HIGH
}

(Web上でソースコードを配布する場合はライセンスを記述しなければ、自由に配布できないということですので、MITライセンスとしておきます。)

【解説】

●9-13行目:
9行目は Arduino core for ESP8266 標準装備のSPIライブラリインクルードです。
10-11行目は自作ライブラリ EasyWebSocket_SPIFFS のインクルードとそれに関連する Hash ライブラリのインクルードです。
12-13行目は自作ライブラリのインクルードです。

●16-18行:
ここは今回のポイントです。
文字列の電光掲示板スクロールではGPIOの反応速度の高速化がキモです。
よって、以前の記事でSPI高速化を図ったことと同じく、pinMode や digitalWrite を使わずに GPIO レジスタ Direct Access を使います。
そして、もう一つの肝があります。
以前の記事では、PIN_OUT, PIN_ENABLE だけだったのですが、その指令はGPIOピン全てにピン設定が影響してしまうことでした。
今回は、PIN_OUT_SET, PIN_ENABLE_SET 指令を使うことによって、ターゲットのピンだけを設定し、他のピンには影響を与えないということができるようになりました。
SPI通信では、#13, #14 ピンに影響したら動かなくなってしまうので、これは大事な設定です。
これについては、ツイッターで@arms22 さんからアドバイスを頂いて、改めてデータシートを見直して発見できました。
@arms22さん、キッカケを作って頂き、ありがとうございました。
m(_ _)m
それと、18行目がポイントで、#define を使う場合に引数を使いたい場合が多々あるので、このようにすれば(a)に好きな整数を代入することができます。
これを知った時には目から鱗でした。
ちなみに、このレジスタアドレスの詳しい動作はESPRESSIF社の以下のサイト
ESPRESSIF Support Documents
の esp8266-technical_reference_en.pdf というファイルの最後の方に記述されていますので参照してみてください。

●20-21行;
xxxxのところを、ご自分のルーター環境に合わせて書き換えてください。

●24-31行;
ESPr Developer ( ESP-WROOM-02, ESP8266 ) のGPIO ピン設定です。
16番ピンは GPIO レジスタ Direct Access ができないため、スピードを求めないリセット信号ピンとして使用します。

●33-35行:
ESP8266 のSPIFFSファイルシステムでアップロードしたフォントファイル等を定義します。

●45-47行:
自作ライブラリのクラス名を定義してます。好きな名前にすることができます。

●60行:
スマホブラウザとWebSocket通信する場合、スリープしていないか、コネクションが切れていないかチェックするために、ESP8266からPINGコマンドを送信しています。
この送信間隔が30秒毎ということです。

●69-70行:
電源投入直後、有機EL ( OLED ) WS0010 をキャラクタモードで起動した際に表示する文字列です。
最終的にはグラフィックモードにしますが、折角なのでキャラクタモードで起動してみます。
69行目で配列数を64としたのは、以前の記事で1行に64文字格納できて、実際に表示できるのは20文字というところから、とりあえず64としてみました。
メモリを節約したいのならば20以下で良いと思います。
70行目ではそうしてます。

●79-80行:
ここで、ESP8266 の GPIO レジスタ Direct Access のターゲットピンをOUTPUTに設定し、有効にしてます。
先ほど紹介したように、PIN_OUT だけだと全てのピンに影響を与えてしまうので、OUT も ENABLE も _SET 指令にします。

●81-84行:
レジスタ Direct Access で、4番ピンをLOW にしたい場合は
PIN(4) = 1;
とすれば良いです。
ただし、1がLOW, 0がHIGHですので注意してください。
83-84行では、16番ピンは レジスタDirect Accessが使えないので、Arduino 標準関数を使用してます。

●86-87行:
これはGPIOエキスパンダのMCP23S08 と OLEDキャラクタモードの初期化です。177-232行で関数化してます。
これの詳細は前回前々回の記事を参照してください。

●89-94行:
キャラクタモードのOLEDに文字列を出力してます。
これは初期起動時のみ表示されるものです。

●95行:
ここで、ルーター(アクセスポイント)とESPr Developer ( ESP-WROOM-02, ESP8266 )と接続します。

●98-117行:
ここでは、スマホブラウザに表示する画面のHTMLタグをString文字列に格納してます。
関数の使い方は複数のページに散らばっていますので、以下のページを参考にしてください。
要するに、ホームページを作っているようなものです。
ただ、HTMLやJavaScriptのヘッダは先ほどSPIFFSでアップロードした spiffs_01.txt ファイルに格納されてます。

beta ver 1.45 → ライブラリを更新しました
beta ver 1.37 → EasyWebSocket ライブラリ Beta 1.37 をアップしました。
beta ver 1.35 → https://www.mgo-tec.com/blog-entry-wroom-websocket-messageboard-05.html

beta ver 1.3 → https://www.mgo-tec.com/blog-entry-easywebsocket-beta13.html

●127行:
ここで、有機EL ( OLED )をグラフィックモードに切り替えています。
234-258行で関数化してます。

●133行:
自作ライブラリEasyWebSocketの関数です。
常にここを通ることによって、ブラウザとコネクションが取れているかを監視してます。
ブラウザからGETリクエストが来ると spiffs_01.txt ファイルと98-117行で指定したHTMLタグ文字列をマージしてブラウザに送信してWebページを表示させます。

●137-166行:
先に紹介した、以前の記事のEasyWebSocket サンプルスケッチとほぼ同じですが、154-160行が異なります。
ここでは、ブラウザのテキストボックスから送信されてきたUTF-8形式文字列を154行でShift_JISコードに変換して、それからSPIFFSファイルシステムにある東雲フォントのビットマップを読み込んで、font_bufに格納してます。
一旦、ESP8266のSRAMに格納してOLEDに出力しないと高速スクロールは実現できません。

●169行;
ここは今回新たに作り直した、文字ドットスクロール関数です。
260-287で関数化してます。
今回のポイントは、この関数1行をメインループ内に置けば、通過する度に SnnmDotOut のドットが1ドットずつ左へスクロールしてくれることです。
このOLED は8×16ドットを12文字分しか表示できません。
スクロールするにはグローバル宣言領域で、ダミーのバイト配列( Next_buf, dummy_font_bufなど )を用意しなければなりませんでした。
でも、今回は1行で済んだので画期的だと思います。
いずれはもっと簡単にしてライブラリ化していきたいと思ってます。

●171行:
ここでスクロールしたピクセルのバイト列を有機EL ( OLED )に出力してます。
289-316行で関数化してます。
ポイントは、この有機EL はバイト列を90度右へ回転させることです。
これが表示速度を低下させてしまう要員になりますね。

その他は前回および前々回の記事を参照してください。

7.コンパイル実行

では、まず、ご自分のルーター(アクセスポイント)起動させておいてください。
そして、ESPr Developer ( ESP-WROOM-02, ESP8266 )とコネクションできるようにしておいてください。
そして、スマホもルーターとWi-Fi接続しておいてください。
その後、コンパイル実行させてみてください。

Arduino IDE のシリアルモニター(速度115200bps)で起動させ、ESPr Developer のローカルIPアドレスが表示されたら、動画にあるようにGoogle Chrome などのブラウザのURL入力欄に ESPr Developer のローカルIPアドレスを入力してください。

まず、Webページが表示されますが、リアルタイム通信のWebSocketハンドシェイクが確立するまでに数十秒待ってください。
すると下図の様に表示されます。

テキストボックスに好きな文字、特に日本語漢字を入力して送信ボタンを押してください。
すると有機EL ( OLED )ディスプレイに文字列が表示されてスクロールされると思います。
そうしたら、スクロールスピードを調節してみてください。
先に紹介した動画のように超高速スクロールができると思います。
これだけ16×16ドットの漢字がスクロールしてくれると気持ちイイですね。

今回はシリアルのSPI通信をパラレル変換して電光掲示板を作りましたが、これが最初からパラレルだとどれだけ高速の処理ができるのだろうと思ってしまいました。
そのためには多量のGPIOが必要です。
それは、ESP-WROOM-32 に期待しましょう!!

有機EL でこれだけ大き目の文字の電光掲示板ができれば、いろいろな場面で活躍できそうですね。

では、今回はこんなところです。
次回は Yahoo! ニュースでも自動で取得して表示させてみようと思います。

ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/04/27 03:55時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/04/27 15:18時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/04/26 21:54時点)

コメント

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