公開日:2023/04/08
title.png

25th Season


#25-3 メッセージカードならぬボックス

 オールインワンのマイコンボードM5StickC Plus(下図)を使って、メッセージボックスを作成しようと思います。

a233_m5cp_pkg.jpg

 誰か大切な人にプレゼントをする時、プレゼントにメッセージを添えるため使われるのは、メッセージカードですよね?それをM5StickC Plusで作ろうと言うのですが、「カード」と呼べるようなサイズ感ではないので、メッセージボックスって事です。
 設計を進めて行きましょう。

メッセージボックスの目標仕様

 大切な相手へのプレゼントを入れた箱🎁の中に、一緒にM5StickC Plusを入れておきます。M5StickC Plusは、以下のような働きをします。

  TFTディスプレイ+オルゴール=メッセージボックス。

 TFTディスプレイへメッセージを表示するようにしておく事によって、メッセージカードならぬメッセージボックスとして使用します。ただの「カード」とは異なるので、シチュエーションに合った音楽も演奏して、演出しましょう。

 音楽は、前回作成したplay_mml()関数MMLを使って演奏します。

 箱の蓋を開けると、M5StickC Plusから音楽が演奏され、同時にTFTディスプレイにメッセージが表示されます。例えば、蓋の裏にM5StickC Plusを固定しておくと、蓋を開けた時に丁度TFTディスプレイが目に入って、メッセージを確認することができます。

 TFTディスプレイに、単なる文字列を表示しても良いけれど、何らかのグラフィックを表示できた方が良いですよね。カラフルに飾ったり、ちょっとしたイラストを添えたり、手書きのメッセージを表示したり、演出の幅が広がります。

 グラフィックデータの保持方法は、M5.LcdクラスのdrawBitmap()メソッドで描画可能なビットマップデータとするのが一番簡単でしょう。後ほど、もう少し検討を進めてみます。

シチュエーション

 実用的には、プレゼントを渡すシチュエーションによってTFTディスプレイに表示するメッセージや、演奏する音楽を変更して使う事になります。

 今回の例題としては、バースデイボックスとして作成しておきます。

 ただし、色々なお祝い事に対応可能な様にプログラムしておきたいと思います。

 従って、演奏するMML文字列と、メッセージ用ビットマップデータを、以下の様に指定可能な構造にしておきます。

  // message box definition modules
  #include "mml_happy_bd.h"
  #include "bmap_happy_bd_1.h"
  #include "bmap_happy_bd_2.h"
  const char **mbx_mml=mml_happy_bd;
  const uint16 *mbx_bmap[ ]=
  {
    bmap_happy_bd_1,
    bmap_happy_bd_2,
    NULL
  };


 ヘッダファイル内で、MMLとビットマップデータを定数宣言してあります。それをインクルード(#include)しておいてから、最終的に、mbx_mmlに演奏するMML、mbx_bmap配列にビットマップデータを指定します。

バッテリ持続時間

 M5StickC Plusは、電源投入した状態で箱の中に設置しておく事になります。

 少なくとも、M5StickC Plusを設置してから、相手が箱を開けてメッセージを確認するまでの間、バッテリで稼働し続ける必要があります。

 M5StickC Plusで万歩計を作成した時、コンパイル時のCPU動作周波数F_CPUとバッテリ動作可能時間の相関データは取得済みです。

  「#24-8 万歩計をスマートに作った

 シチュエーションによって想定される、M5StickC Plus設置から、相手が箱を開けてメッセージを確認するまでの時間により、コンパイル時にF_CPUを低下しておいて、低消費電力化することは可能です。
 とは言え、相手に渡す直前(数分〜数10分前)に電源ONすれば良い話なので、最速のF_CPU=240MHzでも、十分な待機時間を稼げるはずです。万歩計での計測では、F_CPU=240MHzでも2時間ほど稼働できるので、相手に箱を開けてもらうまでの短時間だと割り切れば、F_CPU=240MHzでも十分成立するでしょう。データをご確認の上、ご判断下さい。

メッセージ用グラフィックデータ

 TFTディスプレイに表示する、メッセージ用のグラフィックデータに関して、考察を追加しておきます。
 M5StickC PlusのTFTディスプレイは、240×135[ドット]の長方形です。これを横長画面として使用する事と決めます。従って、ユーザが用意すべきグラフィックデータ仕様を以下の通り定義します。

  メッセージ用のグラフィックデータ仕様:

 ただし、240×135[ドット]分だと、1ドット当たり16ビットピクセルなので、約64Kバイトのデータとなります。プログラムROMとして1.25Mバイト使用可能なESP32をもってすれば、大したサイズではありませんね。

M5StickC Plusライブラリには、JPEG形式を描画するメソッドもあるみたいですが、対応フォーマットや圧縮パラメータに癖があったりすると、すこぶる面倒なので、敬遠しました。


 表示するメッセージは静止画でも良いですが、紙ではなく、TFTディスプレイに表示するのなら、アニメーション表示できる事が理想です。30fpsのフルアニメーションとは言わないまでも、毎秒数コマほどのパラパラアニメーションができると、例えば下図の様に表現力が格段にアップするでしょう。

a253_happybd.png

 アニメーションPNG対応のブラウザでご覧の場合、上の図がパラパラアニメーションしているはずです。

アニメーション

 と言う事で、ESP32のメモリ(ROMサイズ1.25Mバイト)が許す限りのビットマップデータを用意しておいて、アニメーションメッセージを表示できるようなデータ構造としておきます。

 以下の形式で、アニメーションのフレーム描画順番を指定可能とします。

  const uint16 *mbx_bmap[ ]=
  {
    bmap_happy_bd_1,  // ビットマップデータ配列へのポインタ
    bmap_happy_bd_2,
    :
    : (中略)
    :
    bmap_happy_bd_x,
    NULL  // 終端(アニメーションは、先頭に戻って繰り返し)
  };


 アニメーションフレームの表示順を指定する配列変数です。NULLポインタが見つかるまで、メモリの許す限りフレームの表示順番を指定可能です。但し、別途ビットマップデータを保持する必要があります。
 限りあるESP32のROM(Flashメモリ)に記録する事になるので、ご注意ください。

※沢山のフレームを使って、細かいアニメーションを作りたい場合は、JPEGの採用を含め、何らかのビットマップデータ圧縮方式を採用する必要があるでしょう。ここから先の応用は各自で工夫して見てください。

サイズと表示位置の調整

 用意したビットマップデータのサイズと、TFTディスプレイへの表示(描画)位置は、以下の通りに調整します。

 ビットマップデータのサイズによって、表示位置が調整されるため、全てのフレームは同じサイズで統一して準備する事を推奨します。

音楽演奏とアニメーションの同期

 アニメーションの各フレームは、音楽の演奏と同期して、MMLの区切り毎に更新(描画)します。フレーム/秒(FPS)を一定にしたい時などは、MMLの区切り方を工夫してください。

  const char *mml_sample[ ]=
  {
    "T2000,C44",  // ここでフレーム#0を描画
    "D44",  // ここでフレーム#1を描画
    "E44",  // ここでフレーム#2を描画
    "F44",  // ここでフレーム#0を描画
    "G44",  // ここでフレーム#1を描画
    "A44",  // ここでフレーム#2を描画
    "B44",  // ここでフレーム#0を描画
    NULL
  };


 "T2000"(全音符が2,000ミリ秒)で演奏する場合、4分音符"C44"等の演奏時間は、2,000ms÷4=500msです。よって上の例だと、約500ms毎にフレームが描画されます。言い換えると、毎秒2コマのアニメーションです。
 MMLの各行を演奏し終わる度に、順次フレーム番号が増加して行って、フレーム番号がフレーム数N以上となったら、最初のフレーム(#0)に戻って繰り返します。

  const char *mml_sample[ ]=
  {
    "T2000,C44",  // ここでフレーム#0を描画
    "D44,E44",  // ここでフレーム#1を描画
    "F44",  // ここでフレーム#2を描画
    "G44,A44",  // ここでフレーム#0を描画
    "B44",  // ここでフレーム#1を描画
    NULL
  };


 一回目のフレーム#1と、2回目のフレーム#0は、4分音符×2個分=約1s間表示されます。それ以外のフレームでは、4分音符×1個分=約500ms間の表示です。
 上例「ドレミファソラシ♪」程度のMMLなら、1行に記述することができてしまいますが、その様に長いMMLを記述すると、1行分のMMLを演奏し終えるまで、アニメーションデータが更新されなくなります。うまく区切りを調整してお使いください。

 なお、ビットマップデータの描画処理と、音楽の演奏処理は、1つのプロセスでブロッキング処理になっています。
 F_CPUを低下させてコンパイルした場合、ビットマップデータの描画に時間を要する様になるので、音楽演奏のタイミングがずれて、不自然に聞こえる場合があります。それが気になる場合は、バッテリ動作可能時間を犠牲にしても、F_CPU=240MHzを維持することを推奨します。

ビットマップデータ変換用スクリプト

 TFTディスプレイへ表示するためのメッセージグラフィックを、C言語でコンパイル可能なビットマップデータ形式に変換するため、Olive+のスクリプトを作成しました。以下リンクよりダウンロードしてお使いください。

  変換スクリプト:bitmap16.olv

※スクリプト(.olv)の実行には拙作Olive+をWindowsにインストールする必要があります。

 上のスクリプトは、画像のサイズチェックも、リサイズもしません。指定されたグラフィックを、M5StickC Plusで描画可能な16ビットピクセルのビットマップデータに変換するだけの汎用変換スクリプトです。こちらで定義した仕様に従ったグラフィックデータを用意するのはユーザ責任で、お願いします。

 ビットマップデータのサイズが小さければ小さいほど、命令ROM内の占有サイズは小さくできます。
 メモリ(ROMサイズ)が許す限りのビットマップデータを保持可能なので、必要なパラパラアニメーションのフレーム数分用意してください。


 主に、メッセージの表示方法についての検討を終了しました。パラパラアニメーションの採用、演奏する音楽との同期等についても考察を広げました。次回、詳細設計を経て、メッセージボックスを完成させたいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home