桜のはなびらどっとねっとロゴ

桜のはなびらどっとねっと

【旧記事】Arduino Unoで漢字を扱う(文字一覧画像ファイルを使用する方法)

この記事は1年以上前に投稿された記事です。 この警告表示について

P1010168

Arduinoでグラフィック液晶などを利用する場合に漢字を表示したい。漢字はなくともせめてひらがなだけでも…。と思う時もあるかもしれません。そこで今回はArduinoで文字コードから漢字パターンを取得してみたいと思います。

お知らせ

こちらは画像ファイルを利用してフォントデータを取得するものですが。FONTXというフォントファイルを利用した方法を書きましたので、これから実装する場合はFONTXを使った方法で漢字表示を実装した方がいいと思います。

はじめに

Arduinoは海外製なので基本的に英文のみ扱えます。

しかし、実際にいろいろなハードウェアを制御したり、パソコンなどと通信する際に漢字を扱えたら…。と思う時もあるでしょう。そこで今回はArduinoで漢字を取り扱ってみたいと思います。

シリアル通信で漢字を扱う

ハードウェアの前にシリアル通信で漢字を扱ってみたいと思います。

※早くハードウェア側のやり方教えろという人用リンク

といっても、Arduinoのスケッチの文字コードはUTF-8であり、特に何らかの制限をしているわけではないので。単純にパソコンとの通信でArduinoからのデータ転送時に日本語を表示したいだけであればパソコン側でシリアルモニタではなく端末エミュレータを使用し、文字コードをUTF-8に変更してやるだけでとりあえず表示することはできます。

arkanji3 arkanji4
シリアルモニタでの表示 端末エミュレータを使用

 

理由は知りませんが。Arduinoのシリアルモニタのエンコードは「Shift_JIS」になっているので、UTF-8のままだと文字化けしてしまいます。というわけでシリアルモニタで漢字を表示してやるならShift_JISで文字コードを転送してしまえばOKという事になります。

文字コードがUTF-8なArduinoスケッチでShift_JISを表示するにはエスケープ文字を使います。

例えば「桜」という漢字のShift_JISコードは「0x8DF7」となるのでこれを1バイトずつエスケープ文字を入れて表記することでShift_JISの文字コードで文字を表示できます。

表示結果:
arkanji5

ただし、毎回エスケープ文字を入れてコードを書くのは少々面倒だし、実際のところArduinoIDEのシリアルモニタは性能が良くないので、外部の端末エミュレータを使用した方がよいと思います。

外部の端末エミュレータを使用する場合の注意点は、端末側で繋ぎっぱなしにしているとArduinoIDEからプログラミングの書き込みができなくなるので、必ずプログラムの転送前には接続を切っておく必要があるというところくらいです。標準のシリアルモニタが転送前に毎回閉じるのもこの辺が理由。

フォントから文字コードを指定して漢字パターンを入手する

さて本題です。

例えばグラフィック液晶や、前回RaspberryPiで使用したドットマトリクスLEDを使用する際に、何らかの文字を表示したい時があります。英数字やカタカナ程度であればある程度簡単にバイナリ表記(0b~)にしてパターンを書いて表示させることができますが。漢字を表示しようとするとなかなか骨が折れる作業です。

そこでもう少し簡単に文字を表示したい。できればパソコン等で入力した文字をそのまま表示したいという時もあるでしょう。その場合Arduinoでどうやって表示するかというと…

  • 頑張って文字パターンを書き込む
  • 漢字ロムを使用して表示する
  • フォントファイルを使って文字を表示する

という3つの手法が思いつきます。

1つ目は論外ですが、ネット上では漢字ロムを使用して文字パターンを取得する方法がよくとられているようです。しかし、漢字ロムは意外と高いし、表示できる漢字に制約があったり文字コードが特殊(というか今はマイナー)だったりするようなので、やはりここはフォントファイルを使用して好きな文字を表示したいところです。

以前よりこのサイトでは8x8pxのフォントの画像版を使用して8x8pxのフォントを表示する方法と取っておりました。これをArduinoでもできないかと考えたのですが、Arduino Unoで使用しているAVRマイコンはメモリがたった2048バイトしかありません。2048バイトでは英数字も入りません。プログラム用のフラッシュメモリは32kbあるのでうまくプログラムを作れば何とかやりくりできるでしょうが。そんなプログラムを書いていたら来世紀になってしまいます。

幸いにもArduinoではSDカードを読み書きするライブラリが用意されているので、これを利用してSDカード内に格納した画像データファイルを開いて、ファイル内をシークすることで画像データから漢字パターンを読みだしてみたいと思います。これならメモリの使用領域は1文字あたり64バイトとなるのでArduinoでも十分表示できます。(今回作成したサンプルコードで半分近くメモリを使用するのであまり多くの文字は扱えませんが。)

というわけで今回は

  1. 文字コードをシリアルコンソールで入力する
  2. 入力した文字を受信して配列に格納する
  3. 格納した文字コードをJISコードに変換する
  4. JISコードから区点コードに変換する
  5. 区点コードから文字がある位置のバイト数を計算
  6. 計算したバイト数をシフトしてデータを抜き出す
  7. 配列に入れて返す

という手順を行います。

※漢字ロムの代わりにSDカードを使用する場合のメリット

  • 好きなフォントサイズのフォントが使える
    ただし8・16・24pxと、8bit単位で扱えるものが扱いやすい。
  • 書体も好きなモノが作成できる
  • 既存のフォントを持ってくるのなら作成が楽
  • ハードウェアが汎用品なので他のデータと一緒に格納することが出来る
  • 気合を入れて作ればUnicode対応とかもイケる
    絶対にやりたくないが。
  • 漢字の追加や修正が容易

デメリット

  • プログラムが複雑になる
  • 漢字パターンを一から作成する場合は非常に手間
  • プログラムが複雑なので処理速度が劣る
  • 汎用品であるために若干耐久性が劣る
  • 汎用品であるから使い回しが効くという点を考えないなら価格が高め

 

フォントデータの作成

今回も前回同様美咲フォントの画像データを使用します。「misaki_gothic.png」もしくは「misaki_mincho.png」のデータを

カラー液晶や前回作成したドットマトリクスLEDのように、横1列ずつ転送する場合は「上下反転」させ、モノクロのグラフィック液晶などのような縦1列ずつ転送する場合は「左90度回転」させてモノクロビットマップとして保存してください。

このファイルを「8.3形式」のファイル名形式で保存する。(例:SAKURA87.BMP)
ファイルはSDカードのrootに保存する。

SDカードの使用

SDカードを使用する場合はSDカードシールドを使用します。
SDカードシールドは色々なメーカーから出ていますし。前回Raspberry Piで使用したカラー液晶にもSDカードスロットがついていましたので今回はそれを使用します。

SDカードシールドは基本的に11・12・13・4番ピンを使っている物が多いようですので今回はそれに則り以下のように接続します。

  • 11-MOSI
  • 12-MISO
  • 13-SCLK
  • 04-SlaveSelect

※Arduinoは5V系ハードウェアなのに対し、SDカードは3.3V系ハードウェアなので、信号のレベル変換が必要になります。きちんとしたシールドではレベル変換ICを使っているようですが、多くの場合は抵抗で分圧しているようですので。Arduino用に設計されたハードウェア以外を使用する場合は必ず何らかの方法で5V信号を3.3Vに変換してください。

文字コード変換

今回はShift_JISで文字データを受けますが、実際に扱うのはJISコードとなり、更にそれを区点コードに変更して使用します。(直接区点コードに変換してもいいのですが。分けたほうがJISとShift_JIS両方に対応できるからね。)

Shift-JISからJISへの変換は「とほほのWWW入門」という老舗サイトの漢字コードについてというページ内にある変換コードを元に作成しました。Raspberry PiではEUC-JPから直接区点コードになるように計算をしていましたが、Shift-JISはちょっと計算が面倒くさいみたいです。

符号なしキャラクタ型でkanji変数(配列数は2)を作ってその中に元の文字コードを入れ、そのデータを変換します。AVRマイコンなので少しでも高速化させるために*2をビットシフトで行ったり、と若干の高速化を図っています。(これくらいの最適化はコンパイラがやるだろうから効果があるかは不明)

データ読み込み関数の作成

というわけで前準備が終わりましたので文字パターンを読み込む関数を作成したいと思います。

まずはSDカードライブラリの仕様を確認しておきます。

  • SDカードはSDHCまで対応
  • 基本的にはSPIで通信する
  • 8.3形式のファイル名のみ対応
  • 一度に開けるファイルは1つのみなので終わったら毎回閉じる必要がある
  • SDカード自体を閉じる命令はないのでsetup関数であらかじめSDカードを開いておく。

次に読み込むフォントファイルおよびBMPファイルの仕様を確認しておきます。

【フォントファイルの仕様】

  • Windows形式の白黒1bitBMPファイルである
  • 区点コードの並びで印刷可能文字が並べられたファイルである
  • 半角文字は使用せずに全角エリアのみ使用する。
  • 1文字あたり8×8ピクセルである
  • 画像ファイルは「上下反転」で記録されている
    グラフィック液晶など1バイトを転送すると縦方向で表示されるの製品に転送する場合は「左90度回転

【ビットマップ形式の仕様】
※Windows7のペイントツールで作成した場合の条件

  • BMP共通のヘッダが14バイト
  • Windows Bitmap形式の場合は40バイトのヘッダ
  • カラーパレットが2色分(Windows Bitmapは1色あたり4バイト)
  • ↑合計で62バイトのヘッダのあとに画像データが来る
  • 画像データは「下から上」「左から右」に記録されている
    フォントファイルで「上下反転」した理由がこれ。
    これで変な計算をせずにファイル先頭から文字コード分読み込んでいけば良くなる。
  • 1ドットあたり1bitなので1バイトあたり8pxであり、8×8ドットのフォントだと都合がいい
  • 横1行のデータは4バイト区切りで記録されていて、あまりが出た分は0埋めされる。

今回のフォントデータは752pxで1バイトあたり8pxで全94バイト。
2バイト分穴埋め処理が発生しているので1行あたりは96バイトとなる。

つまりは区1行あたりのバイト数は768バイトである。
というわけで「ヘッダ62バイト+(768×区の数値)+点の数値」をオフセット値として設定することで目的の文字を抜き出すことが出来、さらに96バイトずつオフセットを増やすことで1文字分のデータを抜き出すことが出来る。

  • ヘッダサイズ+(768×区)+点+(96×行数)

という計算式になるはずです。1文字あたりは縦8pxで構成されているので、これを8行分読みこめば目的の文字パターンが取得できます。

例えば「お」の文字コードは0x242Aであり、これを区点にすると区4 点10になります。
※区点コードは1から始めていますが、最初の行はオフセットが必要なく、オフセットの値は「0」になる必要があるので、計算式に使うのは区点とも1少ない値になります。

これを上の式に当てはめて計算することによって1行目のオフセットを出します。

  • 62+(768×3)+9+(96×0)

これを計算すると

  • 62+2304+9+0

こうなり、結果オフセット値は「2375」となります。

これに1行あたり96バイトずつずらしていくことで「お」の文字パターンを抜き出すことが出来ます。

これを美咲フォントの画像ファイルを拝借して説明するとこんな感じ↓

misaki_gothic_setumei

※点の9バイトのオフセットは分かりやすいように文字全体を塗りつぶしていますが、実際は「1行」のオフセットになります。

プログラムの作成

今回のプログラムは以下の仕様にしようと思います。

  • SDカードからビットマップファイルを読み込む
  • 8×8ピクセルのフォントファイルに対応
  • フォントファイルは上下反転させる
  • 転送方式の違い(1行ずつ転送するのか、1列ずつ転送するのか)に対応させる
  • ※その他これまでの説明通りに作成した画像ファイルを使用すること。
  • 読み込み方向,読み出し先変数,文字コード格納変数 の順に指定する
  • 受け付ける文字コードはJISコード。実際の使用は区点コード
  • JISコードを使用するので全角文字のみ対応させる。
  • SDカードを共有化させるためにファイルは毎回閉じる

まずヘッダーに事前準備をします。

どういうことを意味しているのかは後ろのコメントを見てください。
SPIライブラリ・SDカードライブラリをインクルードして、必要な固定値を定義しておきます。

次にsetup関数に以下のコードを追加してSDカードを初期化します。

そして作った関数が以下のとおりです。

フォントファイルを開いて、ファイルシーク関数で目的の文字までシーク、その後8行分読みだして配列に入れ、閉じて帰ります。計算式に変なものがついていたり、やたら0が多かったりしますが。これは何故か(恐らくAVRの仕様)ですべての値に型を指定しないとすべてint型(2バイト)で計算されてしまうのでこうしています。(unsigned long型の変数に入れても駄目だった。)

文字コードの指定はunsigned char型の配列で上下バイトで分けて格納します。
(シリアル通信の受信仕様に合わせました。)

文字パターンを格納する変数はunsigned char型で8個の配列を作ります。

ローテーションの数値指定は…

  • 0:場合がローテーションなし
    画像の縦が区で横が点、横8ドット分が1つの配列に格納される
  • 1:場合がローテーションあり
    画像の縦が点で横が区、縦8ドット分が1つの配列に格納される

arkanji6

実際の使用

実際にこれらを使用するコードを載せておきます。

arkanji2

端末エミュレータの設定はこの通りに設定します。
文字コードは送受信ともSjift-JISに。改行コードは送信:CR 受信:LFで設定。
特に受信改行コードはLF、送信文字コードはShift-JISに必ず設定してください。
標準のシリアルモニタを使用する場合は何も設定しなくて問題無いです。

何らかの文字を入力し、エンターを押せば文字が表示されるはずです。

arkanji

この関数を各種デバイスの制御プログラムに組み込むことで簡単に感じを表示することが出来ます。

終わりに

というわけでやたら長い記事になりましたが(文字数カウント1万文字超えてるし。)Arduinoで漢字を扱う方法は以上です。単純にシリアルコンソールで漢字を扱えるようにするにはそんなに面倒なことはありませんが、デバイスに漢字を表示させるのは少々面倒ですね。特にArduinoのメモリは非常に少ないのでRaspberry Piみたいに広大なメモリ領域とストレージ領域を武器にゴリ押しすることが出来ないので色々と面倒くさい事になっています。

しかし、漢字を扱えるようにするメリットは十分あります。漢字が使えなくてもひらがなが使えるようになるだけでもかなり制作物の幅は広がるのではないでしょうか。また、今回のコードは比較的柔軟なコードのため、文字サイズを16・24・32px…と増やしていっても、画像ファイルさえ作ってしまえば少々のコード変更で使用出来ると思います。(char型をint型にする必要があるのでメモリが若干増えることになると思いますが…。)

おまけ(2016年2月2日追記)

この記事を基にした制御文を書いていくうちに、漢字表示部分のコードを良くしたので載せておきます。
なお、このコードは高速化のためにリターンコードを返さなくなっていますのでその点にはご注意ください。

 

“【旧記事】Arduino Unoで漢字を扱う(文字一覧画像ファイルを使用する方法)” への2件のフィードバック

  1. 色電 より:

    色々なサイズと教科書体、明朝体、ゴシック体、丸ゴシック体の入ったSDカードを考えたが難しそう。とりあえず英数記号ひらがなカタカナだけ、24×24と12×12、24×24が明朝12×12をゴシックにすればなんとかなりそう。

コメントを残す

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