ラズパイ内蔵テンキー「Keybow」のファームウェアをRaspbian化する
この度、Raspberry Pi Zero W搭載のテンキーボード「Keybow」を購入しました。
LUAスクリプトで複雑なキーマクロが組めるのが特徴の製品ですが、コントローラがラズパイなのでもう何でもできるじゃんと思ってポチー!
製品の特長はあっきぃさんのブログが詳しいです。
しかしながらこれ。
ファームウェアに通常のRaspbianじゃなくて専用の軽量OS「Keybow OS」が採用されているのですが、まぁ出来ることが少ないと。
例えば無線LAN経由でRqspberry Pi Zero Wにsshでログインしてキーコンフィグファイルを直接編集したいなーと思っても、ざっとwpa_supplicant,openssh_server,vim,nanoなんかが足りないと。apt-getもgccも無いから割と詰んでるなぁと。
よく考えたらこれ別にOSを軽量化する必要あんまりなくね?と思ったので、KeybowのOSをRaspbianに差し替えることにしました。
方針
keybowコマンド
Keybowのキー入力やLEDの点灯は、/boot/keybowと言うC言語で書かれたコマンドで行っています。
Keybow OSからkeybowコマンドと動作に必要なLUAスクリプト群を移植すればよさそうです。
USBガジェットドライバ
Keybow OSはRaspberry Pi Zero WのUSB OTG機能を使って自身をHIDデバイス(キーボード)に見せかけています。
これにはlibcomposite.koと言うドライバが使われているので、これをRaspbian起動時にロードするようにします。
余談
ラズパイでUSB OTGシリアルコンソールを使うときはg_serialと言うドライバを使うので、てっきり類似品のg_hidを使ってると思って新年からハマりました。g_の付くドライバは設計が古く、最近はlibcompositeを使うのがナウいみたいです。
やってみる
Raspbian Stretch Liteをインストールする。
現時点で最新の2018/11/13版のRaspbian Stretch Liteを普通にインストールします。
GitHubからKeybow OSのファームウェアをダウンロードする。
$ cd /tmp/ $ wget https://github.com/pimoroni/keybow-firmware/archive/v0.0.2.tar.gz $ tar xvf v0.0.2.tar.gz
ファームウェアからkeybowコマンド関係のファイルをコピーする。
$ cd keybow-firmware-0.0.2/sdcard $ chmod 755 keybow $ sudo cp -p keybow /usr/local/bin/ $ sudo cp -rp keys.lua keybow.lua default.png layouts/ patterns/ snippets/ /boot/
keybowを起動するsystemdのユニットファイルを作る。
/etc/systemd/system/keybow.service
[Unit] Description=Keybow Daemon [Service] ExecStart=/usr/local/bin/keybow Restart=on-failure RestartSec=5 [Install] WantedBy=default.target
起動時にユニットファイルが実行されるようにします。
$ sudo systemctl enable keybow
/boot/cmdline.txtと/boot/config.txtを編集する
Raspbianの起動時にUSB OTG用ドライバdwc2と件のlibcomposite.koをロードするように記述します。
/boot/config.txt(dtoverlay=dwc2を追加)
/boot/cmdline.txt(行の末尾にmodules-load=dwc2,libcompositeを追加)
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=ffb833f2-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait modules-load=dwc2,libcomposite
ここでRaspbianを再起動して、KeybowのLEDが七色に輝きだしたら成功です。
IFTTTにGoogle Apps Scriptを混ぜたらヤバい化学反応が起こった件
はじめに
2010年12月に彗星のごとく現れたWebサービス同士のマッシュアップサービスIFTTT。
「if (this) then (that)」と言うシンプル極まる構文で手軽にWebサービスが連携出来るのが特徴です。
IFTTTは様々なWebサービスをサポートしており、ちょっと挙げるだけでもTwitter、Evernote、Google Drive、Skypeなどの有名どころから、ナニコレ?ってものまで実に多彩です。
加えて、IFTTT独自のサービス群も魅力的です。
スマホ通知、Alexa・Google Homeの音声入力、スマホのウィジェット(メモ、ボタン、写真)など。
それらを前述のシンプルな構文で自由に組み合わせられるのが、IFTTTの魅力だと思います。
IFTTTの弱点
シンプルさが売りのIFTTTなのですが、それがそのまま弱点にもなっているという印象です。
例えば「為替の情報を1時間に1回Pushbulletに通知したい」と言う比較的シンプルな要求はIFTTTでは実現できません。
為替の情報は「if (this) then (that)」のthisにFinanceサービスを指定すれば取得できますが、Financeサービスは「1時間に1回為替情報を報告する」と言うトリガーを持っていないからです。(1日1回トリガーはある。)
こんな感じで、イベントが発生するタイミングはthisのサービスに依存しており、ユーザーが自由に制御できません。
IFTTTではスマホのウィジェットや各種スマートスピーカーを使って手動でトリガを掛けることもできますが、それ自身がthisを消費してしまうので、例えば下の様なアプレットは実現できません。
ボタンが押されたら(this) 明日の天気を(×指定不可) SMSに送信する(that)
シンプルさの引き換えに、少しでも凝ったアプレットは実現できない。これでは魅力が半減です。
IFTTTの弱点を克服する
そんな「なんでもは出来ないわ。出来ることだけ。」のIFTTTですが、その中に「Webhooks」と言う興味深いサービスがあります。
「Webhooks」はWebhookを送受信するサービスです。
と、言うことでIFTTTがサポートする以外の何かと連携が可能です。
これを上手く使えば、
為替の情報を1時間に1回Webhookする外部サービス -> Webhookを受信したら(this) Pushbulletに通知する(that)
や
ボタンが押されたら(this) Webhookを送信する(that) -> Webhookを受けて明日の天気をWebhookする外部サービス -> Webhookを受信したら(this) SMSに通知する(that)
が実現出来るということです。
外部サービスに為替や天気を取得する処理が必要な反面、IFTTTの豊富なthisやthatを組み合わせられるのは非常に魅力的です。
試してみる
と言うわけで、今回は「為替の情報を1時間に1回Webhookする外部サービス -> Webhookを受信したら(this) Pushbulletに通知する(that)」奴を作ってみようと思います。
「Webhookする外部サービス」にはGoogle Apps Script(GAS)を使います。
GASはGoogleさんのサーバでJavascriptを動かせるサービスで、Webhookを送受信するようなサービスが無料で作れちゃいます。
自宅サーバやVPSを用意しなくてもインターネット上でちょっとした処理が動かせるので、とても使い勝手が良いです。
IFTTTのアプレット
thisの設定
(1)New Applet画面のthisをクリックする。
(2)Webhooksを選択する。
(3)Receive a web requestを選択する。
(4)イベント名を聞かれるので、今回はsample_triggerとする。
thatの設定
(1)New Applet画面のthatをクリックする。
(2)Pushbulletを選択する。
(3)Push a noteを選択する。
(4)Pushbulletに送信するテキストを設定する。
{{Value1}}がWebhook経由で渡されたデータになります。
画面上は白背景で表示されています。
Webhook用のURLの確認
WebhooksサービスのDocumentationボタンから、Webhook用のURLを確認しておきます。
https://maker.ifttt.com/trigger/sample_trigger/with/key/12345678が今回のURLになります。
GASのスクリプト
IFTTTのWebhookにPOSTする処理
// IFTTTにWebHookをPOSTする function sendIFTTTWebHook(endpoint, value) { var message = { "value1":value }; var options = { "method":"POST", "headers": { "Content-Type":"application/json" }, "payload":JSON.stringify(message) }; UrlFetchApp.fetch("https://maker.ifttt.com/trigger/" + endpoint + "/with/key/12345678", options) }
為替情報を得る処理
// JPYUSDを得る function getUSDJPY() { var text = UrlFetchApp.fetch("https://www.gaitameonline.com/rateaj/getrate").getContentText(); var data = JSON.parse(text); var quotes = data["quotes"] var usdjpy = quotes.filter(function(item) { return item["currencyPairCode"] == "USDJPY"; })[0]; return usdjpy; }
メイン処理
// メイン function main() { var usdjpy = getUSDJPY() sendIFTTTWebHook("sample_trigger", "ドル円" + usdjpy["bid"]); }
GASのスクリプトエディタの[編集] - [現在のプロジェクトのトリガー」で、main()が1時間周期で実行されるようにします。
おわりに
これで、為替の情報が1時間に1回Pushbulletに通知できるようになりました。
Pushbulletの扱いはIFTTTに任せているので、GAS側は非常に簡素なコードになっています。
今後「PushbulletじゃなくてTwitterに投稿したい!」となっても、IFTTTにthatをTwitterにしたアプレットを作るだけで対応が可能です。
また「1ドルが112円以上かつ113円以下の時だけ通知させる」みたいな複雑な条件は、GASでサクッと書いてしまえばいいでしょう。
IFTTTの手軽さとGASの自由度が融合した、とても扱いやすい仕組みが出来たと思います。
ところでこの仕組み、UNIXに於けるパイプラインに酷似していると思いませんか?
これこそ、2000年代からみんなが追い求めていた「Web2.0」の一つの到達点ではないかと思いました。
Ubuntu18.04でdnsmasqの起動に失敗する。
環境
apt-get install dnsmasqしたところ、DNSのポート(53/UDP)が既に使われているってことでdnsmasqの起動に失敗しました。
何者だ?と調べてみると、systemd-resolvedって子らしい。
systemd-resolvedは新しい名前解決の仕組みで、DNS、LLMNR、mDNSなんかを統一的に扱えるものの様です。
しかしながら、従来の/etc/resolv.confを直接参照してDNSに問い合わせに行くプログラム(そんな奴いるの?)のために、localhost(127.0.0.53)にスタブDNSサーバを立てつつ、それをネームサーバに指定した/etc/resolve.confを自動的に生成してくれる機能があります。
今回はこの機能がおせっかいをしていると。
幸い、Proxmox VEのUbuntu18.04イメージはsystemd-resolvedのスタブDNSを使わない*1ので、さくっと止めてしまいましょう。
/etc/systemd/resolved.conf
[Resolve] #DNS= #FallbackDNS= #Domains= #LLMNR=no #MulticastDNS=no #DNSSEC=no #Cache=yes DNSStubListener=no ←コメントを外して"no"にする。
systemctl restart systemd-resolvedでスタブDNSがいなくなります。