2018年03月13日

Inky pHATのための画像変換ツールを作ってみた

inkyconvertertest.jpg

先日届いたpimoroni Inky pHATですが、記事にも書いたようにPythonライブラリで扱える画像ファイルは、ちょっと特殊な形式をしています。

そこで、普通のJPEG画像等から、Inky pHAT用のPNGファイルを生成するためのツールを作成してみました。

boochow/InkyConverter

バイナリファイルは以下に置いておきます。(画像のファイル名が日本語だと開けないようです。)

InkyConverter.zip

このツールですが、とりあえず白黒だけの画像なら、以前openFrameworksを使ってArduboy用に作った画像コンバータにちょっと手を加えて画像のピクセル数を変更すれば、Inky pHAT用に使えそうだと思い、作り始めました。

さらに、元画像から赤色の領域だけを切り出せれば、それをディザリングするのも白黒画像と同じやり方でできます。
単純にRGBからRチャネルだけを取り出すと、白色でも赤色領域になってしまいます。
今回は、GやBよりもRの値がある程度大きく、かつRの値そのものがそこそこ大きい、というピクセルを赤色のピクセルと解釈する・・・という比較的単純な方法で実装してみましたが、結構イメージに近い処理が行えました。

inkyconverter.png


白黒赤の画像ができたら、これをパレット形式のPNGファイルにします。パレットは先頭から白、黒、赤の順でなければなりません。このような処理はopenFrameworksには備わっていません。

しかし、調べてみるとopenFrameworksは画像の処理についてはFreeImageというライブラリが使われており、このライブラリの機能を直接使えばパレットの制御もできそうなことが分かりました。
そこで、PNGファイルの保存の部分はFreeImageのマニュアルを調べながら作りました。

簡単にInky pHAT用の画像が作れますので、お持ちの方はぜひお試しください。
posted by boochow at 21:22| Comment(0) | Raspberry Pi | このブログの読者になる | 更新情報をチェックする

2018年03月11日

電子ペーパーディスプレイInky pHATを買ってみた

inkyphat2.jpg

pimoroniから販売されているRaspberry Pi用のe-inkディスプレイ「Inky pHAT」に以前から興味があったので、買ってみました。

解像度は212×104ピクセルで、白黒赤の3色表示です。ちょっと珍しいですね。
表示面積もRaspberry Pi Zeroが隠れるほど大きいです。
また、電子ペーパーなので、書き込んだ後は電源を切っても画像は表示されたままです。
価格はDigi-Keyで2683円でした。安いとは言えない、微妙な価格ではあります。
ソフトウェアはRaspberry Pi用のPythonライブラリが提供されています。

Getting Started with Inky pHAT - Pimoroni Yarr-niversity

このライブラリで、文字の描画や画像の表示などが、PILを援用して行えるようです。
ただ、書き換えはかなり遅く、以下のビデオのような感じで、25秒くらいかかります。
演出として点滅させているわけではなさそうで、何度も描画することによって高い濃度で表示されるようになるのではないかと思われます。



画像を表示させる場合は、ファイル形式が結構面倒です。

・パレット形式、8bitインデックスカラーのPNGファイル
・パレットは「白」「黒」「赤」の順であること

という条件を満たさなくてはなりません。
パレットの色の並び順まで指定されると、なかなか面倒です。
通常、パレットの並び順は画像ソフトが勝手に決めてしまいます。パレット番号を後から変更する場合、画像を復号してピクセルの値も変更しなければならなくなりますので、パレットの並び順を自由に変更できる画像ソフトは見かけません。

マニュアルでは、GIMPを使って画像を生成してくださいということで、GIMP用のパレット定義ファイルが上記のリンクから提供されています。

私はGIMPを使わずに、以下のような方法を使いました。

1)irfanview+PNGOUTプラグインで、上記の指定どおりのパレットを作成してexportする(メニューのImage→Palette)。

2)paint.netで上記の3色を使った画像を作成し、8bit PNGとして保存する。(3色以上使ってもかまいませんが、次のステップで強制的に3色化されます。)

3)保存したファイルをirfanviewで開き、先ほど保存したパレットをimportする。

作成した画像ファイルです。

miku-inky-phat.png


irfanview用のパレットファイルも置いておきます。

inkyphat.pal

さて、このe-inkディスプレイですが、ちょっと動かしてみた感想としては、OLEDやLCDなどの代替にはなりませんが、電源を切っても絵が消えないのは面白いです。
例えば天気予報のように、定期的に更新されるだけの情報であれば、定期的にCPUを起動して情報をこのディスプレイに書き込み、処理が終わったらCPUはシャットダウンすることが可能です。
このような使い方を考えると、Raspberry Piよりも組み込み系のCPUと組み合わせるほうが相性は良いかもしれないと思います。

あと、上の画像ファイルと写真を見比べると分かると思いますが、アスペクト比が微妙に横長です。
まあこの程度は許容範囲でしょう。

ちなみにこのディスプレイモジュールの製造元はこちらのようです。型番はGDEW0213Z16となっています。

2.13 inch three-color low-power electronic paper screen GDEW0213Z16,Color EPD,E Paper Display

コントローラはIL0373というもののようです。サンプルソースはSTM32F10x用のものがダウンロードできます。

白黒赤以外に、白黒黄のディスプレイモジュールもあって、waveshareの以下のページを見ると結構いろいろな製品が出ているようです。

e-Paper - OLEDs / LCDs - Modules / Expansions

posted by boochow at 01:17| Comment(0) | Raspberry Pi | このブログの読者になる | 更新情報をチェックする

2018年03月10日

Raspberry Pi bare metal MicroPython+USBホストコントローラのテスト

usb-mp-repl.png

前回、あれこれと考えたMicroPythonへのUSBキーボード接続ですが、とりあえず簡単にできそうなREPL専用の実装をやってみることにしました。

基本的には、REPLのためにシリアルポートから1文字ずつ読む関数mp_hal_stdin_rx_chr()が、USBキーボードで入力された文字を返せればOKです。
その前にまず、csudライブラリがMicroPythonの上で動作するよう環境を整える必要があります。

csudライブラリはビルドオプションとしてSTANDALONE、LOWLEVEL、DRIVERの3つが用意されています。
今回はメモリ管理をMicroPython側で行いますので、DRIVERでビルドします。

csudをDRIVERとして動作させる場合は、include/platform/platform.hで定義されている関数を環境側で用意しなければなりません。
MicroPythonにはハードウェアを抽象化するレイヤ(mphal)がありますので、その中から適切な関数を選ぶことになります。

注意が必要なのはメモリの確保とGCです。
MicroPythonのドキュメントには、メモリ確保にはpy/misc.hで定義されている「m_new, m_renew, m_del (and friends)」を使え、と書かれていますが、これで確保したメモリはGCの対象になります。
GCでクリアされないためには、確保したメモリブロックがMicroPythonのrootポインタから辿れる必要があります。
このあたりのことはフォーラムの以下のスレッドで書かれていました。

help is appreciated - memory allocated by m_malloc() is automatically free'ed - MicroPython Forum

お作法としては

・GCからメモリを保護したいポインタは、普通に変数を宣言するのではなく、mpconfigport.hの中のMICROPY_PORT_ROOT_POINTERSへ変数の宣言を追加する。
・保護したいポインタを参照する場合、常にMP_STATE_PORT()マクロで囲う。

となります。

csudの変数でstaticになっていてAllocateMemory()でメモリを確保しているものは、「Core、Host、Power、Devices」の4つのようなので、これらの変数へのポインタからなる構造体を定義し、それをroot pointersへ追加しました。

他に、USBキーコードから文字コードへの変換の処理も必要になります。
今回はとりあえず、キー配列はUSキーボードに決め打ちでテーブルを参照するようにしました。

これで、どうやらUSBキーボードから入力できるMicroPythonができました。
(まだ安定しておらず、放置しているとハングアップしてしまいますが・・・)
ラベル:MicroPython USB
posted by boochow at 19:13| Comment(0) | Raspberry Pi | このブログの読者になる | 更新情報をチェックする

2018年03月08日

MicroPythonでのUSBキーボード入力の調査

ここ1ヶ月ほど、Raspberry PiベアメタルMicroPythonにUSBキーボード入力を追加してみたいと思っていろいろ調べています。

用途として、
(1)USBキーボードを単純にREPLのための入力手段としてのみ使う
(2)モジュールを用意してMicroPythonのスクリプトから汎用的に使えるようにする
の2つが考えられ、当然のことながら後者のほうが応用が利きますが、APIを設計する必要があります。

考えてみると、そもそも私はこれまでMicroPythonでUSBを使ったことがありませんでした。
そこでまずはUSBデバイスモードでの利用方法を調べたのが前回の記事でした。

一方、USBホストモードについてフォーラムで情報を探してみたところ、実験的な実装がSTM32にはあることが分かりました。
元来MCU向けであるMicroPythonのUSB機能は、もっぱらUSBデバイスを作成するための機能が整備されており、USBホストはプライオリティが低いようです。

USB host mode ・ Issue #212 ・ micropython/micropython

ちょっといじってみましたが、STMHALの最新ライブラリに追随していないのか、リンクエラーになってしまってUSBホストモードを有効にした状態でビルドすることはできませんでした。

ですので、どのような実装になっているのか、ports/stm32配下のソースコードを調べてみた結果をメモしておきます。

●条件付きコンパイル
関連しそうなスイッチは以下の通りです。

mphalport.c:#ifdef USE_HOST_MODE
usb.c:#if defined(USE_HOST_MODE)
Makefile:#USBHOST_DIR=usbhost
boards/XXX/mpconfigboard.h:#define MICROPY_HW_ENABLE_USB (1)


●REPLでのUSBキーボード入力
以下のように、コードはありますが無効化されています。
int mp_hal_stdin_rx_chr(void) {
for (;;) {
#if 0
#ifdef USE_HOST_MODE
pyb_usb_host_process();
int c = pyb_usb_host_get_keyboard();
if (c != 0) {
return c;
}
#endif
#endif

●USBホストモードへの変更
pyb.usb_mode()の実装(usb.c)で以下のように処理されています。
#if defined(USE_HOST_MODE)
if (strcmp(mode_str, "host") == 0) {
pyb_usb_host_init();
}


●USBホストモードの実装
usb.cの中に
// code for experimental USB OTG support                                        

#ifdef USE_HOST_MODE

で始まる部分があり、
void pyb_usb_host_init(void);
void pyb_usb_host_process(void);
uint pyb_usb_host_get_keyboard(void);

の3つの関数が定義されていますが、実際にはpyb_usb_host_get_keyboardはTODO扱いになっています。

というわけで、現状の仮の実装としては

・pyb.usb_mode("host") を実行するとUSBホストモードが有効に(pyb_usb_host_init()が呼ばれる)
・以降は、REPLループの中でpyb_usb_host_process()とpyb_usb_host_get_keyboard()が呼ばれる

となっています。
usb.cの中に書かれているコメントに、MicroPythonにおけるUSBの実装方針があったので引用しておきます。
// MicroPython bindings for USB

/*
Philosophy of USB driver and Python API: pyb.usb_mode(...) configures the USB
on the board. The USB itself is not an entity, rather the interfaces are, and
can be accessed by creating objects, such as pyb.USB_VCP() and pyb.USB_HID().
We have:
pyb.usb_mode() # return the current usb mode
pyb.usb_mode(None) # disable USB
pyb.usb_mode('VCP') # enable with VCP interface
pyb.usb_mode('VCP+MSC') # enable with VCP and MSC interfaces
pyb.usb_mode('VCP+HID') # enable with VCP and HID, defaulting to mouse protocol
pyb.usb_mode('VCP+HID', vid=0xf055, pid=0x9800) # specify VID and PID
pyb.usb_mode('VCP+HID', hid=pyb.hid_mouse)
pyb.usb_mode('VCP+HID', hid=pyb.hid_keyboard)
pyb.usb_mode('VCP+HID', pid=0x1234, hid=(subclass, protocol, max_packet_len, polling_interval, report_desc))
vcp = pyb.USB_VCP() # get the VCP device for read/write
hid = pyb.USB_HID() # get the HID device for write/poll
Possible extensions:
pyb.usb_mode('host', ...)
pyb.usb_mode('OTG', ...)
pyb.usb_mode(..., port=2) # for second USB port
*/


USBホストモードのAPIとしてはusb_mode('host')が唯一のものであり、接続されたUSBデバイスを使うためのAPIはありません。
ですので、APIは独自に設計せざるを得ない感じですが、USB_HID()などの既存のUSBデバイス向けAPIと衝突するのは望ましくないので、例えば

(1)usb_mode() は共通。現在のモードを返せるようにしておく(pyb.usb_mode()を呼ぶと'host'が返る)
(2)USB_HCD()でホストコントローラのオブジェクトを返す
(3)ホストコントローラオブジェクト経由でデバイスとの通信を行う

みたいな感じかなあと思います。

各種デバイスクラスをどこまでCで実装するかは考えどころですが、Low SpeedのHIDの場合はMicroPythonで様々なデバイスを実装できるほうが、応用が利く気がします。
MicroPythonで実装した場合に、マウスのように比較的更新速度が高いデバイスで速度が追いつくかどうかは気になりますが、これは実験してみないとなんとも言えないですね。
ラベル:MicroPython USB
posted by boochow at 20:00| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2018年03月06日

MicroPythonでUSBマウス

pybmouse.gif

MicroPythonの動作しているSTM32のボードをUSBマウスとして動作させ、PCに接続してみました。
ボードは以前も使ったNUCLEO-F767ZIです。
これは以下のページのサンプルをそのまま使ったもので、上のように、MicroPythonのコードでPCのマウスポインタを動かすことができます。
(画像はLICEcapでキャプチャしました。)

8. Making the pyboard act as a USB mouse − MicroPython 1.9.3 documentation

USBデバイスとして動作するUSBポートは、プログラムを書き込むST-LINKではなく、ボードの反対側についているUSB端子のほうです。
PCへの接続は、以下のようにマウス用とプログラミング用の2本のUSBケーブルが必要になります。

pyb-usb-test.jpg


マウス用のUSBポートは、MicroPythonを動かしている場合、初期状態ではPCからはUSBマスストレージとして認識されます。
このストレージでアクセスできるのは、REPLからosモジュールでアクセスできるものと同じファイルシステムです。
以下のように「PYBFLASH」というドライブ名で見えています。

pybcdc.png


先に、このストレージにある「pybcdc.inf」をPCにコピーしておいてください。
マウスのデバイスドライバをインストールする際に必要になります。
次のステップ以降では、このストレージへのアクセスができなくなります。

コピーしたら、ストレージのboot.pyをメモ帳などで開いて修正します。
コメントアウトされている
#pyb.usb_mode('VCP+HID') # act as a serial device and a mouse

の先頭の#を削除して、この行を有効にして保存します。
これでリセットすると、MicroPythonはマスストレージではなくHIDデバイスとして起動します。

初回起動時はWindowsからマウスとして認識されないので、さきほどコピーしたpybcdc.infを使ってデバイスをインストールする必要があります。

以下のようなコードを入れて、osc(200,20)等と入力すると、マウスカーソルが水平方向に動きます。


USBマスストレージとしてアクセスできるように戻したい場合は、ST-LINK側からboot.pyをuPyCraftなどを使って書き換えてください。
ラベル:USB MicroPython
posted by boochow at 23:01| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする
人気記事