5年ごとの更新日がきたので旧免許状を返納した。新免許状は4月ぐらいに届いてたのでやることは返納のみ。しかし返納先がいつもいつも謎。
新免許に返納先の案内を入れてくれればいいのに。
もしかして返納しなくてもいいんだっけ??ってぐらいの雑さだけど、30日以内に返納しないと30万円以下の過料が課される。なんやねん……
5年ごとの更新日がきたので旧免許状を返納した。新免許状は4月ぐらいに届いてたのでやることは返納のみ。しかし返納先がいつもいつも謎。
新免許に返納先の案内を入れてくれればいいのに。
もしかして返納しなくてもいいんだっけ??ってぐらいの雑さだけど、30日以内に返納しないと30万円以下の過料が課される。なんやねん……
↑ に一応資料をあげときます。めっちゃ酔っぱらってこのエントリを書いております。
mbed 環境は Arduino の次あるいはプログラマなら最初の一歩として本当にお勧めです。若干価格が高いのが玉に傷ではありますが、LPC11U35 Quickstart Board は比較的導入しやすく、mbed 入門・USB デバイス入門に最高だと思っています。ぜひ mbed 環境を試してみてほしいと思います。
もともとキーボードに使っていた BLE Nano も mbed です。これも BLE デバイスとしては非常に安価な部類だと思います。僕は今のところ挫折していますが、ぜひ安定したBLEデバイスを作ってノウハウを教えてください……
builderscon は本当に良くて、今回はスピーカーディナー(スピーカーといろんな人同士の交流)という新しい試みも大変楽しかったです。前夜祭はやっぱりオフレコの闇の話が最高に楽しいですね。
builderscon はリアルイベントなのにインターネットの夜明けのような希望があります。こういうイベントは本当に貴重で、運営のかたには頭が上がりません。まだ builderscon はこれからですが楽しみたい!!
まず BME280 と BMP280 というよく似たやつがあるので注意…。 BMP は気圧と気温しか測れない。
リセット直後の BME280 は圧力・温度・湿度すべての測定がスキップかつスリープモードになっているので、まず測定開始するように設定する必要がある。いくつかモードがあるが、とりあえず NORMAL モードにして定期的に値を更新するようにしとけば良い。
なお用途に応じて最適と思われる設定がデータシートで示されているので、それに従えば良い。
FORCED モード (ワンショットモード) で測定する場合、9. Appendix B: Measurement time and current calculation を参照して tmeasure を求める必要がある。FORCED モードは MODE に書きこむと測定が始まり、終わると SLEEP モードになるという挙動をする。
ググるとほかにも実装が出てくるが、まずはリファレンス実装で試すのが筋。
https://github.com/BoschSensortec/BME280_driver
メーカーがリファレンス実装をつくってる。
$ git clone git@github.com:BoschSensortec/BME280_driver.git $ cd BME280_driver $ gcc -DBME280_FLOAT_ENABLE -I. -o foo examples/linux_userspace.c bme280.c $ ./foo /dev/i2c-1 Temperature, Pressure, Humidity temp 31.10, p 100605.10, hum 48.97 temp 31.10, p 100605.08, hum 49.01 temp 31.11, p 100605.11, hum 48.97 temp 31.12, p 100605.34, hum 48.98 temp 31.13, p 100605.46, hum 49.02
https://github.com/cho45/ruby-i2c-devices/blob/master/lib/i2c/device/bme280.rb
に書いた。calibration データの扱いに地味にハマってなかなかうまくいかなかった。
Rock64 http://akizukidenshi.com/catalog/g/gM-12382/ というのを買ってみた。
とりあえず minimal Ubuntu をいれた。
ssh は最初から有効。rock64:rock64 で入れる。
sudo /usr/local/sbin/rock64_diagnostics.sh -m
便利ツールが最初からある。
そこそこ大きめのヒートシンクをつけてもCPUにフルロードかけると78℃近くまでいく。たぶんヒートシンク必須。だけどCPUクロックに制限かかるまではいかない。というかクロック制限はあるのだろうか?
ちなみにアイドル時にはクロックダウンする。
GPIO2_A2/IR_RX/POWERSTATE2_u M21 に IR Receiver が繋っているが使いかたがよくわからない。RK3328 自体で NEC フォーマットの IR コマンドをサポートしているみたいだが Linux から利用するドライバがよくわからない。
まぁ GPIO 機能もあるので、こっちで読み出しをしてみることにしてみた。
GPIO2_A2 (2_A2 の部分を変換して pin 番号を求める) は 2 * 32 + ("A".charCodeAt(0)-65) * 8 + 2 = 66
root@rock64:/home/rock64# echo 66 > /sys/class/gpio/export
試しに IRKit に使えるフォーマットで受信データを表示するようにしてみた。IRKit は 2MHz sample のカウント数で表示されるので変換している。
//#!/usr/bin/env go run package main import ( "fmt" "time" "github.com/brian-armstrong/gpio" ) type Event struct { th int64 tl int64 } func main() { watcher := gpio.NewWatcher() watcher.AddPin(66) defer watcher.Close() events := make(chan *Event) go func() { prev := time.Now() var th int64 = 0 var tl int64 = 0 for { now := time.Now() delta := now.Sub(prev).Nanoseconds() prev = now _, value := watcher.Watch() switch value { case 1: tl = delta events <- &Event{th, tl} case 0: th = delta default: } } }() for { e := <-events fmt.Printf("%d %d\n", e.th/5.0e2, e.tl/5.0e2) } }
IR 受信にはsys/gpio だと速度がちょっと足りないですね。
さくらのVPSはIPv4/IPv6デュアルスタックで、双方ともにグローバルアドレスがついているため、IPv4 しかない環境から IPv6 アドレスがついたホストへ SSH する際の踏み台に使うことができる。
Host raspberrypi User pi Hostname 2001:db8::40c0 ProxyCommand ssh user@vpshost -W '[%h]:%p'
自宅のIPv6環境でアクセスする場合は、ルーターなどのファイアーウォールの設定が必要。家庭用ルーターはIPv6でも無防備にならないように、デフォルトでほとんどのインカミングパケットを破棄するので、特定ホスト・ポートへのアクセスを許可する設定が別途必要となる。
mbed USBDevice ライブラリの中に USB CDC で動く USBSerial クラスが実装されている。これを Web USB から扱ってみる。これができるとブラウザ上から直接デバイスの設定などを行えるので便利になる。
LPC11U35 でも動かすことができる。
以下のような interface になっている。
CDC class (interface count = 2) interface 0 (endpoint count = 1) CDC Header Functional Descriptor Call Management Functional Descriptor Abstract Control Management Functional Descriptor Union Functional Descriptor Endpoint Descriptor INTERRUPT IN (interval = 16) interface 1 (endpoint count = 2) Endpoint Descriptor BULK IN (endpoint num = 2) Endpoint Descriptor BULK OUT (endpoint num = 2)
シリアル通信まわりだけだと以下のようになる
#include "mbed.h"
#include "CircBuffer.h"
#include "stdint.h"
#include "USBCDC.h"
class MyUSBSerial: public USBCDC {
private:
Callback<void()> rx;
CircBuffer<uint8_t,128> buf;
public:
MyUSBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, bool connect_blocking = true):
USBCDC(vendor_id, product_id, product_release, connect_blocking){
};
bool writeBlock(void* b, uint16_t size) {
uint8_t* buf = reinterpret_cast<uint8_t*>(b);
if (!send(buf, size)) {
return false;
}
return true;
}
uint8_t available() {
return buf.available();
}
void read(uint16_t len, uint8_t out[]) {
while (available() < len);
for (uint16_t i = 0; i < len; i++) {
buf.dequeue(&out[i]);
}
}
uint8_t read() {
uint8_t c = 0;
while (available() < 1);
buf.dequeue(&c);
return c;
}
bool connected() const {
return terminal_connected;
}
protected:
virtual bool EPBULK_OUT_callback() {
uint8_t c[MAX_PACKET_SIZE_EPBULK+1];
uint32_t size = 0;
//we read the packet received and put it on the circular buffer
readEP(c, &size);
for (uint32_t i = 0; i < size; i++) {
buf.queue(c[i]);
}
//call a potential handlenr
if (rx)
rx.call();
return true;
}
virtual void lineCodingChanged(int baud, int bits, int parity, int stop){
// nothing to do
}
};
MyUSBSerial usbserial(0xf055, 0xf001);
int main(void) {
while (1) ;
}
こちらもシリアル通信にあたる部分だけ。
を指定して transferIn/transferOut をすれば良い。DTR などを設定するために controlTransferOut も使うが、ただデバイスとやりとりするだけなら特に必要はない。
以下のような感じ。
const CDC_SET_LINE_CODING = 0x20;
const CDC_GET_LINE_CODING = 0x21;
const CDC_SET_CONTROL_LINE_STATE = 0x22;
const CLS_DTR = (1 << 0);
const CLS_RTS = (1 << 1);
const INTERFACE_INDEX = 1;
const ENDPOINT_INDEX = 2;
class USBCDC {
constructor() {
this.onread = () => {};
}
async connect(filters) {
this.device = await navigator.usb.requestDevice({
filters: filters || [
{ 'vendorId': 0xf055, 'productId': 0xf001 }
]
});
console.log(this.device);
const read = async () => {
const result = await this.device.transferIn(ENDPOINT_INDEX, 64);
this.onread(result.data.buffer, result);
if (result.status === 'stall') {
console.warn('Endpoint stalled. Clearing.');
await this.device.clearHalt(1);
}
read();
};
const configuration = await this.device.open();
if (this.device.configuration === null) {
await this.device.selectConfiguration(1);
}
await this.device.claimInterface(INTERFACE_INDEX);
await this.device.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': CDC_SET_CONTROL_LINE_STATE,
'value': CLS_DTR,
'index': INTERFACE_INDEX
});
read();
}
async write(data) {
if (typeof data === "string") {
// string to utf-8 arraybuffer
const arraybuffer = await new Promise( (resolve, reject) => {
const reader = new FileReader();
reader.onloadend = function () {
resolve(reader.result);
};
reader.readAsArrayBuffer(new Blob([data]));
});
console.log('write ab', arraybuffer);
await this.device.transferOut(ENDPOINT_INDEX, arraybuffer);
} else {
console.log('write rr', data);
await this.device.transferOut(ENDPOINT_INDEX, data);
}
}
async close() {
await this.device.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': CDC_SET_CONTROL_LINE_STATE,
'value': 0x00, // reset DTR
'index': INTERFACE_INDEX
});
await this.device.close();
}
}
const usbcdc = new USBCDC();
btnConnect.onclick = async () => {
usbcdc.onread = (buffer, result) => {
console.log(result);
console.log(String.fromCharCode.apply("", new Uint8Array(buffer)));
};
usbcdc.connect();
};
だいぶ前に買ったのを放置してたので動かした。なぜかSDカードコネクタとかついてる。最初は 5V インターフェイスでレギュレータを使うようになっているが、ジャンパで 3.3V にすることができる。
「Adafruit_ST7735」という名前のライブラリがいくらか公開されているので、それを参考にちょっと書きかえた。ST7735 シリーズは細かい違いがたくさんあるようでなかなか難しい。
このライブラリは setRotation するときに MADCTL を発行するが、RGB と BGR の選択もコマンドで設定を行うため、setRotation をすると色が反転するみたいなことが起きて困った。
基本的には setAddrWindow して更新範囲を指定して pushColor して更新する。
tft.setAddrWindow(offsetx, offsety, offsetx + width - 1, offsety + height - 1); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { usbserial.read(2, buf); uint16_t c = (buf[0] << 8) | buf[1]; tft.pushColor(c); } }
だけ。 表示部分はせっかく python の骨格があるので基本的にはそのまま python で書いてる。けど Pillow (python の画像処理ライブラリ) はそれほど高機能というわけではなくて、文字表示時に細かいオプションが一切指定できなかったりするので Ruby と Cairo とか慣れた他の方法でやったほうがよかったかも。
フォントは Noto Sans CJK の Black。Pillow が OTF をちゃんと読めてよかった。
表示はフルリフレッシュを避けるように数値部分だけを部分書き換えしている。これにより表示更新が早くなるのと、フルリフレッシュ時みたいな全体が反転するチラツキがなくなるので常時していても邪魔くさく感じにくいはず。
↑ 試している様子 (値はランダム)。起動時はフルリフレッシュしてその後は常に部分書き換え
Ben Krasnow: Fast partial refresh on 4.2" E-paper display from Waveshare / Good Display の記事を参考に LUT をコピペしたらできたが LUT が何をしてるのかさっぱりわからない。
https://github.com/cho45/electronic_badge_2018/blob/partialupdate/sketch/sketch.py
lib/epd4in2.py とかにもかなり変更を入れてる。LUT まわりが↑のエントリの epd4in2.cpp からのコピペなのでライセンス的に微妙。
Waveshare 4.2 Black/White EPD
全体的な動作の説明がスペックに書いてないので正確にわかることがあんまりない。
Look-up Table。EPD 書き換えのときに、各フェーズでどのようにしてピクセルに電圧をかけるかを決めると思われる。
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 とあわせて共通側電圧をどうするかを設定すると思われる。基本的に他の 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
B/W (白黒) モードの場合、DTM1 は "OLD" data を SRAM に転送する。DTM2 は "NEW" data を SRAM に転送する。
よくわからない。どっちも転送しておかないと、電圧かけたときに古いデータがでてきたりして謎。
仕様書のところどころで出てくる OTP って何?という話。
One Time Programming 1度だけ書きこみ可能なこと。E-paper の場合 LUT を OTP して固定することができる、ということらしい。とりあえず無視して良い。
画面外になる部分が partial update でもチラチラ更新されてしまうので、これを隠すためにフレームをつくってつけた。
以下のようにすれば動くことが確認できた。
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}`);
});
})();
NEEWER LED-NL660 というものをタイムセールで買ってみたので ColorMunki Photo + Argyll CMS で測定してみた。
WHITE と YELLOW というつまみがあり、2種類のLEDの光量が変更できるようになっている。色温度の調整はこれを微妙に動かして行う。
Result is XYZ: 5747.510694 6065.655076 5528.248292, D50 Lab: 439.775063 -11.375823 -26.558254 Ambient = 6065.7 Lux, CCT = 5545K (Duv 0.0049) Suggested EV @ ISO100 for 6065.7 Lux incident light = 11.2 Closest Planckian temperature = 5376K (DE2K 6.5) Closest Daylight temperature = 5492K (DE2K 2.3) Color Rendering Index (Ra) = 97.5 [ R9 = 85.5 ] R1 = 97.0 R2 = 98.3 R3 = 99.7 R4 = 98.1 R5 = 97.6 R6 = 96.7 R7 = 97.9 R8 = 94.6 R9 = 85.5 R10 = 97.6 R11 = 97.7 R12 = 83.7 R13 = 97.5 R14 = 98.4 Television Lighting Consistency Index 2012 (Qa) = 98.6
スペック上は5600Kまでと書いてあるが 5500K の LED っぽい。
Result is XYZ: 5563.515641 5243.322549 2120.666499, D50 Lab: 418.170369 60.679109 158.296845 Ambient = 5243.3 Lux, CCT = 3126K (Duv 0.0016) Suggested EV @ ISO100 for 5243.3 Lux incident light = 11.0 Closest Planckian temperature = 3079K (DE2K 3.0) Closest Daylight temperature = 3095K (DE2K 0.9) Color Rendering Index (Ra) = 98.1 [ R9 = 88.9 ] R1 = 99.0 R2 = 99.3 R3 = 99.0 R4 = 98.8 R5 = 98.5 R6 = 98.1 R7 = 97.3 R8 = 95.0 R9 = 88.9 R10 = 98.3 R11 = 98.5 R12 = 85.5 R13 = 99.2 R14 = 98.6 Television Lighting Consistency Index 2012 (Qa) = 98.1
3100Kぐらいが下限
Result is XYZ: 11395.644074 11395.813163 7722.945028, D50 Lab: 546.392814 29.623320 61.495553 Ambient = 11395.8 Lux, CCT = 4173K (Duv 0.0006) Suggested EV @ ISO100 for 11395.8 Lux incident light = 12.2 Closest Planckian temperature = 4158K (DE2K 0.9) Closest Daylight temperature = 4232K (DE2K -3.6) Color Rendering Index (Ra) = 98.8 [ R9 = 96.1 ] R1 = 99.3 R2 = 99.6 R3 = 99.6 R4 = 99.6 R5 = 98.9 R6 = 97.3 R7 = 98.4 R8 = 97.8 R9 = 96.1 R10 = 99.1 R11 = 97.1 R12 = 83.4 R13 = 99.2 R14 = 98.5 Television Lighting Consistency Index 2012 (Qa) = 98.3
最も明るいが4200Kぐらいの中途半端な色温度になる。
5000K 程度にあわせたとき。(WHITE 全部+YELLOWが左水平とちょっとぐらい)
Result is XYZ: 6934.349461 7181.415867 5994.476376, D50 Lab: 466.163889 1.000754 -3.284202 Ambient = 7181.4 Lux, CCT = 5029K (Duv 0.0029) Suggested EV @ ISO100 for 7181.4 Lux incident light = 11.5 Closest Planckian temperature = 4941K (DE2K 4.1) Closest Daylight temperature = 5042K (DE2K -0.6) Color Rendering Index (Ra) = 98.2 [ R9 = 90.1 ] R1 = 98.4 R2 = 99.3 R3 = 99.5 R4 = 98.5 R5 = 99.0 R6 = 97.0 R7 = 97.9 R8 = 96.0 R9 = 90.1 R10 = 99.3 R11 = 96.9 R12 = 85.9 R13 = 99.0 R14 = 98.7 Television Lighting Consistency Index 2012 (Qa) = 98.6
Ra は全ての指定色でかなり高い値が出てる。R12(青)もそこそこ高い値が出ていて良い。
このときのスペクトル
nanopi-neo2_friendlycore-xenial_4.14.52_20180628.img.zip の場合
Nano Pi Neo 2 の提供 OS イメージだと /sys/gpio が root 保有になっており、gpio の操作に特権が必要になっている。これを Raspbian と同様に gpio group をつくり、グループに対してパーミッションを与えるようにする。
グループを追加する。
sudo groupadd gpio sudo usermod -aG gpio pi
udev のルールを追加し、gpio に変更があった場合、所有権と権限を設定しなおすようにする。
$ sudo vim /etc/udev/rules.d/80-gpio.rules SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chown -R root:gpio /sys/devices/platform/soc/*pinctrl/gpio*'" SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chmod -R ug+rw /sys/devices/platform/soc/*pinctrl/gpio*'" SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chown -R root:gpio /sys/class/gpio'" SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chmod -R ug+rw /sys/class/gpio'"
sudo udevadm control --reload-rules
test をやると rule の実行状態がわかる。結果が成功したかどうかとかもこれでわかる。
sudo udevadm test --action=change /sys/devices/platform/soc/1c20800.pinctrl/gpiochip1/gpio/gpio0
trigger でもルール適用できるがあまり詳細なことはわからない。
sudo udevadm trigger --verbose --subsystem-match=gpio
レポジトリは https://github.com/friendlyarm/NanoHatOLED OLED 自体は I2C 経由、スイッチは GPIO に接続されている。当然だが NanoHatOLED に付属の python スクリプトを用いなくとも OLED やボタンを自力でハンドリングすることは可能 (後述)
/etc/rc.local から /usr/local/bin/oled-start が起動される。
/usr/local/bin/oled-start は /root/NanoHatOLED に cd して ./NanoHatOLED を起動してる。
./NanoHatOLED はバイナリだが、やってることはスイッチの GPIO を監視して、子プロセスに対しそれぞれ gpio0=SIGUSR1 gpio2=SIGUSR2 gpio3=SIGALRM を発生させているだけ。正直なんで C でやってるのかよくわからない。
./NanoHatOLED は /root/NanoHatOLED/BakeBit/Software/Python && python bakebit_nanohat_oled.py を起動している。
I2C 接続。アドレスは 0x3c (0b0111100)。128x64 OLED コントローラは SSD1306 (互換)
datasheet http://wiki.friendlyarm.com/wiki/images/a/af/096-30-SPEC_QG-2864KLBEG01_VER_C.pdf https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
I2C のコネクタは 2.0mm ピッチ。Grove システムのやつと一緒っぽい。http://wiki.seeedstudio.com/Grove_System/ http://akizukidenshi.com/catalog/g/gC-12634/ PHコネクター? かと思ったが違う。PH コネクタはピッチは同じだが挿すことができない。
シリアルは 2.54mm ピッチ。ちゃんと調べてない。
そこそこいい感じに書けた。
GPIO のボタンは Node.js の fs モジュールだけで GPIO の割込みを扱うには? | tech - 氾濫原 に書いた通りの方法で扱っている。EventEmitter の API にしてある。
OLED に対応する canvas (node-canvas) オブジェクトを保持し、更新があったかどうかを確認しつつ一定周期で再描画するような設計にした。これにより普通にブラウザでJSを書くのと同じようなモデルでコードを書くことができる。
Aliexpress で 3W 365nm のパワーUV LEDを買ってみた。ちなみに 3W と書いてあっても 3W じゃないことが多いので注意が必要。絶対最大定格 700mA Forward Voltage が 3.0〜4.0V。なので2.1W〜2.8W程度。放射束はスペック上 900mW と書いてある。
これを定電流降圧回路で600mAで光らせた。放熱器必須。アルミ基板なので熱を吸われてしまい半田がつけにくい。
桜のマークを視認する場合 365nm ぐらいで発光するとよくわかる。可視光が少ない紫外線でないと蛍光がわかりにくい。パスポートの場合、顔写真ページには顔写真そのものが蛍光印刷されていておもしろい。
印章はとてもわかりやすく蛍光する。裏側の印章は蛍光しない。「千円」の漢数字のところは非常にわかりにくいが蛍光している。またお札全体に帯がついていてこれも薄く蛍光している。蛍光インクは透明だがすこしテカテカしているので、実は蛍光しなくても光を反射させると普通にわかってしまう。
一瞬(0.5秒ぐらい)で流動性は完全になくなるが表面がべとつく。約3分程度で表面が完全硬化する感じ (ベトツキがなくなる)。
パワーLEDだと1灯でも照らす範囲が広いので、適当なリフレクタ ( 3Dプリントしたものにアルミ箔を貼ったもの) をつけるとなかなかフラットに照射できる。
UV計にも感度スペクトルがあるので、相対的な強さしかわからないが、OptSupply OSV1YL5111A (放射束が2〜4mW(@20mA)) と比べるとほぼ同じ距離で10倍程度違う。
なお太陽光の快晴時地表 UV 強度は 60W/m2 = 6,000μW/cm2 らしい。パワー UV LED 1灯でも今回測定した距離では2倍ほどの紫外線照度がある。
少し離して2灯でこんな感じ。これ以上近付けると測定器の計測限界を超えるみたい。
ちなみにこのUV計は国内ではマザーツールが扱っているがLutron のSP-82UVみたい。Lutron の校正証書みたいのがついてくる。
ref.