2012年8月19日日曜日

キーボード G510 のLCDアプレット作成

エアコンのリモコンとか作った そのに。

Arduinoが検知した温度をどこに表示させようかと考えて思い立ったのが、Logicool Gaming Keyboard G510 のLCD。

軽く調べてみて分かったことは
  • Logitech/Logicool の G-Series と呼ばれる機器の幾つかはLCD(と幾つかのボタンのセット)を搭載しており、このLCDはWindows上で動くAppletと呼ばれるソフトウェアから制御する。
  • Applet は G-Series のユーティリティソフトウェアから制御する。
  • G-Series のユーティリティソフトウェアのインストール先にある LCDSDK フォルダ内に、AppletのSDKがある

なんだかいい感じです。

Applet開発はどうやるのか


取り敢えずSDKを覗いてみると、プログラミングガイドライン、APIリファレンス、サンプルコードなど情報は揃っていました。開発言語はC/C++, ビルド環境は Visual Studio でいいみたい。

SDKは大きく二つに分かれています。
  • LCDSDK
    • テキスト表示、ビットマップ表示、プログレスバーなど、便利なライブラリが用意されたセット
  • LCDSDKCore
    • LCDとの接続、LCD横のボタン操作イベント、LCDのドット単位操作など、必要最小限のAPIセット

ぱっと見で単純そうだったので LCDSDKCore の方を使うことにしました。

SDKに含まれるサンプル ColorAndMono を参考にします。
このサンプルは、接続されているLCDがカラーかモノクロかを判断して、ランダムなノイズのような画像をLCDに表示するだけのものですが、LCDの接続/切断、LCD横についているボタンのイベント、その他各種イベントについても一通り書かれているので参考にするにはうってつけ。
ソースを見れば分かりますが、WndProcに処理を実装していくタイプの標準的なWin32アプリケーションに、LCD用の初期化処理とイベント処理が追加されたようなものです。

まず、私が持っている G-Series 機器は G510 だけなので、カラー判別処理とカラーLCD用の処理はバッサリ無視。
G510のLCDは、横160 × 縦43 ドット、単色です。


LCDの表示更新処理には lgLcdBitmap160x43x1 というスゴい名前の構造体を使います。
メンバは2つ。
  • hdr … ビットマップのフォーマットを指定。
  • pixels … 表示させる画像データ。unsigned char の配列。
pixels の各Byteがドットを表し、先頭ビットが 1 だと対応するLCDのドットが明るくなる、といった感じ。lgLcdUpdateBitmap 関数に hdr のアドレスを渡すと、ヘッダの後ろに続く pixels が読まれてLCDに反映されるみたいです。

ここまでわかればAppletを作る最低限の情報は揃っています。

製作

こんなアプリケーションを作ることにしました。 

  • G-Seriesのユーティリティソフト起動時に自動起動
  • 1秒ごとにLCDを更新する (WM_TIMER 使用)
  • USBシリアルポートへArduinoが送り続けてくる温度情報を読み取る
  • 温度情報をLCDに表示
  • 日時も表示
  • 余ったスペースに何か絵を表示できればいいな


LCDのための定型処理は ColorAndMono を参考に必要な処理を実装。
ColorAndMonoサンプルのソースを読めば分かることばかりなのでここでは割愛。

絵はモノクロ2値ビットマップを読み込みます。

文字描画関数なんてものは用意されていないのでそこは何とかする必要があります。
素直に LCDSDK を使うか、別途DIBに描いてコピーするか、自前でビットマップフォントを作るか、まぁどれでもいけると思います。
今回は絵の表示処理が使い回せるのでビットマップフォントを作りました。
日時と温度を表示するだけなので数字+αがあればいいのです。


ちょっとハマったところ その1

Windowsでは fopen("COM3", "r") とかやってもシリアルポートからの入力を読めないみたいです。
この制限を書いた正式なドキュメントは見つからなかったのだけれど、とりあえずダメなものはダメなのだと諦めて Win32 API から読むことにしました。

HANDLE hCom = CreateFile(
    port,            // シリアルポート。"COM3" みたいな文字列
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL
);

if (hCom == INVALID_HANDLE_VALUE) {
    // このへんでエラー処理
    return -1;
}

DCB dcb;
GetCommState(hCom, &dcb);

dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;

SetCommState(hCom, &dcb);

// あとは ReadFile でごにょごにょ。

ちょっとハマったところ その2

2値ビットマップのピクセルデータは 1bit = 1dot なのですが、横ライン1行が 32bit 境界で揃っています。

こちらの説明が分かりやすかったです。
BMP ファイルフォーマット

MSDNには 16bit 境界みたいに書いてあるけど違うと思うの。。。

こんなの出来ました

LCDでネギを振るミクさん

LCDでネギを振るミクさん

0 件のコメント:

コメントを投稿