製作目的
最近、文字に興味を持ち始めてきた息子。
本を読んでる中でも、ふと介助者の指をひっぱって「これ読んで」と言わんばかりの動作をする。
しかし、知的障害を有し好き嫌いの激しい息子に対して、普通の教科書などでの言葉の学習はなかなか難しい。
興味のない本に対しては、わずか一分も持たずポイっとされてしまう。
また、iPadなどのアプリを使って文字学習を行う事もあるが、
最近では「iPad = YouTube動画を見るモノ」という先入観が打ち勝って、
なかなかiPadを渡しても、文字勉強に活用するのが難しい…
そこで、息子が興味を持つ様な表示方法を用いて、「自然と文字に目がいく様な & 伝えたい言葉を自然に覚える仕掛け」を考えてみた。
製作物
最近文字を認知しだした息子の学習好奇心を引き出すため
— おぎ-モトキ@父親エンジニア (@ogimotoki) 2020年12月6日
「スマホで操作可能なポータブル電光掲示板」
を作った
気になる言葉を介助者がスマホ入力して、大きく表示!
『光る&動く』で子供のワクワク心をつかみつつ勉強できる!
実際の小学校の国語授業で使えるといいな#家族のためのモノづくり pic.twitter.com/wrWhpHRvgr
スマートフォンやPCでウェブブラウザを開いて文字を入力、その文字を電光掲示板上に大きく表示するというシンプルな機能です。日本語も表示できるので、小学生の漢字のお勉強等に活用できます。
ハード構成
電光掲示板(LEDマトリックス)には、以下の部品を使っています。
amzn.to
amzn.to
4mmピッチ(P4)もしくは3mmピッチ(P3)の横64Pixel×縦32Pixelの電光掲示板を使っています。こちらの制御規格は"HUB75"と呼ばれるものだそうです。
ryosukeeeee.hatenablog.com
qiita.com
本規格について詳細は上サイトを参考にさせて頂きました。
この規格I/Fの良いところは、デイジーチェーンの様に複数のパネルをつなげる事でパネルを大きくする事ができる点です。
こんな風に、2枚を接続するのも簡単です。
なお、電光掲示板はピッチサイズも様々なモノが販売されている様なので、必要なサイズに応じて選択すればよさそうです。
制御マイコンは、単体でネットワークに接続可能なESP32 DevKit Cを使いました。
amzn.to
esp32 dev kitCとLEDマトリクスのHUB75端子と直結して使用します。(IDCケーブル フラットリボンケーブル)
Amazon | uxcell メスコネクタ エクステンションワイヤー アダプタ IDCケーブル フラットリボンケーブル 8ピン 2.54mmのピッチ 30cmの長さ 5個入り | DIY・工具・ガーデン
接続ピン構成は以下としました。
R1 : 25 G1 : 26
B1 : 27
R2 : 21 G2 : 22
B2 : 23
A : 12 B : 16
C : 17 D : 18
CLK : 15 LAT : 32
OE : 33
ソフト構成① LEDマトリクス接続
LEDマトリクスの描画方法についていくつかのライブラリを試してみましたが、以下のライブラリを使うと、後述する日本語フォントと相性が良さそうでした。
github.com
描画ライブラリの使い方は、M5Stack等の液晶表示に使われるAdafruit_GFX.hと同じ感覚で使えます。
例えば、以下の様な描画サンプルになります。
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h> MatrixPanel_I2S_DMA matrix; matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); matrix.drawRect(0, 0, matrix.width(), matrix.height(), matrix.color444(15, 15, 15)); matrix.drawRect(1, 1, matrix.width()-2, matrix.height()-2, matrix.color444(15, 0, 0)); matrix.drawRect(2, 2, matrix.width()-4, matrix.height()-4, matrix.color444(0, 0, 15)); matrix.setTextSize(1); //1サイズは8×8サイズ matrix.setCursor(23, 7); matrix.setTextColor(matrix.color444(15,15,15)); matrix.println("HELLO"); matrix.setCursor(12, 16); matrix.println("WORLD!");
なお、2枚以上を接続する場合、以下のサンプルコードや接続方法を参考にすると良さそうです。
ソフト構成② 日本語表示
上記のライブラリを使った場合、8x8の英数字表記のみになります。子供の日本語勉強などのためには日本語表示は必須となります。
そこで、日本語フォントを導入して簡単に表示可能な環境を作りたいと思います。
・東雲フォント 16Pixel x 16Pixel
・美咲フォント 8Pixel x 8Pixel
の2種類のフォントを導入したいと思います。
これらは予めフォントデータをストレージ等に格納しておく必要がありますが、M5Stackと異なりSDカードスロットがないため、今回はESP32内蔵のフラッシュメモリ(SPIFFS)に格納して使いたいと思います。
SPIFFSの使い方、格納方法は以下のサイトを参考にしました。
www.mgo-tec.com
これは非常に便利で3KB以下のデータであれば、SDカード不要でデータを格納して使う事が出来そうです!
■日本語 東雲フォント(16x16)の導入方法
www.riraotech.com
■日本語 美咲フォント(8x8)の導入方法
www.mgo-tec.com
製作ソフトコードの一部は以下になります。
#include <Adafruit_GFX.h> // Core graphics library #include <ESP32_SPIFFS_ShinonomeFNT.h> #include <ESP32_SPIFFS_UTF8toSJIS.h> #include <ESP32_SPIFFS_MisakiFNT.h> #include "FS.h" #include "SPIFFS.h" const char* UTF8SJIS_file = "/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく const char* Shino_Zen_Font_file = "/shnmk16.bdf"; //全角フォントファイル名を定義 const char* Shino_Half_Font_file = "/shnm8x16.bdf"; //半角フォントファイル名を定義 const char* Misaki_Zen_Font_file = "/MSKG_13.FNT"; //全角フォントファイル名を定義 const char* Misaki_Half_Font_file = "/mgotec48.FNT"; //半角フォントファイル名を定義 #define MAX_FONT_NUM 20 //表示文字(16x16)の最大文字数 ESP32_SPIFFS_ShinonomeFNT SFR; ESP32_SPIFFS_MisakiFNT MFR; #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h> MatrixPanel_I2S_DMA matrix; void setup() { matrix.begin(R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN ); // setup the LED matrix //フォントデータバッファ if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){ Serial.println("SPIFFS Mount Failed"); return; } SFR.SPIFFS_Shinonome_Init3F(UTF8SJIS_file, Shino_Half_Font_file, Shino_Zen_Font_file); MFR.SPIFFS_Misaki_Init3F(UTF8SJIS_file, Misaki_Half_Font_file, Misaki_Zen_Font_file); // LED表示タスク設定 TaskHandle_t th; //マルチタスクハンドル定義 xTaskCreatePinnedToCore(DisplayLED, "DisplayLED", 4096, NULL, 10, &th, 0); //マルチタスク起動 } void loop() { mode_switch = digitalRead(MODE_INPUT_PIN); if(mode_switch == 0 && mode_switch_old ==1){ if(font_16x16==true) font_16x16= false; else font_16x16= true; display_is_changed = true; Serial.println(font_16x16); } delay(100); mode_switch_old = mode_switch; } void DisplayLED(void *pvParameters) { while(1){ //文字表示 displayStrCenterToLeftShift(displayStr, matrix.color444(15, 15, 15), 100, true); display_is_changed = false; delay(100); } } void displayStrCenterToLeftShift(String str, uint16_t color, int shiftTime_ms, bool skip){ uint16_t sj_length = 0;//半角文字数 uint8_t font_buf[MAX_FONT_NUM*2][16] = {0}; uint8_t misaki_font_buf[MAX_FONT_NUM][8]; String strmes = str + " "; bool stopflg = false; //20文字以上の場合、20文字までに制限する //if(strmes.length() > MAX_FONT_NUM*3) strmes = strmes.substring(0, MAX_FONT_NUM-1); matrix.fillRect(0, 8, matrix.width(), 16, matrix.color444(0, 0, 0)); //displayStrCenter(strmes, color); // 東雲フォント(16x16) if(font_16x16 == true){ sj_length = SFR.StrDirect_ShinoFNT_readALL(strmes, font_buf); for(int i=0; i<4*sj_length; i++){ for(int j=0; j<sj_length; j++){ drawFont16x16(font_buf[j],font_buf[j+1], 8*j-2*i, 8, color); } if(stopflg == false){ delay(1000); stopflg = true; } //matrix.fillRect(0, 8, matrix.width(), 16, matrix.color444(0, 0, 0)); if(display_is_changed == true && skip == true) return; delay(shiftTime_ms); } //美咲フォント }else{ sj_length = MFR.StrDirect_MisakiFNT_readALL(strmes, misaki_font_buf); //String 文字列から一気にフォント変換 for(int i=0; i<2*sj_length; i++){ for(int j=0; j<(sj_length/2+1); j++){ drawFont8x8(misaki_font_buf[j], 4+8*j-2*i, 12, color); } if(stopflg == false){ delay(1000); stopflg = true; } //matrix.fillRect(0, 8, matrix.width(), 16, matrix.color444(0, 0, 0)); if(display_is_changed == true && skip == true) return; delay(shiftTime_ms); } } }