以下のようにすれば動くことが確認できた。

select や poll を明示的に呼び出すことができないが、edge を設定しさえすれば fs.watch でイベントを受けとれるようだ。

また、seek() もなぜか存在してないが read() の引数に position というのがあり、これが seek() 相当になるようだ。

const fs_ = require('fs');
const fs = fs_.promises;
fs.watch = fs_.watch;

async function watchInterrupt(pin, func) {
	try {
		await fs.writeFile(`/sys/class/gpio/export`, `${pin}`);
	} catch (e) {
		if (e.code === 'EBUSY') {
			// ignore
		} else {
			throw e;
		}
	}
	await fs.writeFile(`/sys/class/gpio/gpio${pin}/direction`, 'in');
	await fs.writeFile(`/sys/class/gpio/gpio${pin}/edge`, 'both'); // falling raising both
	const fh = await fs.open(`/sys/class/gpio/gpio${pin}/value`, 'r');
	const buf = new Uint8Array(1);
	fh.read(buf, 0, 1, 0);
	const watcher = fs.watch(`/sys/class/gpio/gpio${pin}/value`, {}, (eventType, filename) => {
		if (eventType === "change") {
			fh.read(buf, 0, 1, 0);
			const val = buf[0]-48;
			func(val, pin);
		} else {
			// XXX
			console.log(`unchecked event "${eventType}" occured with "${filename}"`);
		}
	});
	return {
		close: async () => {
			watcher.close();
			fh.close();
			await fs.writeFile(`/sys/class/gpio/gpio${pin}/edge`, 'none');
		}
	};
}

(async () => {
        watchInterrupt(0, (val, pin) => {
                console.log(`gpio${pin} changed -> ${val}`);
        });
        watchInterrupt(2, (val, pin) => {
                console.log(`gpio${pin} changed -> ${val}`);
        });
        watchInterrupt(3, (val, pin) => {
                console.log(`gpio${pin} changed -> ${val}`);
        });
})();
  1. トップ
  2. tech
  3. Node.js の fs モジュールだけで GPIO の割込みを扱うには?

やってること

  • prometheus の API を叩く
  • 表示する

だけ。 表示部分はせっかく python の骨格があるので基本的にはそのまま python で書いてる。けど Pillow (python の画像処理ライブラリ) はそれほど高機能というわけではなくて、文字表示時に細かいオプションが一切指定できなかったりするので Ruby と Cairo とか慣れた他の方法でやったほうがよかったかも。

フォントは Noto Sans CJK の Black。Pillow が OTF をちゃんと読めてよかった。

表示はフルリフレッシュを避けるように数値部分だけを部分書き換えしている。これにより表示更新が早くなるのと、フルリフレッシュ時みたいな全体が反転するチラツキがなくなるので常時していても邪魔くさく感じにくいはず。

部分書き換え (partial update)

↑ 試している様子 (値はランダム)。起動時はフルリフレッシュしてその後は常に部分書き換え

Ben Krasnow: Fast partial refresh on 4.2" E-paper display from Waveshare / Good Display の記事を参考に LUT をコピペしたらできたが LUT が何をしてるのかさっぱりわからない。

E-paper display の覚え書き (LUT ってなんだ) | tech - 氾濫原 LUT について追記

コード

https://github.com/cho45/electronic_badge_2018/blob/partialupdate/sketch/sketch.py

lib/epd4in2.py とかにもかなり変更を入れてる。LUT まわりが↑のエントリの epd4in2.cpp からのコピペなのでライセンス的に微妙。

  1. トップ
  2. tech
  3. builderscon の電子名札に家庭のメトリクス表示

Waveshare 4.2 Black/White EPD

全体的な動作の説明がスペックに書いてないので正確にわかることがあんまりない。

What is LUT?

Look-up Table。EPD 書き換えのときに、各フェーズでどのようにしてピクセルに電圧をかけるかを決めると思われる。

W2W / B2W / W2B / B2B LUT

6バイトでワンセット、7回繰替えされる。42バイト(コマンドバイトを除く)。各状態遷移のときにパネルにどういう電圧のかけかたをするかを規定する。

以下のようなフルリフレッシュ用 LUT だと

    lut_wb = [
        0x80, 0x17, 0x00, 0x00, 0x00, 0x02,
        0x90, 0x17, 0x17, 0x00, 0x00, 0x02,
        0x80, 0x0A, 0x01, 0x00, 0x00, 0x01,
        0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ]

以下のような構造になる。

LUT_WB
lv: VDL GND GND GND
fr:  23   0   0   0 repeat: 2
lv: VDL VDH GND GND
fr:  23  23   0   0 repeat: 2
lv: VDL GND GND GND
fr:  10   1   0   0 repeat: 1
lv: VDH VDH GND GND
fr:  14  14   0   0 repeat: 2
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0

たぶん level select で指定した電圧を frame 数ぶんかける、そしてそれを repeat 数回行う、ということだと思う。違うかもしれない。VDH をかけると黒に、VDL にすると白になる。

VCOM LUT

VCOM とは? 名前からするに各ピクセルの共通側電圧の設定。他の LUT とあわせて共通側電圧をどうするかを設定すると思われる。基本的に他の LUT の frame、repeat 数とあわせて全状態で GND にしとけばいいっぽい。

6バイトでワンセット、7回繰替えされる。最後に2バイトつくので44バイト(コマンドバイトを除く)

VCOM
lv: GND GND GND GND
fr:  23   0   0   0 repeat: 2
lv: GND GND GND GND
fr:  23  23   0   0 repeat: 2
lv: GND GND GND GND
fr:  10   1   0   0 repeat: 1
lv: GND GND GND GND
fr:  14  14   0   0 repeat: 2
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0
lv: GND GND GND GND
fr:   0   0   0   0 repeat: 0

DTM1 DTM2

B/W (白黒) モードの場合、DTM1 は "OLD" data を SRAM に転送する。DTM2 は "NEW" data を SRAM に転送する。

よくわからない。どっちも転送しておかないと、電圧かけたときに古いデータがでてきたりして謎。

What is OTP?

仕様書のところどころで出てくる OTP って何?という話。

One Time Programming 1度だけ書きこみ可能なこと。E-paper の場合 LUT を OTP して固定することができる、ということらしい。とりあえず無視して良い。

  1. トップ
  2. tech
  3. E-paper display の覚え書き (LUT ってなんだ)