SPIFFS用 Arduino IDE スケッチ入力
次に、SPIFFS 用スケッチを紹介します。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include "ESP32_LCD_ILI9341_SPI.h" #include "ESP32_Button_Switch.h" #include "ESP32_SPIFFS_ShinonomeFNT.h" #include "ESP32_SPIFFS_UTF8toSJIS.h" #include "ESP32_WebGet.h" #include "TimeLib.h" //Use Arduino time library ver1.5- const int8_t sck = 18; // SPI clock pin const int8_t miso = -1; // MISO(master input slave output) don't using const int8_t mosi = 23; // MOSI(master output slave input) pin const int8_t cs = 14; // Chip Select pin const int8_t dc = 27; // Data/Command pin const int8_t rst = 33; // Reset pin const int8_t LCD_LEDpin = 32; const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください 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"; //半角フォントファイル名を定義 ESP32_LCD_ILI9341_SPI LCD(sck, miso, mosi, cs, dc, rst, LCD_LEDpin); ESP32_SPIFFS_ShinonomeFNT SFR; ESP32_Button_Switch BTN; ESP32_WebGet _EWG; //------文字表示系 引数初期化------------ const uint8_t MAX_TXT_NUM = 4; //ニュース表示最大数 uint8_t max_txt = 40; //1x1倍 最大文字表示数 uint8_t news_sj_txt[ MAX_TXT_NUM ][ 400 ] = {}; //Shift_JIS文字コード格納 uint16_t news_sj_length[ MAX_TXT_NUM ]; uint8_t news_font_buf[ MAX_TXT_NUM ][2][16] = {}; //16x16フォント全角1文字格納バッファ uint8_t Scl_Buf[ MAX_TXT_NUM ][ 16 ][ 640 ] = {}; //文字列スクロールpixelバッファ //red (0-31), green (0-63), blue (0-31) uint8_t red = 31, green = 63, blue = 31; uint8_t V_size = 3, H_size = 3; //V_size(垂直方向文字サイズ)、H_size(水平方向サイズ) uint16_t X0 = 0, Y0 = 0; int32_t scl_speed = 0; //スクロール速度(0が最速) bool font_read_ok[ MAX_TXT_NUM ] = { false, false, false, false }; bool scl_pause = true; //-----Yahoo記事取得 引数初期化------------ const char *yahoo_host = "news.yahoo.co.jp"; uint32_t NewsGetLastTime = 0; bool News_first_get = true; bool NewsGet = false; int8_t Scl_Cnt[MAX_TXT_NUM] = {}; //文字スクロールカウント uint16_t Fnt_Cnt[MAX_TXT_NUM] = {}; //フォント半角1文字スクロールカウント uint8_t Zen_or_Han[MAX_TXT_NUM] = {}; //フォント読み取り時に関数から返ってきた全角または半角かの数値を格納 //-----ボタンスイッチ 引数初期化----------- const uint8_t buttonA_GPIO = 39; const uint8_t buttonB_GPIO = 38; const uint8_t buttonC_GPIO = 37; uint8_t btn_stateA = _Release; uint8_t btn_stateB = _Release; uint8_t btn_stateC = _Release; boolean V_size_down = false; //***********セットアップ**************************** void setup() { Serial.begin(115200); delay(1000); pinMode(buttonA_GPIO, INPUT); //GPIO #39 は内部プルアップ無し pinMode(buttonB_GPIO, INPUT); //GPIO #38 は内部プルアップ無し pinMode(buttonC_GPIO, INPUT); //GPIO #37 は内部プルアップ無し SFR.SPIFFS_Shinonome_Init3F(UTF8SJIS_file, Shino_Half_Font_file, Shino_Zen_Font_file); //ライブラリ初期化。3ファイル同時に開く LCD.ILI9341_Init(true, 40000000); LCD.Display_Clear(); LCD.Brightness(255); //LCD LED Full brightness //++++++++++++++++++++++++++++++++++++++++++ String test_str[ MAX_TXT_NUM ] ; test_str[0] = "M5stack"; test_str[1] = "やふ~にゅ~す"; test_str[2] = "電光掲示板"; test_str[3] = "by SPIFFSフラッシュ"; uint8_t test_buf[ MAX_TXT_NUM ][ 20 ][ 16 ] = {}; uint16_t test_sj_len[ MAX_TXT_NUM ] = {}; uint8_t num = 0; //文字列番号 for( num = 0; num < MAX_TXT_NUM; num++ ){ test_sj_len[ num ] = SFR.StrDirect_ShinoFNT_readALL(test_str[ num ], test_buf[ num ]); Serial.printf("test_sj_len = %d\r\n", test_sj_len[ num ]); } num = 0, X0 = 48; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 1, X0 = 48; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 2, X0 = 80; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); num = 3, X0 = 8; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 3, test_sj_len[ num ], X0, num * 48, red, green, blue, test_buf[ num ]); X0 = 0; for( int i = 0; i < 256; i++){ LCD.Brightness(i); delay(10); } //--------メッセージウィンドウ表示--------- red = 31, green = 63, blue = 31; LCD.Display_Clear(0, 4 * 48, 319, 239); LCD.Draw_Rectangle_Line(0, 4 * 48, 319, 239, red, green, blue); red = 31, green = 0, blue = 0; Message(" WiFi Connecting..."); //--------Wi-Fiアクセスポイント接続--------- _EWG.EWG_AP_Connect(ssid, password); //Wi-Fi ルーターと接続 delay(1000); _EWG.EWG_NTP_TimeLib_init(9, "time.windows.com"); //NTPサーバー取得初期化 _EWG.NTP_OtherServerSelect(9); //NTPサーバーと接続できなかった場合、他のNTPサーバーと接続できるか試す関数 red = 31, green = 63, blue = 31; LCD.Display_Clear(0, 4 * 48, 319, 239); LCD.Draw_Rectangle_Line(0, 4 * 48, 319, 239, red, green, blue); //ニュース記事文字列のセットアップ num = 0, red = 31, green = 63, blue = 31; LCD.Scrolle_Font_SetUp(num, max_txt, red, green, blue); num = 1, red = 0, green = 63, blue = 0; LCD.Scrolle_Font_SetUp(num, max_txt, red, green, blue); num = 2, red = 31, green = 0, blue = 31; LCD.Scrolle_Font_SetUp(num, max_txt, red, green, blue); num = 3, red = 31, green = 63, blue = 0; LCD.Scrolle_Font_SetUp(num, max_txt, red, green, blue); Serial.printf("Free Heap Size = %d\r\n", esp_get_free_heap_size()); TaskHandle_t th; //マルチタスクハンドル定義 xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動 } //***********メインループ**************************** void loop() { Blink_Wait_Message(); //News記事取得中、待機メッセージ表示 uint8_t num = 0; //文字列番号 for(num = 0; num < MAX_TXT_NUM; num++){ X0 = 0, Y0 = num * 48; if( scl_pause == false ){ if( LCD.Scrolle_Inc_HVsizeUp_8x16_Font_DisplayOut(num, Zen_or_Han[num], scl_speed, H_size, V_size, &Scl_Cnt[num], news_sj_length[num], X0, Y0, news_font_buf[num], Scl_Buf[num]) ){ font_read_ok[ num ] = true; } } } if( V_size_down == true ) { LCD.Display_Clear( 0, 0, 319, 4 * 48 - 1); V_size_down = false; } } //************ マルチタスクループ **************************** void Task1(void *pvParameters) { while(1){ uint8_t num = 0; //文字列番号 YahooNewsGET(60000); //60秒毎に記事取得 for( num = 0; num < MAX_TXT_NUM; num++ ){ if( font_read_ok[num] == true ){ Zen_or_Han[num] = SFR.Sjis_inc_FntRead(news_sj_txt[num], news_sj_length[num], &Fnt_Cnt[num], news_font_buf[num]); font_read_ok[num] = false; } } button_action(); delay(1); //most important } } //******** ニュース記事取得中、待機メッセージ表示******* void Message(String str){ uint8_t f_buf[ 40 ][ 16 ] = {}; uint16_t len = SFR.StrDirect_ShinoFNT_readALL(str, f_buf); X0 = 1, Y0 = 4 * 48 + 1; LCD.Display_Clear(1, Y0 + 1, 318, 238); LCD.HVsizeUp_8x16_Font_DisplayOut(2, 2, len, X0, Y0 + 10, red, green, blue, f_buf); } //******** ニュース記事取得中、待機メッセージ表示******* void Blink_Wait_Message(){ if( scl_pause == true ){ uint8_t f_buf[ 40 ][ 16 ] = {}; uint16_t len = SFR.StrDirect_ShinoFNT_readALL(" News Getting Wait", f_buf); X0 = 1, Y0 = 4 * 48 + 1; red = 31, green = 0, blue = 0; while(1){ if( scl_pause == false ){ char web_get_time[6]; sprintf(web_get_time, "%02d:%02d", hour(), minute()); //ゼロを空白で埋める場合は%2dとする String str = " Get OK! " + String( web_get_time ); len = SFR.StrDirect_ShinoFNT_readALL(str, f_buf); red = 0, green = 63, blue = 0; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 2, len, X0, Y0 + 10, red, green, blue, f_buf); break; } red = 31, green = 0, blue = 0; LCD.HVsizeUp_8x16_Font_DisplayOut(2, 2, len, X0, Y0 + 10, red, green, blue, f_buf); delay(300); LCD.Display_Clear(1, Y0 + 1, 318, 238); delay(300); } } } //************** Yahoo RSS ニュースガジェット ************************ void YahooNewsGET(uint32_t get_interval){ if( (News_first_get == true) || ((millis() - NewsGetLastTime) > get_interval) ){ scl_pause = true; String news_str; uint8_t num = 0; //文字列番号 num = 0; news_str = WebGet(yahoo_host, "/rss/topics/top-picks.xml"); NewsStrFontConv( num, news_str ); num = 1; news_str = WebGet(yahoo_host, "/rss/topics/sports.xml"); NewsStrFontConv( num, news_str ); num = 2; news_str = WebGet(yahoo_host, "/rss/topics/it.xml"); NewsStrFontConv( num, news_str ); num = 3; news_str = WebGet(yahoo_host, "/rss/topics/entertainment.xml"); NewsStrFontConv( num, news_str ); News_first_get = false; NewsGet = true; scl_pause = false; NewsGetLastTime = millis(); } } //************************************** void NewsStrFontConv(uint8_t num, String news_str){ Fnt_Cnt[num] = 0; news_sj_length[num] = SFR.UTF8toSJIS_convert(news_str, news_sj_txt[num]); Serial.printf("news_sj_length[%d] = %d\r\n", num, news_sj_length[num]); Zen_or_Han[num] = SFR.Sjis_inc_FntRead(news_sj_txt[num], news_sj_length[num], &Fnt_Cnt[num], news_font_buf[num]); font_read_ok[num] = false; Scl_Cnt[num] = 0; } //************************************** String WebGet(const char *host, String target_url){ char web_get_time[6]; sprintf(web_get_time, "%02d:%02d", hour(), minute()); //ゼロを空白で埋める場合は%2dとする String news_str = "◆ " + String(web_get_time) + " "; news_str += _EWG.EWG_https_Web_Get(host, target_url, '\n', "</rss>", "<title>", "</title>", "◆ "); Serial.printf("News Get = %s\r\n", news_str.c_str()); return news_str; } //**************************************** void button_action(){ btn_stateA = BTN.Button(0, buttonA_GPIO, true, 10, 300); switch( btn_stateA ){ case _MomentPress: Serial.println("Button A Moment Press"); V_size--; if(V_size < 1) V_size = 1; Serial.printf("V_size =%d\r\n", V_size); V_size_down = true; break; case _ContPress: Serial.println("-------------Button A Cont Press"); V_size++; if(V_size > 3) V_size = 3; Serial.printf("V_size =%d\r\n", V_size); break; default: break; } btn_stateB = BTN.Button(1, buttonB_GPIO, true, 10, 300); switch( btn_stateB ){ case _MomentPress: Serial.println("Button B Moment Press"); H_size--; if(H_size < 1) H_size = 1; Serial.printf("H_size =%d\r\n", H_size); break; case _ContPress: Serial.println("-------------Button B Cont Press"); H_size++; if(H_size > 15) H_size = 15; Serial.printf("H_size =%d\r\n", H_size); break; default: break; } btn_stateC = BTN.Button(2, buttonC_GPIO, true, 10, 300); switch( btn_stateC ){ case _MomentPress: Serial.println("Button C Moment Press"); scl_speed++; if(scl_speed > 100) scl_speed = 100; Serial.printf("scl_speed =%d\r\n", scl_speed); break; case _ContPress: Serial.println("-------------Button C Cont Press"); scl_speed--; if(scl_speed < 0) scl_speed = 0; Serial.printf("scl_speed =%d\r\n", scl_speed); break; default: break; } }
【解説】
殆ど micro SD 用と同じですが、SPIFFS の場合は多少マルチタスクが有効になるので、LCD 画面表示はメインループで制御し、フォント読み込みは Task1 で動作させています。
これによって、読み取りの遅い SPIFFS でもある程度スムースにスクロールさせることができます。
また、133行目のメッセージ点滅関数では、173-196行にあるように別途 while ループで動かしているのに、その間、別タスクの Task1 でニュース記事をGET できて、フォント変換も行うことができています。
ただし、文字スクロール最中に記事の一括フォント変換をやろうとすると、どうしてもうまくいきませんでした。
ですから、先でも述べたように、SPIFFS でも記事を取得している間はスクロールを停止するようにしました。
コンパイル書き込み実行
では、ご自分の Wi-Fi ルーターを起動し、しばらく経ってから、Arduino IDE でそれぞれコンパイル書き込み実行してみてください。
シリアルモニタも 115200 bps で起動しておくと、細かいログが確認できます。
あとは、最初に紹介した動画のように表示されればOKです。
ボタン操作は前回の記事と同様です。
長押しは0.3秒以上です。
【A button】
長押し → 垂直方向サイズアップ
瞬時押し→垂直方向サイズダウン
【B button】
長押し → 水平方向サイズアップ
瞬時押し→水平方向サイズダウン
【C button】
長押し →スクロール速度アップ
瞬時押し→スクロール速度ダウン
編集後記
えらい疲れ果てました・・・。
一旦プログラムを組み始めると、あれこれ修正する点が出てきて、止め処が無くなります。
今回、M5Stack の LCD が VSPI 接続されていたのは残念だったのですが、それを割り切って使えばあまり気にせずに使えることが分かりました。
その分、GPIO が節約できるので、利点もあるということです。
でも、この情報量の多いディスプレイの画面一杯に4列の文字スクロールは、以前ならば無理だと思っていましたが、今回は更に倍角でもスクロールできるようになったことは収穫です。
多少なりともプログラミング力が上がったのかな? と思い込んでみました。
今回も素人ゆえの過ちや無駄が多いかもしれません。
もし、何かありましたらコメント投稿等でご連絡いただけると助かります。
今回はここまでです。
ではまた・・・。
コメント
2つのスケッチ(SPIFFSとSD-Card)が、問題なく動作したので報告します。M5Stack用のソフトは、ハードを配線しなくて良いので楽ですよね。
SD-Cardの方は、私のパソコンにはESP32_SD_ShinonomeFNTとESP32_SD_UTF8toSJISの2つのライブラリーが入っていないのでエラーが出ましたが、検索して見つけたページからダウンロードしたものを使いました。また、最初SD-Cardにフォントファイル(fontフォルダー)をコピーしなかったので、何も表示されませんでしたので、ちゃんとSD-Cardを入れたら動きました。
例のごとく、このブログ内容とソースコード+ライブラリーはまだ全然読んでいないので、またM5Stackをいじるときにじっくりみてきます。現在頭が本業のiPhoneソフトの開発モードなので。
あいむさん
いつも動作確認報告ありがとうございます。
m(_ _)m
一応、記事中に ESP32_SD_UTF8toSJIS と ESP32_SD_ShinonomeFNTライブラリ は別記事を参照してインストールするように促していましたが、やっぱり長い文章なのでスルーされてしまうかもしれませんね。
私自身もそうですが、やっぱりユーザー目線で見ると、パッと見て分かる方が良いのかもしれません。
いろいろ考え直してみます。
ソースコードを見られるのは、突っ込まれる要素満載なので、iPhone ソフト開発されている方々に見られるのは辛いものがあります。
何か気付いたことがあればコメント頂けると助かります。
iPhoneソフト開発がんばってください。
こんにちわ
さっそく動作確認させていただきました。
とりあえずご報告まで
マッキーさん
いつもご報告感謝いたします。
動いてくれるとウレシイですね。
ありがとうございます。
mgo−techさん、H.Wです。
以下の報告、上手くUP出来なかった様ですので再度UPさせて頂きます。
1.Yahoo!ニュース4行記事同時スクロールの件
1)画面最下部の「Get OK!」以降の時分表示ですが、一桁の時に直前に’0’が挿入されないので、表示に多少違和感があります。
→シリアルモニターの表示と同様に、#186行を以下のように書き換えられた方がよいのではないでしょうか?
#186 : Message( ” Get OK! ” + String(web_get_time ) );
(その前に以下の宣言文を2行追加)
char web_get_time[6];
sprintf(web_get_time, “%02d:%02d”, hour(), minute()); //ゼロを空白で埋める場合は%2dとする
2)タクトSWの’C’を短押し&長押ししてもスクロール速度が殆ど変化しない。
→SWITCH文内の#250行のインクリメント文と#256行のディクリメント文が逆になっていないでしょうか?
2.その他の件
1)当初から、どのスケッチのFlash時にも、’connection …—…—…—’が永遠に続いて書き込みができませんでした。
→EN端子とGND端子間にコンデンサ(0.22~2.2μF)を入れる対処Tipsが出されており、取り合えず手元にあった1.5μFのフィルムコンを挿入すると書き込みができるようになりました。(再現性あり…)
この対応策は妥当でしょうか? それともM5stackのロットによるものなのでしょうか?
尚、本体側にチップセラミックコンを実装対応されてる方もおられるようですが、 mgo-techさん含めて他の方々はどうされてるのでしょうか?
以上、宜しくご検討お願い致します。
H.Wさん
いつも当ブログをご覧いただき、ありがとうございます。
まず、回答です。
1-1) の件は把握しておりましたが、これは要は私の手抜きです。
メッセージの受信時間さえ把握できれば良いだけのメッセージなので、あまり表示はこだわりませんでした。
確かに、シリアルモニターにはちゃんと出力しているんだったら、こちらの方をちゃんとやれば良いのにというお話ですね。
私もそう思いましたので、早速直しました。
今後のスケッチも所々手抜きがありますが、その辺は個人ブログなので何卒ご容赦ください。
1-2) スクロール速度あまり目立った変化はありませんが、確実に変化していると思います。
瞬時押しが減速、長押しが加速ですので、スケッチは問題ないと思います。
ただ、scl_speed は増加で減速、減少で加速という値になっています。
実は、これは、scrolle interval 値に名前を変えた方が良かったかもしれません。
これは変数名が良くありませんね。
趣味独学だと、こういうところが適当でスミマセン。
今後新たにプログラムを組む時には気を付けたいと思います。
因みに、記事を4つもスクロールさせると、速度は遅いのであまり変化しないと思います。
それよりも、文字サイズを変えた方がスクロール速度は劇的に変わりますね。
それはブログ記事にも書いてある通り、あまり意味がない操作ボタンとなってしまいました。
その辺は販売品のように作り込んでいないので、あとは皆さんで改良していっていただければと思います。
2-1) は SPIFFS データではなく、スケッチを M5stack に書き込むという認識でお話します。
1~2年前の ESP32 ( ESP-WROOM-32 )関連開発ボードは、ESP32 の Revision が 0 が殆どでした。
その場合は、USBシリアルの書き込みバグが Espressif 公式でも発表されました。
そこで、EN端子にコンデンサを追加するという方法が有効だったのですが、Arduino core for the ESP32 のソフトウェア上でかなり改善され、Rev 0 でも特に問題無く書き込みできるようになりました。
M5stack は殆ど Rev 1 なので、改善された ESP32 が使われているはずですので、あまり書き込みできないという情報は私は聞いておりません。
Mac の場合は、USBドライバをまずインストールしなければうまくいかないようです。
Windows 10 の場合は接続した時点で自動的にドライバインストールされます。
もし、気になるようでしたら、Facebook のESP8266/ESP32環境向上委員会に質問してみてはいかがでしょうか?
そちらの方がプロフェッショナルが沢山いらっしゃるので、確実な情報が得られると思います。
別件ですが、Wi-Fi が接続できない問題は一つ原因が見つかりました。
私のESP32_WebGetライブラリの EWG_AP_Connect 関数内に、Serial.begin(115200); があり、Serial.beginが二重使用されていたことが原因でした。
これについさっき気付きました。
随分昔にこの症状に気付いていたのですが、しばらくこの関数を使うブランクがありすぎて、思い出すのに時間がかかりました。
まだ完全解決ではないのですが、これも一つの原因です。
大変失礼しました。
m(_ _)m
mgo-techさん、H.Wです。
ご多忙中、余計なお世話に早速対応して頂き有難うございます。
時間表示の前の’0’はなくてもよいかと思いますが、分表示の前にはやはり’0’挿入があった方がよい様に思いましたので、敢えて提案させて頂きました。
スクロールの件については、シリアルモニターを見ながら、数値が増えた方が速度が増すものと勝手に思い込んでいたものですから、的外れな指摘となってしまい大変失礼致しました。
また、m5stack(というかESP32)のスケッチ書き込み時の不具合対応経緯についてご説明頂き有難うございます。
ということは、現時点では本来EN端子とリセット端子間にコンデンサー等を追加しなくてもよい仕様に改善されている筈なんですね?
(確かに、この商品の’開発システム’という性格があるとは言え、ユーザ側で前記の様な対応をしなければならない様では多少問題ですものね?)
何れにしても、購入したスイッチサイエンスさんに先ずその旨問い合せしてみようと思います。
ご教示有難うございました。
H.Wさん
いろいろご指摘ありがとうございます。
何かと手抜きが多いかと思いますが、今後とも気楽に作っていきたいと思いますので、どうかご容赦くださいませ。
M5stackについては、確かに正規代理店のスイッチサイエンスさんにお問い合わせするのが一番良いと思いますね。
原因が判明するといいですね。