初版公開 2005年12月25日
最終更新 2006年01月03日

Matrix Orbital

-- Linux で利用できるUSB VFDユニット --




サーバーとして稼働するPC は通常モニターはOFF で、ログインさへしてない状態で稼動させるものです。
そんなサーバーに20文字x2行程度の文字表示デバイスをつけ、 最小限の情報を常時表示させたいと希望するのは私だけではないと思います。

このページではデフォルト状態のLinux で認識可能なUSB VFDデバイスMatrix Orbital MX330を紹介します。

結論

Matrix Orbital MXシリーズはUSB で接続し、電源を供給すると 標準のLinuxでそのまま認識できます。
フリーで配布されているLCDproc でCPU負荷やメモリ,ディスク状況を表示することが できますし、自分で好きな文字列をecho "hoge hoge" > /dev/ttyUSB0 のように簡単に表示させることが可能です。



Matrix Orbital とは

Matrix Orbital (http://www.matrixorbital.com/) は、 インテリジェントなシリアルインタフェースを備え、容易に扱えるLCD ユニットを提供することで発展した米国のメーカーです。 LCD 表示器は複雑で組み込むのに時間のかかるデバイスでした。 Matrix Orbital のRS232C 接続のシンプルな製品を用いることで防衛・航空宇宙産業をはじめとする広い分野で、 エンジニア達はLCD に時間をかけず、システムが本来取り組むべき課題に集中することができました。

最近のMatrix Orbital ブランドの製品には、PC で使うことを配慮して5inch ベイに搭載できるようになったUSB インタフェース対応のものがあります。
表示デバイスの種類や表示色、表示文字の数、入力キーパッドの有無で多くの バリエーションがあります。

Palm デバイスにはMatrix Orbital のエミュレートをさせるフリーソフト PalmOrb (http://palmorb.sourceforge.net/)があります。 海外ではそれぐらい人気があるのでしょう。

モデル MX330

私が購入したのはMX330 という製品です。MX3 シリーズは表示デバイス VK202-24-USB を搭載していて、5x7 ピクセルの英数字を20文字x2行表示可能です。 表示はVFD(蛍光表示管) です。 液晶(LCD)ではないので、バックライトではなく自ら光を発します。

日本では秋葉原のUSER'S SIDE (http://www.users-side.co.jp/)が扱っています。 ただし、マニュアルなどは全て英語です。 私はUSER'S SIDE 店頭で2005年12月に20790円(税込)で購入しました。
(Matrix Orbitalのサイトを見ている と、直販では100 USドルぐらいのようです)

パッケージの中身はユニットの他に、ドライバやマニュアルPDF の入った8cm CD, 英語のセットアップ説明シート,Windows アプリケーションのインストールキーを 入手するための登録方法の説明シート(日本語)だけです。



USER'S SIDE によるMatrix Orbital シリーズの情報は http://www.users-side.co.jp/guide/press/MX345.phpからどうぞ。

MX シリーズは単なる文字表示デバイスではなく、オプションの温度センサーと ケースファンをアプリケーションで連携させ、温度に応じたファンの自動制御が 可能らしいです。基盤には確かにそのためのコネクターがあります。 ですが私はファンコントロールには関心がなく、全く利用していません。


Matrix Orbital MX シリーズによるファンコントロールについては USER'S SIDE のレビューページ (http://www.users-side.co.jp/guide/press/mx2_review2.php) が詳しいようです。

その他にも、外部に20キーのキーボードを接続可能であったり、 3つまでのLED をつけられるインタフェースがあり, それを点灯制御することができたりします。USB ケーブルはコネクタ経由がイヤなら ピンヘッダで接続することもできます。 USB ではなく、TTL レベルのシリアル接続のためのピンヘッダもあります。 とてもおもしろいデバイスです。

動作確認

セットアップして動かしてみようとしたところ、はまりました。 結論を言うと、 USB だけ接続してもダメで、ファンコントロールを利用しない場合でも、FDD 電源コネクタを用いて給電する必要がありました。 付属のセットアップガイドにもUSB ケーブルだけを接続するような説明がありますが、USB バスパワーではダメなのです。

(2006/01/03 追記)
ファンコントロールを12V モードから5V モードにジャンパ設定を変更しても 症状は変わらず、やはり外部電源供給が必要であることを確認しました。


このことに気づかなかったため、Linux に接続しても次のようにsyslog に延々とエラーが出ました。
Dec 24 00:17:23 knight kernel: usb.c: failed to set device 2 default configuration (error=-84)
Dec 24 00:17:24 knight kernel: usb.c: failed to set device 3 default configuration (error=-84)
Dec 24 00:17:25 knight kernel: usb.c: failed to set device 4 default configuration (error=-84)
Dec 24 00:17:27 knight kernel: usb.c: failed to set device 5 default configuration (error=-84)
Dec 24 00:17:28 knight kernel: usb.c: failed to set device 6 default configuration (error=-84)
Dec 24 00:17:29 knight kernel: usb.c: failed to set device 7 default configuration (error=-84)
Dec 24 00:17:30 knight kernel: usb.c: failed to set device 8 default configuration (error=-84)
Dec 24 00:17:32 knight kernel: usb.c: failed to set device 9 default configuration (error=-84)
                     :
                     :
                     :

Linux だからダメなのか、とWindowsXP SP2 のPC につなぎなおし、付属する8cm CD-ROM からドライバを入れようとしましたがインストールの最後にデバイスを起動できません でした、とエラーになりました。

あれやこれやと1日近くはまりましたが、「ひょっとして、」とファン用電源をつなぐ と勝手にタイトル文字が表示されました。


この状態でUSB ケーブルをLinux 機に接続しdmesg で確認すると次のように自動で認識されました。 利用しているカーネルは2.4.31 で、2005年夏ごろのSlackware-currentす。
usb.c: registered new driver serial
usbserial.c: USB Serial support registered for Generic
usbserial.c: USB Serial Driver core v1.4
usbserial.c: USB Serial support registered for FTDI SIO
usbserial.c: USB Serial support registered for FTDI 8U232AM Compatible
usbserial.c: USB Serial support registered for FTDI FT232BM Compatible
usbserial.c: FTDI FT232BM Compatible converter detected
usbserial.c: FTDI FT232BM Compatible converter now attached to ttyUSB0 (or usb/tts/0 for devfs)
usbserial.c: USB Serial support registered for USB-UIRT Infrared Tranceiver
usbserial.c: USB Serial support registered for Home-Electronics TIRA-1 IR Transceiver
ftdi_sio.c: v1.3.5:USB FTDI Serial Converters Driver

lsmod で確認すると、次のようにftdi_sio モジュールと、usbserial モジュールがロードされていることがわかります。
ftdi_sio               20632   0 (unused)
usbserial              20668   0 [ftdi_sio]

Matrix Orbital MX シリーズにはFTDI (Future Technology Devices International社: http://www.ftdichip.com/) のUSB シリアルコンバータチップが載っています。 Linux はこのチップのドライバをロードし、デバイスファイル/dev/ttyUSB0 で通信できるようにしてくれます。


Windows で利用する場合は様々な情報を表示するために、付属するLCDC というアプリケーションを利用します。 このアプリはアクティベーションが必要で、付属するコードで専用のWeb サイトに登録することでインストールキーを得るようになっています。
ところが、この登録サイトが正常に動作していないようで、画面遷移の2 ページ目でボタンを押してもいつまでたっても3ページ目が返ってきません。Windows で使う方は厳しい状況です。
LCDC はアクティベーションが必要ですが、Windows 用のMatrix Orbital のUSB ドライバはインストールキー等が不要です。ドライバが入ればフリーソフトである LCD smartie (http://lcdsmartie.sourceforge.net/)を用いて文字を表示することは可能です。


Linux で利用

Matrix Orbital を含み、様々な文字表示デバイスに情報を表示するフリーの パッケージとしてLCDproc (http://lcdproc.omnipotent.net/) があります。これを使うことで、CPU 負荷やメモリの利用状況などシステムのステータスを表示することができるようです。

LCDproc を利用せずとも、Matrix Orbital で簡単な表示を行えます。
自分で好きな情報を表示したい場合は、最初に一度だけ
stty -F /dev/ttyUSB0 19200 pass8 -cstopb
を実行し、USB シリアルラインを調整してから、
echo "hoge hoge" > /dev/ttyUSB0
とするだけで可能です。このように、デバイスファイルに文字コードを流し込むと カレントの座標にその文字が表示されます。
(なお、root 以外の一般ユーザがこういった操作を行うためには、/dev/ttyUSB0 の アクセスパーミッションを666 などに設定する必要があります)

2行のテキストファイル hoge.txt を準備しておいて、
cat /tmp/hoge.txt > /dev/ttyUSB0
というのもありです。

マニュアルには表示している全ての文字をクリアしたり、 カレント座標ではなく指定した位置に文字を出力するための エスケープコード等が解説されています。
例えば、Clear Display は10進で 254 88 とあります。 この254 88 はecho で出力できる8進数にすると376 130であることから、
\echo -en "\376\130" > /dev/ttyUSB0
でVFD をクリアできます。
echo の頭に'\'をつけているのは、tcsh 等のシェル組み込みのecho が-en 引数 を扱えないことを避け、/bin/echo などの外部のecho を利用するためです。 echo のオプションで-e は文字コードを8進で指定する 指示で、-n は末尾の改行コードを抑制し、せっかくVFD をクリアしたのに カレントポジションが2行目に移動することを抑制する効果があります。

表示位置を指定する場合は254 71[column][row]とあるので、8進数にすると376 107 ですから、
\echo -en "\376\107\1\2" > /dev/ttyUSB0
で次回からの出力開始位置が2行目の1カラム目になります。(座標は1からで、0からでは ないようです)

このような自分で好きな文字を表示する方法で、 注目している銘柄の株価や為替相場を常時表示したいと目論んでいます。
その第一歩として、C 言語で現在時刻をVFD表示するプログラムを書いてみました。 (Linux 用です)
/*************************************************************************
"VFD_disptime.c"                                              2006/01/03

Matrix Orbital MX330 に現在時刻を表示するプログラム
Ctrl-C で終了する

コンパイル方法
  cc -o VFD_disptime VFD_disptime.c

                             by S.K. (http://www.lares.dti.ne.jp/~seiki/)
*************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/param.h>

/*
 * 引数の指す領域に現在時刻を HH:MM:SS 形式で返す
 * VFD で左斜め前からでも見やすくするために、前に空白を2つ挿入している
 */
int get_time_str(char *buf)
{
    time_t t;
    struct tm *tmp = NULL;

    t = time(0);
    tmp = localtime(&t);
    sprintf(buf, "  %02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
    return 0;
}

int open_VFD()
{
    struct  termios termios;
    int fd;

    if( (fd = open("/dev/ttyUSB0", O_RDWR)) < 0 ){
        perror("open()");
        return -1;
    }

    if( ioctl(fd, TCGETS, &termios) < 0 ){
        perror("ioctl()");
        return -2;
    }

    termios.c_iflag &= ~ISTRIP;  // 8bit目のクリアをしないようにする 
    termios.c_cflag &= ~CSIZE;   // データを8bit にするために、まず設定クリア
    termios.c_cflag |= CS8;      // データ長を8bit に設定
    termios.c_cflag &= ~CSTOPB;
    termios.c_cflag &= ~PARENB;  // パリティを無効に
    cfsetispeed(&termios, B19200);
    if( ioctl(fd, TCSETS, &termios) < 0 ){
        perror("ioctl()");
        return -3;
    }
    return fd;
}

void close_VFD(int fd)
{
    if( fd > -1 )
        close(fd);
}

void clear_VFD(int fd)
{
    char cc[BUFSIZ];

    if( fd < 0 )
        return;
    sprintf(cc, "%c%c", 254, 88); // MX シリーズのVFDクリアシーケンス
    write(fd, cc, 2);
}

void slow_write(int fd, const char *str)
{
    const char *cp = NULL;
    for( cp = str; *cp != '\0'; cp++ ){
      write(fd, cp, 1);
      usleep(2000);  /* 2mm sec sleep */
    }
}

/*
 * 指定座標に指定文字列を表示する
 */
void print_VFD(int fd, int row, int column, const char *str)
{
    char cc[BUFSIZ];
    int len = 0;
    int ii;

    if( fd < 0 )
        return;

    if( row < 1 || row > 2 )
        row = 1;
    if( column < 1 || column > 20 )
        column = 1;

    /* 座標指定シーケンス */
    sprintf(cc, "%c%c%c%c", 254, 71, column, row);
    write(fd, cc, 4);

    strcpy(cc, str);
    len = strlen(str);
    if( len < 20 ){
      for( ii = len; ii < 20; ii++ ) /* 末尾を空白で埋める */
            cc[ii] = ' ';
      cc[20] = '\0';
    }
    slow_write(fd, cc);
}

/*
 * SIGINT 受信時の終了処理を定義する関数
 * (VFD の表示をクリアするだけ)
 */
void module_exit()
{
    int fd = 0;

    fd = open_VFD();
    clear_VFD(fd);
    close_VFD(fd);
    exit(0);
}

/*
 * 時刻表示の主処理
 */
int work_module()
{
    char time_str[BUFSIZ];
    char old_time[BUFSIZ];
    int fd = 0;

    signal(SIGINT, module_exit);

    fd = open_VFD();
    clear_VFD(fd);
    close_VFD(fd);

    old_time[0] = '\0';
    while(1){
      /* 時刻の表示 */
      get_time_str(time_str);
      if( strcmp(time_str, old_time) != 0 ){
        strcpy(old_time, time_str);
        fd = open_VFD();
        print_VFD(fd, 1, 1, time_str);
        close_VFD(fd);
      }
      usleep(200000);
    }
    exit(0);
}

int main(int argc, const char *argv[])
{
    work_module();
    exit(0);
}



他への使い回しを考慮して、open_VFD(), close_VFD(), clear_VFD(), print_VFD() という関数を切ってあります。

これを発展させて、実行後デーモンとしてバックグランドに常駐する改良版も 作成してみました。 引数なしで普通に実行するとデーモンとして開始します。実行のときに& をつける必要はありません。終了させたい場合は killall -TERM VFD_disptime_daemon でOK です。デーモン版ソースコードはこちら からどうぞ。

組込みの様子

サーバとして利用しているLinux 機は 小型のmini-ITX なので5inch ベイがありません。 Matrix Orbital MX330 は、CD-ROM ドライブ等を内蔵できる外付けUSB BOX (novac 2台はい〜る KIT USB)に入れてみました。 AC アダプタが2つになるのはイヤなので、電源はPC 本体から外に出しました。
USB バスパワーだけで動けばうれしいんですが...
写真では電源コネクタを2つ外出しにしてますが、使用してるのは片方だけです。




以上



メインメニュー へ戻る