WROOMでUTF-8文字コードをShift_JIS変換して日本語漢字表示(半角カナ対応)してみました

ESP8266 ( ESP-WROOM-02 )

昔から作りたいと思っていたUTF-8漢字コードをShift_JISに変換するテーブルがようやくできました。

※半角カナに対応しました。(2016/2/8)

今まで、Webから取得したニュースや天気予報を Arduino や ESP-WROOM-02 で日本語表示させたかったのですが、殆どがUTF-8コードで作られていて、無料で使える Shift_JIS日本語漢字フォントが使えませんでした。 どこかにUTF-8をShift_JISコードに変換できるテーブルはないものかとネットを探しまくりましたが、ありませんでした。

Arduino IDE のスケッチ内部では UTF-8 コードで処理しています。
しかし、シリアルモニターではWindows環境ではShift_JISコードなのです。OSのデフォルトコードになってしまうようです。
ですから、スケッチ中に漢字を入力してシリアルモニターに表示させようとすると文字化けします。
世界的な流れとして、殆どがUTF-8に移行しているのに、なぜかWindows日本語バージョンはShift_JISっていうのが不思議ですね。
そんなこんなで、ず~っと前からUTF-8→Shift_JIS変換テーブルが欲しかったのです。

ということで、長い時間をかけて勉強してようやく自己流で完成するに至りました!!

スポンサーリンク


最初はC言語でできないかと、いろいろやってみたのですが無理でした。

JavaScriptなら出来る気がしたのですが、できませんでした。
JavaScriptでいい線までいったので、Javaで試してみました。
かなりいい感じでできたのですが、JIS第二水準漢字が変換してくれませんでした。

そこで、ネットのWeb上にある変換コード表を見ていると、Perl言語で自動生成させている方が多いということを突き止めました。
そこで初めてPerl言語に手を出して、コードを組んで自動生成させてみました。
この言語は結構クセがあり、内部文字列などという不思議な法則があって理解するまでに時間がかかりましたが、ようやく自分の納得いく変換テーブルの生成ができたんです。
ただ、JIS 13区は一つ一つ手入力でしたが・・・。

ここまでの道のりは長かった・・・。

Perl でUTF-8 3バイトの漢字コードを1づつカウントアップさせて、その文字からShift_JISコードに変換して、ファイルにバイナリ出力しました。
それをようやく GitHub にアップすることができました。
因みに、Perl言語はド初心者で、ネット情報ではなかなか体得できなかったので、この本が大変参考になりました。

かんたん Perl (プログラミングの教科書)
技術評論社
¥3,278(2024/03/29 10:00時点)

電子書籍版もあり、とても重宝しましたので紹介させていただきました。

では、この変換テーブルの使い方を説明します。
以下、Windows での使い方になります。

1.GitHubから変換テーブルをダウンロード

まず、GitHubにアップした私が自作したUTF-8→Shift_JIS変換テーブルをこちらのページからダウンロードします。
下図にあるようなところをクリックしてZIPファイルをダウンロードしてください。

ダウンロード完了したら解凍してください。
使うファイルは Utf8Sjis.tbl というファイルです。
後でこのファイルをESP-WROOM-02 のフラッシュメモリに書き込みます。
これはバイナリー形式で、UTF-8並びのShift_JISコードが書き込まれています。
JIS第一水準、第二水準があり、更に13区のコードも書き込みました。

※半角カナに対応しましたので、Ver1.1となってます。(2016/2/8)

2.使うもの

お勧めなのが、過去の記事で何度も紹介しているスイッチサイエンスさんのESP-WROOM-02開発ボードです。

ESP-WROOM-02開発ボード
スイッチサイエンス(Switch Science)
ESPr Developer(ピンソケット実装済)
スイッチサイエンス(Switch Science)

これの使い方はこちらのページを参照してください。
動作が安定していて、とても使いやすいですよ。

3.Arduino IDE にStaging版ESP8266ボードをインストール

予めこちらのページを参照していただき、Arduino IDE にStaging版ESP8266ボードをインストールしておきます。
Stable版では動作しませんのでご注意ください。

4.Arduino IDE プラグイン SPIFFS ファイルシステムアップローダーをインストール

予めこちらのページを参照していただき、Arduino IDEのプラグイン、SPIFFSファイルシステムアップローダーをインストールしておきます。
2016/2/1現在ではversion 0.2.0 になってます。

5.スケッチを入力

先にも述べましたが、日本語Windows の場合、文字コードはなぜかShift_JISが標準なので、Arduino IDEのシリアルモニターではShift_JIS表示となります。
しかし、Arduino IDEではUTF-8となるので、シリアルモニター内で日本語漢字表示させるためには、UTF-8からShift_JIS変換が必要になります。

そこで、スケッチに書き込んだ日本語テキストをWROOMフラッシュにアップロードした変換テーブルファイルを読み出してShift_JISコードを抽出して、シリアルモニターにバイナリ出力して漢字表示させてみるプログラムを組みました。

スケッチはこんな感じです。(※Macでは文字化けするかもしれません)

※半角カナに対応しました。(2016/2/8)

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <FS.h> //Arduino core for ESP8266 用 SPIFFSファイルシステムライブラリ

//***********セットアップ***************************************************
void setup() 
{
  Serial.begin(115200);  
  SPIFFS.begin();
  Serial.println();
  
  char str[7][127] = {
    {"UTF8→S-JIS 全角日本語漢字変換テスト表"},
    {"、。〃¢‐ 一倅怎瀁耀退!¥熙~"},
    {"※〒℃⇒⇔♪Ωαβγθπφ●○◎◆◇■□★☆"},//よく使われる記号
    {"①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ"},//JIS 13区文字
    {"㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡〝〟№㏍℡"},//JIS 13区文字
    {"㊤㊥㊦㊧㊨㈱㈲㈹㍾㍽㍼㍻∮∑∟⊿∪∩∠⊥≡≒√∵∫"}, //JIS 13区文字
    {"半角/ABC 全角/ABC 半角カナ/アカサタナワヲン ガパ ァィゥェォャュョ"}
  };
  
  byte utf8_1, utf8_2, utf8_3;
  byte sjis[2];
  byte str_SJIS_b[7][127];
  uint32_t sp_addres;

  byte array_cnt = 0;
  byte fnt_cnt = 0;
  int sjis_cnt = 0;

  for(array_cnt = 0; array_cnt<7; array_cnt++){
    while(str[array_cnt][fnt_cnt] != '\0'){
      if(str[array_cnt][fnt_cnt]>=0xC2 && str[array_cnt][fnt_cnt]<=0xD1){//UTF8 2バイト文字の場合
        utf8_1 = str[array_cnt][fnt_cnt];
        utf8_2 = str[array_cnt][fnt_cnt+1];
        utf8_3 = 0x00;
        fnt_cnt = fnt_cnt + 2;
      }else if(str[array_cnt][fnt_cnt]>=0xE2 && str[array_cnt][fnt_cnt]<=0xEF){//UTF8 3バイト文字の場合
        utf8_1 = str[array_cnt][fnt_cnt];
        utf8_2 = str[array_cnt][fnt_cnt+1];
        utf8_3 = str[array_cnt][fnt_cnt+2];
        fnt_cnt = fnt_cnt +3;
      }else{
        utf8_1 = str[array_cnt][fnt_cnt];
        utf8_2 = 0x00;
        utf8_3 = 0x00;
        fnt_cnt = fnt_cnt + 1;
      }
  
      if(utf8_1>=0x20 && utf8_1<=0x7E){ //半角ASCII文字を判別
        sjis[0] = utf8_1;
      }else{
        UTF8_To_SJIS_cnv(utf8_1,utf8_2,utf8_3, &sp_addres); //UTF8コードからUTF8→Sjis変換テーブルファイルのアドレスを計算
        SPIFFS_Flash_UTF8SJIS_Table_Read(sp_addres, sjis); //変換テーブルファイル"Utf8Sjis.tbl"からShift_JISコード取得
      }

      if((sjis[0] >= 0x80 && sjis[0] <= 0x9F) || (sjis[0] >= 0xE0 && sjis[0] <= 0xF0)){ //全角文字
        str_SJIS_b[array_cnt][sjis_cnt] = sjis[0];
        str_SJIS_b[array_cnt][sjis_cnt+1] = sjis[1];
        sjis_cnt = sjis_cnt + 2;
      }else{ //半角はこちらに分類される
        str_SJIS_b[array_cnt][sjis_cnt] = sjis[0];
        sjis_cnt = sjis_cnt + 1;
      }
      Serial.println("----------------------");
      Serial.print("UTF-8 Code=");Serial.print(utf8_1,HEX);Serial.print(utf8_2,HEX);Serial.println(utf8_3,HEX);
      Serial.print("UTF-8 Binary=");Serial.write(utf8_1);Serial.write(utf8_2);Serial.write(utf8_3);Serial.println();
      Serial.print("Shift_JIS Code=");Serial.print(sjis[0],HEX);Serial.println(sjis[1],HEX);
      Serial.print("Shift_JIS Binary=");Serial.write(sjis[0]);Serial.write(sjis[1]);Serial.println();
    }
    str_SJIS_b[array_cnt][sjis_cnt] = '\0';
    fnt_cnt = 0;
    sjis_cnt = 0;
  }

  Serial.println("/////////////////////////////");
  for(array_cnt = 0; array_cnt<7; array_cnt++){
    sjis_cnt = 0;
    while(str_SJIS_b[array_cnt][sjis_cnt] != '\0'){ //Shift_JISバイト列をバイナリ出力
      Serial.write(str_SJIS_b[array_cnt][sjis_cnt]);
      sjis_cnt++;
    }
    Serial.println();
  }
}
//**************メインループ*********************************************
void loop() {

}
//***********UTF-8コードをSPIFFS内の変換テーブルを読み出してShift-JISコードに変換****
void UTF8_To_SJIS_cnv(byte utf8_1, byte utf8_2, byte utf8_3, uint32_t* spiffs_addrs)
{
  uint32_t UTF8uint = utf8_1*256*256 + utf8_2*256 + utf8_3;
  
  if(utf8_1>=0xC2 && utf8_1<=0xD1){ //0xB0からS_JISコード実データ。0x00-0xAFまではライセンス文ヘッダなのでそれをカット。
    *spiffs_addrs = ((utf8_1*256 + utf8_2)-0xC2A2)*2 + 0xB0; //文字"¢" UTF8コード C2A2~、S_jisコード8191
  }else if(utf8_1==0xE2 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE28090)*2 + 0x1EEC; //文字"‐" UTF8コード E28090~、S_jisコード815D
  }else if(utf8_1==0xE3 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE38080)*2 + 0x9DCC; //スペース UTF8コード E38080~、S_jisコード8140
  }else if(utf8_1==0xE4 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE4B880)*2 + 0x11CCC; //文字"一" UTF8コード E4B880~、S_jisコード88EA
  }else if(utf8_1==0xE5 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE58085)*2 + 0x12BCC; //文字"倅" UTF8コード E58085~、S_jisコード98E4
  }else if(utf8_1==0xE6 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE6808E)*2 + 0x1AAC2; //文字"怎" UTF8コード E6808E~、S_jisコード9C83
  }else if(utf8_1==0xE7 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE78081)*2 + 0x229A6; //文字"瀁" UTF8コード E78081~、S_jisコードE066
  }else if(utf8_1==0xE8 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE88080)*2 + 0x2A8A4; //文字"耀" UTF8コード E88080~、S_jisコード9773
  }else if(utf8_1==0xE9 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE98080)*2 + 0x327A4; //文字"退" UTF8コード E98080~、S_jisコード91DE
  }else if(utf8_1>=0xEF && utf8_2>=0xBC){
    *spiffs_addrs = (UTF8uint-0xEFBC81)*2 + 0x3A6A4; //文字"!" UTF8コード EFBC81~、S_jisコード8149
    if(utf8_1==0xEF && utf8_2==0xBD && utf8_3==0x9E){
      *spiffs_addrs = 0x3A8DE; // "~" UTF8コード EFBD9E、S_jisコード8160
    }
  }
}

//***********SPIFFSフラッシュ内のUTF8Sjis変換テーブル読み込み*************
void SPIFFS_Flash_UTF8SJIS_Table_Read(uint32_t addrs, byte *buf)
{
//  Dir dir = SPIFFS.openDir("/");//これは読み取り速度が遅くなるかもしれないのでコメントアウトしている
  File f2 = SPIFFS.open("/Utf8Sjis.tbl", "r");
  f2.seek(addrs,SeekSet);
  buf[0] = f2.read();
  buf[1] = f2.read();
}

このスケッチをIDEにコピペして、一旦保存しておきます。

このコードを説明する前にちょっと解説しておきたいことがあります。

まず、Utf8Sjis.tbl ファイルをバイナリエディタで見てみましょう。
私が使っているものは定番のフリーエディタ Stirling です。Windows8.1の64bitパソコン環境でも問題なく動作していて、とても使いやすい日本語バイナリエディタです。

テキストファイルというものは、例えば ”ABC” 改行 ”あいう” という文字をUTF8コードで保存して、バイナリエディタで開くと、こう表示されます。

ウインドウの左側と上側にあるインデックスがファイルの中の住所にあたるアドレスを示しています。
半角の”A”はUTF8でもShift_JISでもASCIIコードで記録されます。つまり、
“A” = 0x41
“B” = 0x42
“C” = 0x43
となります。
改行すると、CR と LF というコードが書き込まれます。それが0x0D, 0x0Aですね。
その後にUTF8コードの”あいう”と入力すると
“あ” = 0xE38182
“い” = 0xE38184
“う” = 0xE38186
と書き込まれます。

では、それを踏まえてUtf8Sjis.tbl ファイルをバイナリエディタで見てみましょう。

こういう感じに書き込まれています。
GitHub で公開するにあたって、皆さんに個人用、商用問わず自由に使っていただくためにはライセンス表記しなければいけないみたいですので、ヘッダとしてMITライセンス文を埋め込んでいます。
MITライセンスの意味はネットで調べていただくと分かりますが、個人利用、商用利用、再配布は自由。ただ、無保証で、ライセンス表記はしてください。というものです。
URLも埋め込んでありますが、MITライセンス全文を表記できませんので、それの掲載先のリンクを表示してます。

その後、アドレス0xB0 からUTF-8並び順に存在するShift_JISコードを1づつカウントアップしながら書き込んでいます。
UTF-8の最も若い番号でShift_JISが出現するのは 0xC2A2 の2バイトコードです。
0xE2 から3バイトコードになります。
つまり、UTF-8はマルチバイトコードですので、扱いが難しいわけです。
そこで、下図の表のように1バイト目がE2からEFまでを分割してまとめて、不要な部分をカットしました。
その各ブロックでShift_JISの文字が最初に出現する位置を下の表で表しています。
そこから連続してカウントアップしたものをバイナリに書き込んでいます。

それでも、世界の文字全てを網羅したUTF-8コードからみると、Shift_JISはスカスカで空白だらけです。
でも、日本語、英語の他、ハングル文字、中国漢字、ロシア文字、アラビア文字、などなど、世界の文字やいろいろな記号がShift_JISとは比べものにならないくらい膨大なデータが入っています。
例えば、こんな特殊記号もありました。

Shift_JISにはこんな文字はありません。なるほど、世界がUTF-8化するわけですねぇ~・・・。
改めて、この文字コードとフォントを開発している人達はスゴイと思いました。尊敬に値しますね。

本当は余分な空白をカットせずに、すべてUTF-8並び順でバイナリ出力した方が、WROOMからのシーク速度は速いのですが、2Mバイトを超えてしまうので、仕方なくカットしました。
それで、235kBまで節約できました。
フラッシュに余裕があって良かったです。

前置きが長くなりましたが、このスケッチをザッと説明します。

●1行目:SPIFFSライブラリをインクルードします。
●7行目:SPIFFSファイルシステム開始
●10~18行: 文字列を事前に入力(UTF8)
 12行目の「¥」という文字はUTF8の一番最後の文字。
 「熙」という文字はShift_JISの一番最後の文字です。
●31~46行:半角文字、2バイト文字、3バイト文字の振り分け
●48行目:カナ以外の半角文字(ASCIIコード参照)を分別する。
●51行目:UTF8コードから変換テーブルファイルのアドレスを計算する関数呼び出す。
 ”~”という文字だけは該当のShift_JISコードが無かったので別に割り当てました。
●52行目:変換テーブルファイル”UTF8Sjis.tbl”を読み込んでShift_JISコードを抽出
●59~62行:カナを含めた半角文字はここで分類されて、配列を1バイト進めてます。
●123行目:SPIFFSのフラッシュ内のファイルを読み込みモードで開く。
 ここで注意することは、ファイル名の前にスラッシュ“/”を入れることを忘れずに。
●124行目:バイナリファイルのアドレス位置にシークする。
●125~126行目:バイナリファイルを1バイトづつ読み込む。

半角カナはUTF-8コードでは特殊です。

「。」~「ソ」までは0xEFBDA1~0xEFBDBF
「タ」~「゜」までは0xEFBE80~0xEFBE9F

という割り当てになってます。つまり3バイト文字です。
半角カナのShift_JISコードはありません。ASCIIコードと同じ1バイト文字です。
0xA1~0xDF
となっています。ですから、一度Shift_JISに変換してから分類した方が扱いやすいと思います。

以上です。アドレスを計算する関数ではif文が多く使われていて、文字コードを抽出する時間が遅くなってしまいます。今のところの私の頭ではこの他の方法は思いつきませんでした。
もっと良い方法が分かる方は是非教えていただきたいです。

6.UTF8→Shift_JIS変換テーブルファイルをフラッシュにアップロードする

現在のスケッチのフォルダを下図のようにクリックして開きます。

そこに下図のようにdataフォルダを新たに作成しておいてください。

そのフォルダに最初にダウンロードしたUtf8Sjis.tbl というファイルをコピー&ペーストしておきます。

その後、下図のように、ESP8266 Sketch Data Upload をクリックして、ESP-WROOM-02のフラッシュメモリにアップロードします。
※この時、シリアルモニターは閉じておいてください。起動していると書き込みできません。

白い点々が表示し始めたらアップロード開始しています。
Upload Speed が115200でも結構時間がかかります。
点々の進行が止まったらアップロード完了です。

7.コンパイル実行させる

コンパイル実行させると下図のように見事にシリアルモニターに日本語漢字が表示されています。
JIS 13区 の記号もバッチリですね。

文字列を一文字づつコード変換して、/////////の表示の後に6分割した文字列全体をシリアルモニターにバイナリ出力しています。

途中の文字ごとの表示はこういうことです。

前にも述べましたが、WindowsのシリアルモニターはデフォルトではShift_JISですので、UTF-8コードをそのままバイナリ出力すると文字化けします。
その様子も良く分かりますね。
半角文字は1バイトですので、2バイト目はゼロとなっています。

美咲フォントや恵梨沙フォントなどのShift_JIS並びのフリーフォントを使って、LEDマトリックスなどで表示させる場合には、以上のように一旦SPIFFSでフラッシュの中のコード変換ファイルを読み込んでUTF-8をShift_JISに変換して、再びフラッシュを読み込んでフォントデータを抽出する形になります。
つまり、2回フラッシュを読み込むことになるので、その速度は出来るだけ早い方が良いというわけです。
一番良いのは、UTF-8コード並び順にビットマップデータフォントがあることですが、空白だらけの余分なデータが多いので、メモリの少ない電子工作用マイコンに組み込むことは現実的ではないようです。

今回は以上です。
次回はこの文字コードを使って美咲フォントを読み出して、電光掲示板を作ってみたいと思います。

ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/03/29 13:47時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/03/28 20:55時点)
Excelでわかるディープラーニング超入門
技術評論社
¥1,700(2024/03/29 10:12時点)

コメント

  1. Sabotenboy より:

    手持ちの漢字ROM(SPI)をESP8266に読ませ、OLED(I2C)に表示させることは出来ましたがいかんせんJISコード指定なROMで^^;
    と言う訳で使わせていただきますm(__)m

    • mgo-tec mgo-tec より:

      Sabotenboyさん
      初めての変換テーブル使用コメントありがとうございます。
      実際に使っていただけるというのは、とっても有り難いことです。
      ジャンジャン使ってやってください。

  2. nopnop2002 より:

    こんにちは。
    esp-open-rtosで日本語を表示するために
    変換テーブルを使わせていただきました。
    こちらで公開しています。
    https://github.com/nopnop2002/esp-open-rtos-example

    ありがとうございました。

    • mgo-tec mgo-tec より:

      nopnop2002さん

      記事をご覧いただき、そして、変換テーブルを使って頂き、こちらこそありがとうございます。
      もう相当昔に作ったもので、お恥ずかしい限りの変換テーブルですが、それでも使って頂けるだけで感謝感謝です。
      m(_ _)m

タイトルとURLをコピーしました