watchX:日本語で「こんにちわーるど!」

M5Stackの時につかってた、ひらがなデータを表示してみようと思います。

M5Stack:日本語でこんにちワールド!
http://shikarunochi.matrix.jp/?p=2212


シリアルモニタへの出力

その前に、デバッグ用にシリアルモニタへの出力を試してみたのですが、単純に

Serial.println("Hello World!");

と書くだけだと、出力できませんでした。setup()でシリアルの立ち上がり待ちしてあげる必要あるみたいです。

void setup(){
  Serial.begin(115200);
  while (!Serial);

こういう感じ。そうすれば後は普通に。

Serial.println("Hello World!");
Serial.println("こんにちワールド!");

日本語もOK!


フォントデータ

今回、フォントデータはひらがなだけ定義するのですが、それでもサイズが大きくて、メインメモリの変数では定義できません。フラッシュメモリ上に配置します。そのための書き方はこういう感じ。PROGMEM を付けて書きます。

const byte jpFont[] PROGMEM ={ 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x3F,0x80,0x08,0x00,0x0F,0x00,0x1A,0x80,0x2A,0x80,0x2C,0x80,0x19,0x00,0x00,0x00,//ぁ
0x00,0x00,0x04,0x00,0x04,0xC0,0x7F,0x00,0x08,0x00,0x0F,0x80,0x19,0x40,0x29,0x20,0x4A,0x20,0x4C,0x20,0x34,0x40,0x01,0x80,0x00,0x00,//あ
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x21,0x00,0x20,0x80,0x20,0x80,0x24,0x80,0x18,0x00,0x00,0x00,0x00,0x00,//ぃ
0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x20,0x80,0x40,0x40,0x40,0x40,0x40,0x20,0x42,0x20,0x24,0x00,0x18,0x00,0x00,0x00,0x00,0x00,//い
:
};

こうやって定義した変数にアクセスするには、pgm_read_byte_near() 関数を使います。

byte fontData = pgm_read_byte_near(&jpFont[0]);

これで jpFont[0] が取得可能。


日本語文字の扱いは?

ちゃんと文字列処理をしないといけないところなのですが、String が使えるなら頼ってしまいたいところ。
ひらがな→フォントデータアドレスに変換をかけるときに、watchX のString形式で日本語文字をどういう形式で保持しているのかを確認します。

String jpData = "あ";
Serial.print("length = ");
Serial.println(jpData.length());

結果は

length = 3

1文字あたりの内部での文字長は3。日本語で1文字としては処理されてないみたいですね。ということなので、日本語1文字を取得するならこういう感じで。

String jpData = "あいう";
Serial.println(jpData.substring(0, 3));
Serial.println(jpData.substring(3, 6));
Serial.println(jpData.substring(6, 9));

これで「あ」「い」「う」取れました。

ひらがな表示!

フォントデータの順にあわせて、インデックスをこういう感じで作って

const String jpHiraIndex = "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんー";

これで、

String s = "あ";
int fontIndex = jpHiraIndex.indexOf(s)/ 3; //内部では日本語1文字を3文字で保持なので3で割る

何文字目かを取得。1文字のフォントデータは2バイトなので、フォントのデータの先頭は

byte fontData = pgm_read_byte_near(&jpFont[fontIndex * 26]);

こうなりますね。ここから26バイトがこの文字のフォントデータ。あとはデータに合わせてドットを打てばよし。

void drawJPString(byte x, byte y, String s){
  int posX = x;
  for(int index = 0;index < s.length();index=index + 3){ //内部では日本語1文字を3文字で保持
    drawJPChar(posX, y, s.substring(index,index + 3)); //1文字ずつ切り出して表示
    posX = posX + 12;//1文字分、位置を横に移動
  }
}
void drawJPChar(byte x, byte y, String s){
 int fontIndex = jpHiraIndex.indexOf(s);
 if(fontIndex >= 0){
   fontIndex = fontIndex / 3;
   uint8_t dotPosX = x;
   uint8_t dotPosY = y;
   for(int i = 0;i < 13;i++){ //フォント縦 13ドット
     for(int j = 0;j < 2;j++){ //フォント横 2バイト = 16ドット中、12ドット使用
       byte fontData = pgm_read_byte_near(&jpFont[i * 2 + j + fontIndex * 26]);
         for(int index = 0;index < 8;index++){
         if(fontData >> (7 - index) & 1 == 1){ //上位バイトのほうが左側
           if(dotPosX < 128 && dotPosY < 64){ //画面内に入ってるかチェック
             drawPixel(dotPosX, dotPosY, 1);
           }
         }
         dotPosX = dotPosX + 1;
       }
     }
     dotPosX = x; //ドットのX座標を一番左に戻して
     dotPosY = dotPosY + 1; //ドットのY座標を一つ下に。
    }
  }
}

(x, y)の位置から、 s の文字を表示します。

String s = "あいうえおかきくけこ";
drawJPString(0,0,s);
s = "さしすせそたちつてと";
drawJPString(0,13,s);
s = "なにぬねのはひふへほ";
drawJPString(0,26,s);
s = "まみむめもやゅゆょよ";
drawJPString(0,39,s);
s = "らりるれろわゐゑをん";
drawJPString(0,52,s);
ssd1306_drawBuffer(0, 0, 128,64, mbuf);

これで呼び出し。

きれいに出たー。


全体のソースはこちら。