ESP-WROOM-02 で WebSocket リアルタイム制御 日本語電光掲示板作成(単体LEDマトリックス版)

記事公開日:2016年2月28日
最終修正日:2017年3月31日

スポンサーリンク

こんばんは。

ようやくここまでこぎ着けました。
スマートフォンのブラウザからWi-Fiリアルタイム制御してLEDマトリックス電光掲示板をコントロールすることができるようになりました。
以前の記事 では平仮名まででしたが、今回はスゴイですよ。
JIS第一水準、第二水準漢字に加えて、JIS 13区の記号、半角英数字、半角カナまでスクロール表示できるようになりました。
そして、何よりも部品点数が極端に少なくて、ESP-WROOM-02 ( ESP8266 ) と I2C制御 LEDドットマトリックス だけなんです。たった2点のモジュールだけです。

ただ、今回紹介するものは、電光掲示板といえども、8X8 LEDドットマトリックスは単体のみです。
段階を踏んで、次回の記事ではLEDマトリックスを4つ連結したものをアップする予定です。こちらは実用レベルだと思っています。

去年製作したこちらの記事の 電光掲示板では、Arduino UNO と Ethernet Shield、EEPROM、SDカードなどを使用していて、寸法も結構大きなものでした。
今回のものは究極に部品点数が少なく、しかも小さいです。

それに、以前はHTTP GETリクエスト、レスポンスだけの通信だったので、文字列を送るのに数十秒かかってしまい、操作性がイマイチでしたが、今回は最初のコネクション以外はストレスを感じないレベルまで来ました。

今回は当方で独自に作った以下のライブラリ等をガッツリ使っています。

●EasyWebSocket BETA 1.3 ライブラリ
→ こちらの記事参照
●UTF-8 → Shift_JIS 変換テーブル
こちらの記事参照
●ESP-WROOM-02 用 UTF-8 → Shift_JIS 変換ライブラリ
こちらの記事参照

そして、日本語フォントは過去の記事で何度も紹介させていただいた、フリーの8×8 ドットの 美咲フォント を使用させていただきました。
このフォントは8×8ドットですが、実際は読みやすくするために7×7 ドットに収まっています。複雑な文字は読みにくいですが、遠目で見ると意外と判読可能です。
電子工作家にとって、これはほんとに素晴らしいフォントです。
開発者の門真さんに敬意を表します。

動作の様子はこんな感じです。

いかかですか?
ここまで読めればヨシだと思います。
スクロールスピードや明るさコントロールもリアルタイムで追従してくれてますね。
文字列の読み込みにはほんの少しタイムラグがありますが、気にならないレベルだと思います。
1つ、難点は、一度に送信できる文字列の文字数が全角40文字(120バイト)までというところです。
ここは今後の課題ですね。
技術的にはそれ以上の文字数を送ることは可能なのですが、WebSocket の仕様を更に研究しなければなりませんので、いつか対応してみたいと思っています。

では、この作り方を解説していきます。
初めてEasyWebSocket を利用する方はいろいろなプラグインやライブラリをインストールするためにいろいろなページを見ることになると思いますので、覚悟しておいてください。

1.準備するもの

●ESPr Developer ( ESP-WROOM-02 開発ボード ) (スイッチサイエンス)

何度も紹介しておりますが、当ブログの一押しおすすめボードです。
Amazon.co.jp ではこんな感じで販売しております。

かなり売れていますので、在庫切れの場合がありますので、ご注意ください。
これの使い方についてはこちらのページで紹介しております。

●I2C通信 8×8 Mini LED マトリックス基板 (Adafruit)

これも過去の記事で紹介させていただきました。
青色は結構高価です。
Amazon.co.jp ではこんな感じで販売されています。

過去の記事では、秋月電子通商さんから、LEDドライバ HT16K33 ボードを購入して、LEDマトリックスを別途購入して、独自基板を作ったりしましたが、結局のところ、Adafruitさんのこのボードを使ったほうがとても簡単です。千円以内で収まってくれればいいんですけどね・・・。

2.接続する

接続方法は至ってシンプル。
こんな感じです。

Adafruit8x8led11

先にも紹介しましたが、ESP-WROOM-02開発ボード自体の使い方やピンヘッダの接続方法はこちらのページをご参照ください。

Adafruit 8×8 ミニLEDマトリックス基板の組み立て方はこちらのページ をご参照ください。

I2C通信なので、SDA と SCL 信号線を間違えないようにしてください。

LEDマトリックス基板はESP-WROOM-02の3.3V電源でOKです。
LEDドライバのHT16K33はデータシートによると推奨は5Vですが、問題なく動作しています。

また、I2C 信号線にはLEDマトリックス基板側にプルアップ抵抗が内蔵されてますので、直に接続でOKです。

3.最新版Arduino IDE、ESP8266ボードプラグインのインストール

Arduino IDE は必ずArduino cc ページのバージョン 1.6.5 をインストールしてください。
他のバージョンでは正常に動作しませんのでご注意ください。
Arduino IDE は Arduino.cc のこちらのページにあります。

ESP-WROOM-02 ( ESP8266 ) をArduino化するプラグイン、Arduino core for ESP8266 WiFi chip のESP8266ボードのインストール方法はこちらのページをご覧ください。

4. SPIFFSファイルシステムアップローダープラグインのインストール

ESP-WROOM-02 のユーザーフラッシュメモリにフォントデータをアップロードするプラグイン、SPIFFSファイルシステムアップローダーのインストール方法はこちらのページをご覧ください。

5.EasyWebSocket BETA 1.3 ライブラリのインストール

私の自作したArduino IDE ライブラリ、EasyWebSocket BETA 1.3 のインストール方法はこちらのページをご覧ください。

そして、EasyWebSocket BETA 1.3 Sample スケッチを開いて別名で保存しておきます。
そのスケッチフォルダの /data フォルダを開いて、spiffs_01.txt ファイルのローカルIPアドレスをご自分のルーターで割り当てたESP-WROOM-02 のローカルIPアドレスに書き換えてください。

そうしたら、サンプルスケッチのプログラムを全て消去しておき、後で記載してあるサンプルスケッチを入力できるようにしておいて、上書き保存しておきます。

6.UTF-8 → Shift_JIS 文字コード変換ライブラリのインストール
および、
UTF-8 Shift_JIS 変換テーブルのダウンロード

私の自作したArduino IDE ライブラリ、UTF-8  Shift_JISコード変換ライブラリのインストール方法はこちらの記事をご覧ください。
その時に、Utf8Sjis.tbl ファイルをダウンロードしておき、前項で保存しておいたサンプルスケッチフォルダの /data フォルダにコピーしておいてください。

Utf8Sjis.tbl ファイルはUTF-8 文字コードをShift_JIS 文字コードへ変換するテーブルで、バイナリ形式ファイルです。
この詳細はこちらのページを参照してください。

7.美咲フォントの全角フォントをダウンロード

当ブログで何度も紹介させていただいている、フリーの 8×8ドット日本語漢字フォント、美咲フォントをダウンロードします。
全角フォントは こちらのページ の PC-E500 SCRNJPN.FNT 形式 をダウンロードして解凍しておいてください。
その中から MSKG13KU.FNT というファイルを先に述べたスケッチフォルダの /data フォルダにコピーしておいてください。

8.美咲フォントの半角フォントをダウンロード

半角フォントは同じ美咲フォントのページの FONTX2 形式 をダウンロードして解凍しておいてください。
その中から 4X8.FNT というファイルを先に述べたスケッチフォルダの /data フォルダにコピーしておいてください。

9.SPIFFSファイルシステムアップローダーでフォントファイル等をフラッシュにアップロードする

先に述べた空白サンプルスケッチのスケッチフォルダの中の /data フォルダには以下の4つのファイルが入っていることを確認します。

spiffs_01.txt
Utf8Sjis.tbl
MSKG13KU.FNT
4X8.FNT

4つのファイル合計のサイズは 301 kB 程度です。
事前にArduino IDE のツールメニューのFlash Size を 4M(1M SPIFFS) に選択してあることを確認しておいてください。
これをSPIFFSファイルシステムアップローダーでESP-WROOM-02 のフラッシュのユーザー領域へアップロードします。
下図のようなところをクリックするとアップロードが始まります。

EWS_Beta13_08

白い点々の進行が止まったらアップロード完了です。
かなり時間がかかりますので、気長にお待ちください。

10.プログラムの流れ

では、スケッチを紹介する前に、文字列をスクロールするプログラムの流れを説明します。
(WebSocket コネクション部分は省略)

①ブラウザからUTF-8文字コードで文字列送信
WebSocketの仕様では、文字列はUTF-8文字コードのみしか送れないことに注意。
Shift_JISでは送れません

②ESP-WROOM-02 で文字列受信
受信した文字列はWebSocket仕様によりマスク処理されているので、それをEasyWebSocketライブラリ中でマスク解除してUTF-8文字コードを取り出します。
(WebSocketのマスク処理についてはこちらの記事を参照してください)

③自作ライブラリでUTF-8文字コードをShift_JISへ変換
この時に、Utf8Sjis.tbl ファイルを読み込んで、Shift_JISコードを抽出します。
ここの部分の詳細はこちらのページを参照してみてください。

④Shift_JISコードから美咲フォントのアドレスを計算し、美咲フォントファイルからビットマップフォントデータ8バイトを抽出。
例えば、平仮名の 「う」 という文字を美咲フォントから抽出する場合を説明すると、Shift_JISコードは 0x82A4 となります。
そのコードから美咲フォントアドレスを割り出していきます。これの方法はちょっとコツが要ります。

美咲フォントをフリーのバイナリエディタで解析していくと、Shift_JISコード並びでも、空白のコード部分をカットして、Shift_JISコードが存在している部分だけ詰めてビットマップが書き込まれています。
それを踏まえて、Shift_JIS漢字コード表から 「う」 という文字がバイナリ列のどの辺にあるかを予想して、適当な8バイトを抽出して、エクセルなどでビットマップ表示させてみます。その文字が何かが分かったら、アドレスと比較して 「う」 の位置の見当を付けていきます。

バイナリエディタStirlingで平仮名 「う」 が見つかると、こんな感じです。
ws_message02

そこのアドレスは 0x978 だと分かりました。
それをエクセルでビットマップ表示させてみるとこんな感じになります。
ws_message01

右に90度回転されていますね。
全角の美咲フォントはすべてこういう表示になっていますので、後で左90度に回転するようにビットの並び替えをしないといけません。

そして、Shift_JISコードの0x82A4 から、美咲フォントのアドレス 0x978 を割り出すには

フォントアドレス = ( ( 0x82A4 – 0x8140 ) – 53 ) * 8 = 0x978

という式が成り立ちます。
0x8140 はShift_JIS の先頭アドレス(スペース)です。
53という数値は、結果が0x978になるように割り出した数値です。
8を掛けているのは、ビットマップが8バイトだからです。
これを全文字についてひたすら割り出していくのですが、しばらく計算しているとある法則が分かってきますので、そうしたらシメたものです。
そんな感じで割り当てたものが後述するスケッチになります。

⑤ビットマップ文字をスクロールさせる。

電光掲示板のビットマップスクロールの抜粋した例はこんな感じになります。これは1文字分をスクロールします。ただ、これを実行させても一瞬にして終わってしまうので、適度に時間制御してやる必要があります。

for(int i=0 ; i<8 ; i++){
  bitWrite( Next_buff[i],7, bitRead( scl_buff_1[i],7)); //7番目のビットの取り置き
  scl_buf[i] = scl_buf[i]<<1;
  bitWrite( scl_buf[i],0, bitRead( timp_buf[i],7)); //7番目のビットの取り置き
  timp_buf[i] = timp_buf[i]<<1;
}

これを解説すると、まず、プログラム上に、実際にLEDに出力するバイト配列と仮想のバイト配列timp_bufとNext_bufを用意します。
LED出力するバイト配列は scl_buf です。

そして、timp_buf 配列に美咲フォント1文字分(8バイト)を代入します。

ws_message03

次に、空のNext_buf配列のゼロビット目に出力中のLEDビット配列の7ビット目をコピーします。これをやらないと、次のビットシフトでビットが捨てられてしまうので注意です。

ws_message04

次に出力中のLEDビット配列の7bit分を1bit左へシフト。7番目のビットは捨てられます。

ws_message05

次にtimp_buf配列の7bit目をLEDビット配列のゼロ番目にコピーします。

ws_message06

timp_buf配列の7bit分を左へ1bitシフト

ws_message07

以上の作業を8回繰り返したら、timp_buf配列に次の文字を代入していきます。

⑥LEDマトリックスに出力させる

Adafruit 8×8 mini LED マトリックスは こちらのページ の記事を参照していただけると分かると思いますが、かなり特殊なビット操作をしてやらなければなりません。このデバイス用に関数を作ってやる必要があります。
ビットを整えることが出来たら、I2C通信へビットを送出すればLEDが点灯します。

11.スケッチを入力する

では、いよいよ以下のサンプルスケッチをIDEに入力していきます。

5の項目で行った、EasyWebSocket サンプルスケッチを空白にしたところに以下のサンプルスケッチを入力します。
このサンプルスケッチはMITライセンスです。
配布、改変、商用利用は自由。ただし無保証です。ライセンス表記はしてくださいというものです。

/* WebSocketリアルタイムコントロール 8x8LEDドットマトリックス単体 電光掲示板サンプルスケッチ
 * ESP-WROOM-02(ESP8266) および Adafruit I2C 8x8 mini LED matrix専用
 * The MIT License (MIT)
 * Copyright (c) 2016 MGO-tec 
 * License reference URL --> https://opensource.org/licenses/mit-license.php
 */
#include <EasyWebSocket.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <Wire.h>
#include <FS.h>
#include <UTF8toSJIS.h>

#define LDaddrs1 (0x70)  //LEDドライバーHT16K33 アドレス

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 = "/4X8.FNT"; //半角フォントファイル名を定義

uint8_t LedDot0[8] = {0,0,0,0,0,0,0,0};

EasyWebSocket ews;

UTF8toSJIS u8ts;

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 scl_txt="";

uint8_t scl_cnt = 0;
uint8_t fnt_cnt = 0;
bool FntReadOK = true;

uint8_t LedDotOut[8] = {0,0,0,0,0,0,0,0};
uint8_t Next_buf[8] = {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_cnv2[8] = {0,0,0,0,0,0,0,0};
uint8_t AdaMini_cnv[8] = {0,0,0,0,0,0,0,0};
uint8_t LedDot_mini_cnv[8] = {0,0,0,0,0,0,0,0};
uint8_t sj_txt[127];
uint8_t sj_cnt = 0;
uint16_t sj_length;

int PingSendTime = 30000;

long SclTime;

int SclSpeed = 100;
bool Scl_Stop = false;
uint8_t Direction = 0;
uint8_t brightness = 0;

uint8_t han_zen = 0;

boolean sjis_txt_in = false;

//***********セットアップ***************************************************
void setup() 
{
  Wire.begin(); // initialise the connection
  Wire.setClock(400000L); //HT16K33のクロックはMax 400kHz
  LED_Driver_Setup( LDaddrs1, 1); //HT16K33システムオシレータ ON
  LED_Driver_Blink( LDaddrs1, 1, 0); //blink_Hz=0 点滅off, 1は2Hz, 2は1Hz, 3は0.5Hz, on_off=0は消灯、1は点灯
  LED_Driver_Brightness( LDaddrs1, 0 ); // brightness= 0~15
  LED_Driver_DisplayInt( LDaddrs1 ); //Display Black OUT

  delay(300);
  
  SPIFFS.begin();

  html_str1 = "<body style='background:#000; color:#fff;'>\r\n";
  html_str1 += ews.EWS_TextBox_Send("txt1", "Hello! World! ★●▲■","送信");
  html_str1 += "<br>\r\n";
  html_str1 += ews.EWS_TextBox_Send("txt2", "日本語漢字 全角美咲フォント 半角Fontフォント","送信");
  html_str1 += "<br>\r\n";
  html_str1 += ews.EWS_TextBox_Send("txt3", "半角カタカナもOK! ウェブソケット通信ヤッタ~","送信");
  html_str1 += "<br>\r\n";
  
  html_str2 = "Scroll Speed\r\n";
  html_str2 += ews.EWS_Canvas_Slider_T("Speed",200,40,"#777777","#ff00ff");
  html_str2 += "<br><br>\r\n";
  html_str2 += ews.EWS_On_Momentary_Button("_SclStop", "Scrole Stop", 100,25,15,"#000000","#777777");
  html_str2 += "<br><br>\r\n";
  
  html_str3 = "Brightness\r\n";
  html_str3 += ews.EWS_Canvas_Slider_T("bright",200,40,"#777777","#ffff00");
  html_str3 += "<br><br>\r\n";
  
  html_str4 = ews.EWS_Status_Text(20,"RED");
  html_str4 += "<br>\r\n";
  html_str4 += "<br><br>\r\n";  
  html_str4 += ews.EWS_Close_Button("WS CLOSE",150,40,17);
  html_str4 += ews.EWS_Window_ReLoad_Button("ReLoad",150,40,17);
  html_str4 += "</body></html>\r\n";

  html_str5 = "";
  html_str6 = "";
  html_str7 = "";
  
  ews.AP_Connect(ssid, password);
  
  SclTime = millis();

}
//**************メインループ*************************************************
void loop() {
  
  ews.EWS_HandShake(html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7);
  
  uint8_t sjis[2];
  uint8_t utf8_1, utf8_2, utf8_3;

  uint32_t sp_addres=0x9DCC; //スペース(対応文字がない場合にスペースとして初期化しておく)

  uint16_t fnt_adrs_half;
  uint16_t fnt_adrs_Zen;
  uint8_t tmp_buf2[8];
  uint8_t 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'){
          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 '_':
              Scl_Stop = true;
              break;
            case 'b':
              br = (ret_str[0]-0x30)*100 + (ret_str[1]-0x30)*10 + (ret_str[2]-0x30);
              //LEDドライバHT16K33の明るさは0~16
              LED_Driver_Brightness(LDaddrs1, floor(br/12));
              break;
          }
        }else if(ret_str[0] == 't'){
          scl_txt = ret_str.substring(ret_str.indexOf('|')+1, ret_str.length()-1);
          scl_txt += String(" ") + String("\0");
          
          u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, scl_txt, sj_txt, &sj_length);
          
          Serial.print("Shift_JIS CODE = ");
          for(int i=0; i<sj_length; i++){
            Serial.printf("%X,",sj_txt[i]);
          }
          Serial.println();
          for(int i=0; i<sj_length; i++){
            Serial.write(sj_txt[i]);
          }
          Serial.println();
          
          fnt_cnt = 0;
          scl_cnt = 0;
          sj_cnt = 0;
          FntReadOK = true;
          sjis_txt_in = true;
        }
        ret_str = "\0";
      }else{
        ret_str = "\0";
      }
    }
    
    if(sjis_txt_in == true){
      if(millis() - SclTime > SclSpeed){
        if(sj_cnt == sj_length){
          sj_cnt = 0;
        }
        if(FntReadOK==true){
          if((sj_txt[sj_cnt]>=0x20 && sj_txt[sj_cnt]<=0x7F) || (sj_txt[sj_cnt]>=0xA1 && sj_txt[sj_cnt]<=0xDF)){ //半角カタカナ"。~゚"
  
            fnt_adrs_half = 0x110 + (sj_txt[sj_cnt] - 0x20)*8;
            SPIFFS_Flash_FontRead(Half_Font_file, fnt_adrs_half, tmp_buf_cnv);
            sj_cnt++;
            
            if((sj_txt[sj_cnt]>=0x20 && sj_txt[sj_cnt]<=0x7F) || (sj_txt[sj_cnt]>=0xA1 && sj_txt[sj_cnt]<=0xDF)){
              fnt_adrs_half = 0x110 + (sj_txt[sj_cnt] - 0x20)*8;
              uint8_t dummy_buf[8];
              uint8_t i;
              SPIFFS_Flash_FontRead(Half_Font_file, fnt_adrs_half, dummy_buf);
              switch(Direction){
                case 0:
                  for(i=0; i<8; i++){
                    dummy_buf[i] = dummy_buf[i]>>4;
                    tmp_buf_cnv[i] = tmp_buf_cnv[i] | dummy_buf[i];
                  }
                  break;
                case 1:
                  for(i=0; i<8; i++){
                    tmp_buf_cnv[i] = tmp_buf_cnv[i]>>4;
                    tmp_buf_cnv[i] = dummy_buf[i] | tmp_buf_cnv[i];
                  }
                  break;
              }
              sj_cnt++;
            }
            FntReadOK = false;        
          }else{
            Sjis_To_Misaki_Font_Adrs(sj_txt[sj_cnt], sj_txt[sj_cnt+1], &fnt_adrs_Zen);
            SPIFFS_Flash_FontRead(Zen_Font_file, fnt_adrs_Zen, tmp_buf);
            Dot_Rotation(90, Direction, tmp_buf, tmp_buf_cnv);
            FntReadOK = false;
            sj_cnt = sj_cnt + 2;
          }
          Dot_Rotation(0, Direction, tmp_buf_cnv, tmp_buf_cnv2);
        }

        if(Scl_Stop == false){
          LED_AdaMini88_cnv(LedDotOut, LedDot_mini_cnv);
          LED_Driver_DisplayOutput(LDaddrs1, LedDot_mini_cnv, LedDot0);
          Scroller_Dot_Replace( Direction, Next_buf, LedDotOut, tmp_buf_cnv2);
          scl_cnt++;
        }
        
        if(scl_cnt == 8){
          scl_cnt = 0;
          FntReadOK = true;
        }
        SclTime = millis();
      }
    }
  }else if(ret_str == "_close"){
    ret_str = "\0";
    scl_txt = "";
  }
}
//**********************LEDドライバ HT16K33 セットアップ*******************
void LED_Driver_Setup(uint8_t LD_addrs, uint8_t on_off)
{
  //HT16K33は8X8マトリックスLEDを2台まで制御できる。
  //ドライバIC#1 アドレスは0x70から設定する。
  Wire.beginTransmission(LD_addrs);
  Wire.write(0x20 | on_off);  //システムオシレータをONにする
  Wire.endTransmission();
}
//**********************LEDドライバ HT16K33 点滅周期設定*******************
void LED_Driver_Blink(uint8_t LD_addrs, uint8_t on_off, uint8_t blink_Hz)
{
  //blink_Hz=0 点滅off, 1は2Hz, 2は1Hz, 3は0.5Hz, on_off=0は消灯、1は点灯 
  Wire.beginTransmission(LD_addrs);
  Wire.write(0x80 | (blink_Hz<<1) | on_off); 
  Wire.endTransmission();
}
//**********************LEDドライバ HT16K33 明るさ設定*********************
void LED_Driver_Brightness(uint8_t LD_addrs, uint8_t brightness)
{
  // brightness= 0~15
  Wire.beginTransmission(LD_addrs);
  Wire.write(0xE0 | brightness);
  Wire.endTransmission();
}
//**********************LEDドライバ HT16K33 画面初期化*********************
void LED_Driver_DisplayInt(uint8_t LD_addrs)
{
  Wire.beginTransmission(LD_addrs);
  Wire.write(0x00);
  for(int i=0;i<8;i++){  
    Wire.write(B00000000);
    Wire.write(B00000000);
  }
  Wire.endTransmission();
}
//**********************Adafruit Mini 8x8ドット bit変換******************************
void LED_AdaMini88_cnv(uint8_t* Bdot1, uint8_t* Bdot2)
{
  for(uint8_t i=0; i<8; i++){
    for(uint8_t j=0; j<7; j++){
     bitWrite(Bdot2[i],6-j,bitRead(Bdot1[i],j));
    }
    bitWrite( Bdot2[i],7,bitRead(Bdot1[i],7));  
  }
}

//**********************LEDドライバ HT16K33 8X8データ送信*******************
void LED_Driver_DisplayOutput(uint8_t LD_addrs, uint8_t* DotB1, uint8_t* DotB2)
{
  int i,j;
    Wire.beginTransmission(LD_addrs);
    Wire.write(B00000000); //これは必要
    for(i = 0; i<8; i++){
      Wire.write(DotB1[i]);
      Wire.write(DotB2[i]);
    }
    Wire.endTransmission();
}
//*******************Shift_JISコードから美咲フォントアドレス計算********************************************
void Sjis_To_Misaki_Font_Adrs(uint8_t jisH, uint8_t jisL, uint16_t* fnt_adrs) 
{    // S-JISコードからMisakiフォントファイル上のバイト位置をポインタで返す。
  uint16_t SjisCode;
  int16_t adj; 

  if( jisH != '\0'){  //'\0'ならば読み込まない。
    if((jisH >= 0x81 && jisH <=0x9f) || (jisH >= 0xe0 && jisH <=0xef)){  //全角の場合
      SjisCode = ((uint16_t)jisH << 8 )+jisL;
      if(SjisCode>=0x8140 && SjisCode <=0x88fc){
        if     (SjisCode>=0x8140 && SjisCode<=0x817e) adj =  16;  // 一般記号
        else if(SjisCode>=0x8180 && SjisCode<=0x81fc) adj =  15;  // 一般記号
        else if(SjisCode>=0x824f && SjisCode<=0x8279) adj = -52;  // 数字、英語大文字
        else if(SjisCode>=0x8281 && SjisCode<=0x82f1) adj = -53;  // 英小文字、ひらがな
        else if(SjisCode>=0x8340 && SjisCode<=0x837e) adj = -120;  // カタカナ
        else if(SjisCode>=0x8380 && SjisCode<=0x83d6) adj = -121;  // カタカナ
        else if(SjisCode>=0x8440 && SjisCode<=0x847e) adj = -188;  // 外国大文字
        else if(SjisCode>=0x8480 && SjisCode<=0x84be) adj = -189;  // 外国小文字、罫線、※丸囲み文字やその他特殊文字は美咲フォントには無い。
        else if(SjisCode>=0x8740 && SjisCode<=0x877e) adj = -768; // 13区
        else if(SjisCode>=0x8780 && SjisCode<=0x879c) adj = -769; // 13区
        else if(SjisCode>=0x889f && SjisCode<=0x88fc) adj = -837;  // 第一水準漢字 亜~蔭まで
        *fnt_adrs = ((SjisCode-0x8140)+adj)*8;
      }else if(SjisCode>=0x8940 && SjisCode<=0x9ffc){ //院~滌
        if(jisL <= 0x7e){
          *fnt_adrs = ((SjisCode-0x8140)-(836+(jisH-0x88))-67*(jisH-0x88))*8;
        }else if(jisL >= 0x80 ){
          *fnt_adrs = ((SjisCode-0x8140)-(837+(jisH-0x88))-67*(jisH-0x88))*8;
        }
      }else if(SjisCode>=0xe040 && SjisCode <=0xeaa4){ //漾~熙
        if(jisL <= 0x7e ){
          *fnt_adrs = ((SjisCode-0x8140)-(836+(jisH-0x88))-67*(jisH-0x88)-12032)*8;
        }else if(jisL >= 0x80 ){
          *fnt_adrs = ((SjisCode-0x8140)-(837+(jisH-0x88))-67*(jisH-0x88)-12032)*8;
        }
      }else{
        *fnt_adrs = (0x8140 + 16)*8;  // 対応文字コードがなければ 全角スペースを返す
      }
    }else {
      *fnt_adrs = (0x8140 + 16)*8;  // 対応文字コードがなければ 全角スペースを返す
    }
  }else {
    *fnt_adrs = (0x8140 + 16)*8;  // 対応文字コードがなければ 全角スペースを返す
  }
}
//*****************フォントファイル読み込み**************************************
void SPIFFS_Flash_FontRead(const char* font_file, uint16_t addrs, uint8_t *buf)
{
//  Dir dir = SPIFFS.openDir("/");//これは読み取り速度が遅くなるかもしれないのでコメントアウトしている
  File f1 = SPIFFS.open(font_file, "r"); //美咲フォントファイル
  if(f1){
    f1.seek(addrs,SeekSet);
    for (byte i=0; i<8; i++){
      buf[i] = f1.read(); //readBytes はSPIFFSファイルシステムver0.2.0ではchar型となるのでここでは使わない
    }
    f1.close();
  }else{
    Serial.print(font_file);
    Serial.println(F(" file has not been uploaded to the flash in SPIFFS file system"));
    delay(30000);
  }
}
//**************************8X8配列並び替え****************************************
void Dot_Rotation(int16_t angle, uint8_t Direction, uint8_t* buff_1, uint8_t* cnv_buff_1)
{
  uint8_t col,row;
  switch(angle){
    case 0:
      for( row = 0; row < 8; row++){
        cnv_buff_1[row] = buff_1[row];
      }
      break;
    case 90:
      for( col = 0; col < 8; col++ ) {
        for( row = 0; row < 8; row++ ) {
          bitWrite( cnv_buff_1[7-row], 7-col , bitRead( buff_1[col], 7-row ) );
        }
      }
      break;
  }
}
//*************************文字スクロール、ドット変換**************************************
void Scroller_Dot_Replace(uint8_t drection, uint8_t* next_buff, uint8_t* scl_buff_1, uint8_t* Orign_buff)
{
  uint8_t i;
  switch( drection ){
    case 0:        //(←)右から左へスクロール----------------------------
      for(i=0 ; i<8 ; i++){
        bitWrite( next_buff[i],7, bitRead( scl_buff_1[i],7)); //7番目のビットの取り置き
        scl_buff_1[i] = scl_buff_1[i]<<1;
        bitWrite( scl_buff_1[i],0, bitRead( Orign_buff[i],7));
        Orign_buff[i] = Orign_buff[i]<<1;
      }
      break;
  }
}

では、このコードをザッと紹介します。

つい最近、ツィッターを見ていたら、C言語やC++でchar形やlong型は極力使わない方が良いという投稿を見ました。
正にその通りで、unsigned や signed を記述すると見た目にややこしいですし、コンパイラによってint型のバイト数が異なるなどの問題がありましたからね。
ということで、このプログラムでも int8_t や uint8_t という型にしました。
こちらの方がバイト数が分かりやすいですね。

●7-12行:
自作ライブラリのEasyWebSocket BETA 1.3、UTF8toSJIS のインクルード
Arduino core for ESP8266 WiFi chipライブラリ、SPIFFSプラグイン関係のインクルード

●16-17行:
ご自分のルーターで割り当てたESP-WROOM-02 のローカルIPアドレスに書き換えてください。

●19-21行:
SPIFFSファイルシステムでフラッシュにアップロードしたファイルの定義

●71-76行:
LEDドライバHT16K33 の I2C通信初期化

●82-109行:
ブラウザへ送信するbody要素内のHTMLタグです。
EasyWebSocketライブラリを使用。
83行目のテキストボックスは120バイト以上入力すると警告ウィンドウが表示されます。
120バイトは全角では40文字となります。
つまり、全角1文字のUTF-8コードは3バイトですからそうなります。
ヘッダ部分はspiffs_01.txtファイルに記述されていて、先にフラッシュへアップロードしています。

●111行:
ルーターとのコネクション確立

●119行:
WebSocketハンドシェイク(コネクション確立)関数。
BETA1.3バージョンからメインループ内に置き、コネクション切断されても再接続しやすいようにしています。

●131行:
ブラウザからのテキストデータ受信

●136-152行:
ブラウザからLEDの明るさやスクロールスピードといったデータが送られてきた場合、例えば、明るさ80%ならば
160|bright
という文字列が送られてきます。
ブラウザの明るさスライダーはHTML5 の Canvas要素で幅が200pxですので、80%の場合は160になります。
スクロールスピードも同様です。
よって、この文字列の4番目の文字 ret_str[4] が ‘b’=明るさ なのか、’S’=スクロールスピード なのかを判別しています。
テキストボックスの文字列が送られてきた場合は
txt1|Hello! World! ★●▲■
という感じで送られてくるので、1番目の文字 ret_str[0] が’t’ ならばテキストボックス文字列と判別します。

●156行:
自作ライブラリです。UTF8コード文字列から Shift_JISコードへ一括変換しています。

●186-212行:
半角フォントも8×8ドットでできているので、半角文字が連続する場合は4バイト分詰めるという作業をしています。
そして、ここで半角の美咲フォントファイル 4X8.FNT を読み込んでいます。

●215-221行:
Shift_JISコードから美咲フォントデータを呼び出しています。
全角フォントは90度回転、半角フォントはそのままというビット操作関数を呼んでいます。

●225-226行:
Adafruit 8×8 mini LED マトリックス基板用にビット変換してI2C通信出力しています。

●227行:
文字列をスクロールする関数を読んでいます。
この関数がメインループ内で8回呼び出されると、1文字分8×8ドット内をスクロールし切り、次の文字を代入していきます。

●303-345行:
今回の目玉である、Shift_JISコードから美咲フォントファイル内の8×8ドットデータを抽出する関数です。
ここの部分は過去の電光掲示板作成の時でも述べましたが、放課後マイコンクラブ さんの記事を参考にしました。この方の記事は素晴らしく、とても役立ちました。感謝いたします。

以前は外付け EEPROM を使えばArduino UNO でもフリーのフォントが使えることに感動しましたが、今回は外付けが一切なしです。
放課後マイコンクラブさんでは恵梨沙フォントを使われていましたが、当方では美咲フォントに変更しています。

先に紹介した、Shift_JISコードからフォントアドレスを計算することをここでやっています。
見ていただければ解ると思うのですが、独自に考えたアルゴリズムがあり、ちょっと難解かもしれません。

12.コンパイル実行させる

では、コンパイルしてみてください。
コンパイルが終わったら、ブラウザのURL入力欄にESP-WROOM-02 のローカルIPアドレスを入力してください。

その様子は始めの方で紹介した動画を参照してください。

どうでしょうか?
Arduino UNO と Ethernet Shield を使ったりした場合は、SDカードやEEPROMが別途必要でしたが、部品点数が最少でここまでできるようになってきました。
ESP-WROOM-02 ( ESP8266 )の巨大なメモリのおかげですね。
こういうデバイスを日本のメーカーからは出さないのでしょうか・・・?。

というわけで、もの凄く長~~い記事になってしまいました。
今回はここまでです。

次回はLEDマトリックスを4つ連結して、ちゃんとした電光掲示板にしてみようと思います。
では、また・・・。

スポンサーリンク

mgo-tec電子工作 関連コンテンツ ( 広告含む )
Amazon.co.jp 当ブログのおすすめ



投稿者:

mgo-tec

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

「ESP-WROOM-02 で WebSocket リアルタイム制御 日本語電光掲示板作成(単体LEDマトリックス版)」への4件のフィードバック

  1. おはようございます。
    たびたび質問で申し訳ありません。
    いろいろ進めてきてここまで来たのですが、数件問題があります。
    助けていただけますと助かります。
    状況は下記の通りです。
    よろしくお願いします。
    1.本内容の導入は成功したのですが、Webでテキストの送信機能だけ機能しません。
     スピード、明るさ、スプロールストップ(075|Speed;137|bright;100|_SclStop;)などは正常に動作します。
     ret_strを見てみても、txtメッセージは受信していないようです。
     なぜでしょうか?
     原因追求のアドバイスなど教えていただければたすかります。
     現在は、ret_str=”txt2|テストメッセージ\r\n”; などを追加することで表示できています。
     いろいろメッセージを変更して試しても成功しています。
     IDEは1.6.5です。
    2.翌日、上記のメッセージを変更しようとして、マイコンボードに書き込むを選択すると、
     warning: espcomm_sync failed
     error: espcomm_open failed
     error: espcomm_upload_mem failed
     と表示されてしまいます。
     FlashSizeを、4M(3M SPIFFS)や4M(1M SPIFFS)に変更してもだめ、速度を115200、その他全速度に変更してもだめです。
     シリアルモニタは正常にモニタできます。ESP-WROOM-02のリセットでWiFiに接続するメッセージも表示されます。
     何か解決方法があれば教えていただければ助かります。
    頼り切ってしまってすみません。
    よろしくお願いします。

    1. cherryさん

      いろいろと多忙でお返事遅くなり、すみません。
      次のことを確認してみてください。

      1.ESPr Developer ボード上に印刷で Rev.2 または Rev.3 という文字はありますでしょうか?
      Rev.2以上の印が無い旧製品は、シリアルUSBで 4M/3M SPIFFSアップロードはできません。
      その場合、OTAでアップロードすることになりますが、美咲フォントの場合は 4M/1M SPIFFSで十分です。

      2.4M/1M SPIFFS でアップロードして、スケッチをコンパイルしているものに、スケッチだけ4M/3M SPIFFS に変更してコンパイルしたらエラーになります。
      必ずSPIFFSアップロードのフラッシュサイズとスケッチコンパイルするときのフラッシュサイズは同じにしてください。

      3.Arduino IDE は 1.6.11 がおすすめです。ESP8266ボードは2016/8/27時点で最新は2.3.0 です。
      最新版に再インストールしてみてください。
      再インストールするときに注意していただきたいのは、1.6.5をアンインストールまたは削除した後、必ずArduino15フォルダを削除してください。
      この方法は以下のページを参照してください。
      (Windows用)
      https://www.mgo-tec.com/esp8266-board-install01-html

      その他は、spiffs_01.txt ファイル内の ws://192.168.0.17 のところをご自分のローカルIPアドレスに書き換えることを忘れないでください。
      あと、もう一つ。 USBハブは使わないでください。電力が足りなくなる場合がありますので、パソコンのUSBポートに直挿ししてください。

      以上でまずは試してみてください。
      それでもまだエラーが出たらまたご連絡ください。

  2. こんにちは
    返信ありがとうございます。IDEを1.6.11に上げました。
    SPIFFSと書き込みは復活しました。
    ありがとうございました。
    ただ、WebPacketでtextだけ受け取れないのは変わりません。
    Android4.2.2、Chrome52.0.2743.98で試験しているのですが….

    1. なるほど・・・、テキストがスマホブラウザから送信されてませんか・・・。

      まず、お伺いしたいのは、ライブラリはすべて最新バージョンでしょうか?
      現在の最新版は
      EasyWebSocket.h — Beta ver 1.39
      UTF8toSJIS.h — Beta ver 1.3
      です。

      そして、IDEのファイルメニューの「スケッチの例」で EasyWebSocket139 というサンプルスケッチを開いていただき、SSIDとパスワードを書き換えてください。
      そして、114行目を以下のように書き換えてください。
      if(millis()-CountTime > 10){ → if(millis()-CountTime > 500){
      それをSPIFFSアップロードしたときと同じFLASHサイズにしたまま、コンパイル書き込みしてください。
      spiffs_01.txt は事前にws:x.x.x.x のところのIPアドレスをテキストエディタで編集して、UTF-8形式で保存してあると思いますので、そのままでよいです。
      (※UTF-8形式で保存することがポイントです。)
      次に、ルーターを立ち上げてしばらくたってからスマホブラウザからアクセスしていただき、WebSocket接続して、テキストボックスに何か文字を入力して送信してみてください。
      スマホブラウザの from WROOM DATA のところのテキスト表示が変われば、正常にWebSocketでテキスト送受信ができています。

      以上をまず試してみてください。

コメントを残す

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

*画像の文字を入力してください。(スパム防止の為)