IoT何をいまさら(117) ESP32C3版Xiaoで車輪の再発明、メニューの改良

Joseph Halfmoon

前回までで、多少「行編集」っぽい入力ができるようになったので、今回はメニューの見た目を改良してみたいと思います。やっぱりメニューはコンソール画面の一番上に固定したいです。ついでにちょいと目立つようにお化粧もしたい。また、エラーとかステータスとかの出力は画面の下の方に別表示としたいです。古き良き時代をリスペクト?

※「IoT何をいまさら」投稿順Indexはこちら

制御コード、エスケープシーケンス、CSIシーケンス

この年寄が若いころ、シリアル端末の業界標準、VT100やVT220の現物を使ってました。VT100(ブイティーハンドレッド)は白黒画面で結構デカかった記憶。それに比べるとVT220(ブイティートゥートゥエニイ)はコンパクトで、アンバーイエローのディスプレイだった記憶。いずれにせよ、80文字x24行くらいの画面で作業をしてました。

VT100やVT220の現物は消えても、それが起源の制御コード、エスケープシーケンスは現代の仮想端末ソフトの中にしっかりいきのこってます。今回使用させてもらっている定番ソフト Teraterm Proもそのようなソフトウエアの一つです。

前回までは画面上で編集をするのに、0x01から0x1Fまでの1バイトの制御コード、および、0x1Bに続いて1バイトのコードを送る2バイトのエスケープシーケンスを使ってました。

今回からはエスケープシーケンスの中でもCSIと呼ばれるらしい制御シーケンスを使っていきます。エスケープ(0x1B)の後に ‘[‘ が来て、その後ダラダラと制御パラメータが続き、最後に制御コマンド文字で〆るもの。文字に色をつけたり非常に自由度が高い制御ができるコードです。オリジナルのVT100は白黒2色の端末だったので、色などはどこかで拡張された?のだと思いますが、制御コードの素人にはその歴史は分からず。

今回追加した機能とそのソース

今回追加するのは以下のような機能です。とりあえず画面はVT100式の80文字x24行を想定してます。

    1. メニュー行を画面の最上位行に表示する
    2. メニューと他の入出力行の違いを一目瞭然とするために、メニューはリバース表示とする
    3. 何か処理した結果や、エラーなどは画面の下の方(24行目)に表示する
    4. 上記の出力も他との違いを目立たせるためにリバースとする

そこで、以下のようなCSIシーケンスを仮想端末に送る関数を追加するとともに、メニューを表示するblockingMenu()関数に若干の変更を加えました。こんな感じ。

void CSIseq(const char arg[]) {
  Serial.print((char)0x1B);
  Serial.print('['); 
  for (int i=0; i<strlen(arg); i++) {
    Serial.print(arg[i]); 
  }
}

void scrHome() {
  CSIseq("1;1H");
}

void setRev() {
  CSIseq("07m");
}

void setNorm() {
  CSIseq("0m");
}

void eraseLine() {
  CSIseq("2K");
}

void dispStat() {
  saveCursor();
  CSIseq("23;1H");
  eraseLine();
  setRev(); 
}

void dispMain() {
  restoreCursor();
  setNorm();
}

void dispSPC(int arg) {
  for (int i=0; i<arg; i++) {
    Serial.print(' ');
  }
}

char blockingMenu(char *buf) {
  scrHome();
  setRev();
  eraseLine();  
  Serial.print(buf);
  dispSPC(76-strlen(buf));  
  setNorm();
  char selchar = blgetASCII(0x3);
  Serial.println("");
  return selchar;
}

また、ステータス/エラー行の表示に対応させるため、menuを呼び出しているメインループ側も若干修正してます。

void loop(){
  bool menuEnable = true;
  int choice = 0;
  char buf[BUFMAX];
  bool temp;
  char menuStr[] = "MENU> C(md D(rive F(ile Q(uit ";
  while (menuEnable) {
    choice = blockingMenu(menuStr);
    eraseLine();   
    switch (choice) {
      case 'C':
        temp = blockingReadLine(buf, BUFMAX); 
        dispStat();
        if (temp) {
          Serial.print("Input Str: ");
          Serial.println(buf);
        } else {
          Serial.println("<CTRL>+C");
        }
        dispMain();
        break;
      case 'F':
        Serial.println("File");
        break;
      case 'D':
        Serial.println("Drive");
        break;
      case 'Q':
        Serial.println("Quit");
        menuEnable = false;
        break;
      default:
        Serial.println("Unknown.");
        break;
    }
    delay(200);
    digitalWrite(led, HIGH);
    delay(200);
    digitalWrite(led, LOW);
  }
  Serial.end();
}
実機上の実行結果

Arduino IDE(今回から2.0.4を使わせていただいております)からビルドし、ESP32C3 Xiao に書き込んで動作させ、Teraterm Proで接続した画面が以下に。UIupdated

一番上のMENU>とある行がメニュー行です。そこでCと一文字打てば、下の行で行入力ルーチンが動作。1行入力してEnterキーを押せば下の方に反転したInput Str: 入力した文字列、が出力されます。

だんだん雰囲気でてきた?先は長いな。

IoT何をいまさら(116) ESP32C3版Xiaoで車輪の再発明、行入力その2 へ戻る

IoT何をいまさら(118) ESP32C3版Xiaoで車輪の再発明、スクリーンエディタ? へ進む