背景

現在は CNC のコントローラとして Grbl + 自作のインターフェイスを使っています。Grbl の G-code インタプリタは必要な機能はほとんどどありますが、凝ったことをしようと思うと少し困ることがでてきます。

ということで、Beagle Bone Black と Machinekit (Linux CNC) での環境構築をぼちぼちはじめています (まだある程度設定しただけで動かせてませんが)。その過程で結局コードを読むハメになってるので覚書を残しておきます。

Machinekit とは何か

Machinekit は Linux CNC (EMC2) からの fork プロジェクトです。Linux CNC は x86 しかサポートしていませんが、Machinekit は ARM もサポートしています。細かい違いがいろいろあるみたいですが、実のところよくわかってません。

OSSのG-code実行機だと Linux CNC が最も高機能なようなので、これが動く環境がうまく作れれば、機能で困ることはなくなるはずです。

Machinekit / Linux CNC はどのようにして GPIO を操作するか

名前の通りなのですが、これらは Linux 上で動きます。Linux CNC の典型的な実行環境は、普通の x86 コンピュータにパラレルポートを付けたマシンです。パラレルポートをGPIOとして使用し、ステッピングモータドライバなどに送る信号を出力します。

全てソフトウェアで制御信号を生成するため、Xenomai という Linux カーネルにリアルタイム拡張を行うものを組込み、複数のリアルタイムスレッドを協調して動かすことでスムーズに実行できるようにしてあります。

具体的には

  • base-thread
    • 25μs ごとに起動
    • 最小の実行単位を扱うスレッドで、GPIO の実際の操作を行う
  • servo-thread
    • 1ms ごとに起動
    • 入力などを処理して base-thread で行う操作を決めるスレッド

という2つのスレッドがあります。実行間隔を書きましたが、実際には Xenomai を組み込んだ状態でも各スレッドが起動される間隔にはばらつきがあり、正確に起動されるわけではありません。「リアルタイム」はあくまで最悪の応答時間を保証しているだけです。十分余裕を持って行動するために処理の重さに応じて実行スレッドが分けられているわけです。

Machinekit と Beagle Bone Black

Beagle Bone Black は TI の ARM SoC である AM3359AZCZ100 がメインCPUの、Raspberry Pi に似たカードサイズ Linux コンピュータです。GPIO が豊富にあり、これに Machinekit をインストールすることで、単体でCNCコントローラにできます。

パラレルポートに依存した環境構築というのは今時ちょっとやる気が起きませんし、パラレルポートが増設可能なフル装備の Linux コンピュータを組み立てようと思うと結構コストがかかります。Beagle Bone だと単体で役目を果たすことができます。

Raspberry Pi ではダメなのか? という疑問があるかと思いますが、実は BBB にあって Raspberry Pi にはない重要な機能があります。それが PRU (Programmable Real-time Unit) です。

PRU は要するにメインのCPUと独立して動作できるマイコンです。メインCPUは1GHzですが、PRU は 200MHz の独自クロックで、メインCPUとは独立して動作します。

PRU の実装自体は独自の命令セットのアセンブリを書いて、TI 提供のツールでコンパイルして実行バイナリを得ます。なので、あまり高度なことをやるのは難しいですが、CNC の制御信号を出すというような用途にはまさにうってつけです。

フツーのPCと何が変わるか

Machinekit のリアルタイム処理は base-thread と servo-thread に分かれていると書きましたが、BBB の場合は base-thread は存在せず、servo-thread だけがあります。base-thread 相当の処理が PRU で独立して行われます。

Machinekit の hal_pru_generic ドライバにおいて PRU が GPIO を操作する間隔はデフォルトでは10μsとなっています。これは普通のPCのデフォルトの2倍以上の速度ですが、内部的には PRU 内でビジーループでえ 10μs を待つように実装されています。GPIO まわりのタスク処理が 10μs すなわち 2000 CPUサイクル以内ならば遅延することがありません。

現状では

  • stepgen
    • ステッピングモータの駆動パルス生成
  • pwmgen
    • ソフトウェア PWM
  • encoder
    • エンコーダーパルスをなんかするやつ(使ったことないです)

を PRU 内で実行できます。

PRU と PRU Low Latency I/O

AM3359AZCZ100 にはPRU Low Latency I/O というのがあり、複数の特定ピンの GPIO を PRU 内から r30/r31 レジスタへの読みかきによって1サイクルで行えるというものすごいものがあります。

一方で、PRU 内からだからといって PRU Low Latency I/O しか使えないというわけでもなく。普通の GPIO もそれよりはレイテンシがありますが読み書き可能です。

Machinekit の実装でもいずれのピン設定も利用可能です。前述の通り 10μs ごとの操作になるので、特に Low Latency ピンにこだわる必要はなく、この場合特にメリットもありません。

このへんは HAL の設定まわりになるので、ちゃんと動かせてからそのうち書きたいと思います。

  1. トップ
  2. tech
  3. Machinekit (Linux CNC) のアーキテクチャと、BeagleBone Black での動作

HID Proxy機能のあるドングルを使うとOSを介さずUSB HIDキーボードとしてBIOS起動時に認識させることができます。(BIOSというか、近年だと UEFI ですが)

 -

5.0 / 5.0

MM-BTUD43 (CSR8510 A10)

一部の BLE USB アダプタには HID Proxy モードが結構前から実装されているようです。これは CSR 社製のものです。たまたま手元にあったのですが、どうやらこれも HID Proxy モードをそなえいるようです。

経緯

BIOS 画面で BLE キーボードを使うため、HID Proxy モードのあるアダプタを探していました。当初そんなものはなさそうと思ってたのですが、最近になって以下のようなページを見つけました。

How to put CSR8510 A10 into HID Proxy Mode?

Switching Boot Modes
The initial boot mode is set by PSKEY_INITIAL_BOOTMODE. If this PS Key is set to 2 (HID proxy mode), CSR8510 A10 enumerates as USB HID device.

When the PC boots with its operating system and Bluetooth host stack, the Bluetooth host stack may reboot the CSR8510 A10 in mode 0 (standard HCI operation).
In this mode, the Bluetooth Host Stack handles the HID device functions.

Note:
Switching from HID to HCI is allowed in both HID Boot Protocol Mode and HID Report Protocol Mode configuration. In HID Report Protocol Mode, the USB report descriptors should include the feature report to accept the USB Set Feature request to accept the command from the host.

This report is defined as:
/* Feature report to enable Host Communications */
0x06, 0x00, 0xff, /* USAGE PAGE (Vendor 0xFF00) */
0x09, 0x01, /* USAGE (Vendor Page 1) */
0x95, 0x08, /* REPORT COUNT (8) */
0x75, 0x08, /* REPORT SIZE (8) */
0xB1, 0x02, /* FEATURE (Var) */


When in HCI mode - it is basically a 'pass-through' mode - no profiles, can with any Bluetooth 'flavour'.

When in HID Proxy mode - it is indeed meant for BLE only - using 2 instances of HID over GATT, one for keyboard and one for mouse.

対象は BLE デバイスのみです。もちろん事前にOSを起動した状態でペアリング(正確にはボンディング)してある必要があります。これによってデバイスに情報が保存され、HID Proxy モードでも通信が可能になります。

しかし、デフォルトでは PSKEY_INITIAL_BOOTMODE は 0x0000 のようで、HID Proxy として働きません。本来メーカーで設定する項目で、ユーザが書きかえられる部分ではありません。ただ、ちょっと面倒ですがデバイスを再設定することができました。

bccmd

Linux の bluez に CSR のデバイスを設定するためのコマンドが含まれており、これを使って HID Proxy モードを有効にします。

手元にさっと利用できる Linux が Raspberry Pi しかなかったので、Raspberry Pi で行いました。bluez が入ってさえいればなんでもいいはずです。おそらく VM 上で起動しても一時的にデバイスを VM 側に接続するようにすれば動くはずです。

USB にデバイスを接続した上で以下のコマンドを実行していきます。

# 現在のブートモードを確認
$ sudo bccmd psget bootmode
Initial device bootmode: 0x0000 (0)

# アドレスを確認
$ sudo bccmd psread -s 0
...
0x03cd - Initial device bootmode (2 bytes)
...

# アドレスにブートモードを書きこむ 0x0002 が HID Proxy モードらしい
$ sudo bccmd psset -s 0 0x03cd 0x0002

# 確認
$ sudo bccmd psget bootmode
Initial device bootmode: 0x0002 (2)

しかしどうも、OS起動後は HCI モードに移行というのは全くうまく動きません。HID Proxy から抜けられない。そして、 HID Proxy から抜けられないと bccmd がデバイスを認識しません (productId も変化するんですが、bccmd がそれに対応してないのでUSBデバイスを見付けられない)

ってことで、試したはいいけど戻せなくなってあせりました。

bluez の tools をコンパイル

bluez のツールとして /lib/udev/hid2hci という hid から hci に移行させるツールがあって、これで強制的に HCI にできるかと思いきや、できませんでした。調べているとシステムにインストールされているやつが古かったので、自力でコンパイルします。

http://www.bluez.org/download/

 sudo apt-get install libglib2.0-dev
 sudo apt-get install libdbus-1-dev
 sudo apt-get install libudev-dev
 sudo apt-get install libical-dev
 sudo apt-get install libreadline-dev
 sudo apt-get install libbluetooth-dev

本来必要ない依存もあるかと思いますが、これらを入れないと configure が通らず Makefile が生成されないので全て入れています。

wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.41.tar.xz
tar xJvf bluez-5.41.tar.xz
cd bluez-5.41
 ./configure
 make tools/hid2hci

全部ビルドする必要はないので hid2hci のみビルドしました。これで以下のように戻します。

sudo ./tools/hid2hci --method=csr2 --devpath=$(udevadm trigger  --subsystem-match=usb --attr-match=idVendor=0a12 --attr-match=idProduct=100b --verbose | cut -d '/' -f 3-)
# HCI モードに戻ったので bccmd が普通に使えるように
sudo bccmd psget bootmode

method=csr2 がポイントです。最新だとこのメソッドが使えるようになっていて、HCI モードに移行できます。

なお、このコマンドでやっていることは冒頭で引用した中にある feature report を送っているだけです。なので HID のレポートを送れさえすれば bluez をコンパイルする必要はないはずです…… が、さくっと feature report 送るみたいなのもなかなか難しいのでコンパイルしてみました。

HID Proxy モードを活用するためには…

ペアリングするときだけ HCI として起動してくれればいいので、基本的には HID モードでも良いかという気はします。ただ、キーボード1つにつき1つのBLEドングルが必要になりますので、Bluetooth デバイスが他にもあるなら OS 起動中はちゃんと HCI モードになってほしいところです。

ということで、OS起動時にhid2hci相当のことをやるコマンドを起動すれば問題なさそうです。しかしWindows でこれをやるのは若干面倒です。検索すると hid2hci.exe という CSR のツールがあることはわかるのですが、公式に入手する方法が見つけられませんでした (デバイススタックと一緒にインストールされるのかもしれませんが、OS 標準のスタックを使ってるのでインストールしたくありません)。他のところから怪しい .exe なんてダウンロードして実行するのは愚かなことなので、自分でなんとかします。

hid2hcix.exe

結局、自分で書くのが一番てっとり早いので、hidapi を使って書いてみました。mingw でクロスコンパイル環境にして OS X でコンパイルしています。

Linux には hid2hci があるのでいいとして、OSX でも無駄にコンパイルできるので、主要OSで HCI モードにできる安心感が生まれます。

hidapi がそのままだとキーボードのHIDデバイスに対して使えないので、1行だけ修正をいれてあります。キーボード・マウスのようなHIDデバイスはWindowsがOSレベルで握ってしまっていて、デバイスファイルを CreateFile できないという問題があるようです。また、キーボードやマウスには Feature Report のやりとりしかできません。

Linux の hid2hci をまるまる再実装したほうが便利かもしれませんが、HCI から HID に戻す実装は HCI にアクセスする必要があって面倒ですし、自分の環境だと CSR の CSR8510 A10 以外に入手できるものがないので、決め打ちの実装になっています。

レポジトリ内のコードだけでコンパイルも簡単だしこれで必要十分でしょう。これをOS起動時に自動実行するようにすれば、OS起動前はHID Proxy、OS起動後は Bluetooth ドングルとして働くことになります。

国内販売で CSR8510 A10 を使ってると確認できるもの

レビューとかを見るとプロトコルスタックをインストールしている記述が多かったりしますが、Windows 10 なら OS にプロトコルスタックがあるので不要です。自分ではインストールしたことありません。かなり古いレビューがずっと残ってるので注意が必要です。

 -

5.0 / 5.0

上にも書きましたが MM-BTUD43 は手元にあって、確認済みです。

プラネックス PLANEX Bluetooth USBアダプター Ver.4.0+EDR/LE(省エネ設計)対応 BT-Micro4 - プラネックス

プラネックス

4.0 / 5.0

PLANEX BT-Micro4 は公式に技術情報を公開していて、CSR 8510A10 であることを表記しています。これも手元にあって、確認済みです。

 -

3.0 / 5.0

ロジテック LBT-UAN04C1BK も CSR 8510A10 のようです。これは持ってないので確認してません。高いので特にこれを選ぶ理由はなさそうです。

ref

  1. トップ
  2. tech
  3. BIOS 画面でもBluetooth LE な無線キーボードを使いたい

BLE Nano に載ってる MDBT40 モジュールの認証情報: MDBT40

  • F1D 2402-2480MHz(40ch) 3.0mW/MHz
  • F1D 2405-2480MHz(16ch) 3.0mW

MDBT40 の最大出力は +4dBm。W換算すると2.5mW。少し余裕があるのは mW 単位での承認なんだろうか?

MDBT40P

Seeed Studio が MDBT40P を販売している。けど、メモリサイズが16KBなのか32KBなのか書いてなくて定かではない (たぶん16KB)

MDBT40P と MDBT40 の違いはアンテナで、P は Printed Circuit Antenna のことで、よくあるトレースアンテナ。MDBT40 のほうは積層チップアンテナ。

メーカー仕様書を見ると、積層チップアンテナでは Over 80mm、トレースアンテナでは Up to 60m と書いてあり、あきらかに電波の飛びは違うみたい。アンテナの性能が良いということは、同じ距離飛ばすために必要な送信出力が低いということですから、低消費電力に繋がります。

BLE Nano には MDBT40 が使われているけど、MDBT40の単体販売は ebay で多少あるぐらい。

MDBT40 シリーズはスペック違いがいくつかあるけど、BLE Nano に使われているような、チップアンテナ+256KB Flash+32KB RAM というモデルの単品販売は見つけられなかった。BLE Nano を買うしかない。

MDBT40P の技適は?

アンテナも含めての技適なので、アンテナが変わってる場合、技適が適用されているか改めて注意する必要がある。

メーカー仕様を見る限り、MDBT40P も、あるいは他の多少違うモデルに関しても「MDBT40」という統一モデルとして工事設計認証番号を取得しており、製品にマークと刻印があるので問題なさそう。

  1. トップ
  2. tech
  3. BLE Nano に載ってる MDBT40 モジュールのメモ

PowerShell がダメだったので、素直に Visual Studio 入れて C# 書くぞと思ったわけです。そしたらハマらず書けるだろと。検索したらいかにも動きそうなサンプルコードでてくるしね。

で2時間ぐらい試行錯誤したけど、ダメでした。

References に Windows.Devices を入れたりいろいろして、うまくいきそうだなと思ったけど、結局 await が動かない。Console Application で作ろうとしてるのが悪いのかなんなのかわからないけど、

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.HumanInterfaceDevice;
using Windows.Devices.Enumeration;
// WindowsRuntimeSystemExtensions could not be found
// using WindowsRuntimeSystemExtensions.AsTask;

namespace Foo
{

    class Program
    {
        const UInt16 USAGE_PAGE = 0x01; // Generic Desktop Ctrls
        const UInt16 USAGE_ID = 0x06; // Keyboard

        static void Main(string[] args)
        {
            Task.Factory.StartNew(async () =>
            {
                await new Program().run();
            })
            .Unwrap()
            .Wait();
        }

        private async Task run()
        {
            var selector = HidDevice.GetDeviceSelector(USAGE_PAGE, USAGE_ID);
            var devices = await DeviceInformation.FindAllAsync(selector);
            Console.WriteLine("%s", devices);
        }
    }
}

が書けないんですよ。IAsyncOperation に GetAwaitor がないから await できないって怒られる。よくわからんけど .AsTask() とインスタンスメソッド形式で WindowsRuntimeSystemExtensions を読んでみるけど、これがそもそもできない。

References に WindowsRuntime がないせいか?と思ったけど、Add References... には WindowsRuntime なんで微塵も出てこないんですよ。

で、そういや NuGet とかいう臭そうな名前のパッケージマネージャがあったなと思って、 Manage NuGet Packages... から WindowsRuntime を検索すると、あるわけですね。

ははーん。ようやく辿りついたぞ、って思うじゃないですか。意気揚々と Install ボタンをクリックします。Output を見てるとなんとなくインストールすすんでいる様子がみえます。で、画面の表示が更新されて Output が表示から消えたんで、インストール終わったんだなと思いました。しかしそこには何の変化もない Solution Explorer さんの姿がありました。References にも何も増えてない。一体何をインストールしたんだ?と思ったら、NuGet の画面をもう一度見ると、まだ「Install」のままなんですよ。

そこでまぁ1回ぐらいは失敗するよなと思ってもっかい Install をしました。また Output にインストールがすすむ様子がうかがえました。画面が更新されました。インストールされたはずですが何も変化がありません。もしやと思って、インストール終了と共に自動的に非表示になった Output 画面を見ると、最後の最後で

Could not install package 'System.Runtime.WindowsRuntime 4.0.11'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.6.1', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.
========== Finished ==========

とか出てるんです。エラーメッセージもお世辞にも親切ではない。意味がわかりません。どうしようもないので諦めました。

さようなら Visual Studio。

いくつかのメモ

普段 IntelliJ IDEA を使ってるので、Visual Studio を使ってみると???ってなるところがかなりある。一言でいうと「おもてなし感がない」

  • using を手動で書かなければいけない? いきなりクラス名と書きはじめても一切補完されないし、自動的に using が書かれたりもしない。何かやりかたがある?
  • using を書いても References に自動的に依存が追加されたりはしない。その using がどのアセンブリに入ってるか把握して References に追加しないといけない。
  • using を書いても反映されるまで数秒かかり (別に低スペックマシンってわけじゃないのに)、赤い警告が消えないので「え? これ間違えてるの?」ってしばらく思うハメになる
  • ググって出てくる情報が大抵古い。Windows8 とかいう古代のOSの時代の情報がでてくる。WindowsRuntime を使うときに「このdllを追加しろ!」と「ほげほげを追加しろ!」とかひっかかってくれるんだけど、全部情報が古いので使えない。
  • エディタ上に赤線で警告がでるけど、キーボード操作で見ることができない?? マウスポインタをかざさないと見れなくて何がおかしいのがさっぱりわからない。

フツーにちょろっと、うまくいけば10分〜20分ぐらいで書けるコードのはずだったのに、時間を無駄にしたなあという感じで満足度が極めて低い…… 前向きに考えると、自分の技術力が低すぎて Windows アプリケーション作るのは不可能ってことがわかって良かった。

  1. トップ
  2. tech
  3. Visual Studio Community で Console Application を書こうとして挫折した

[Windows.Devices.HumanInterfaceDevice.HidDevice,Windows.Devices.HumaInterfaceDevice,ContentType=WindowsRuntime]
[Windows.Devices.Enumeration.DeviceInformation,Windows.Devices.Enumeration,ContentType=WindowsRuntime]

こう書いておくと、

$selector =  [Windows.Devices.HumanInterfaceDevice.HidDevice]::GetDeviceSelector($usagePage, $usageId)
$async = [Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync($selector)

と、ここまでは書くことができる。しかし PowerShell には非同期サポートがないので、これ以上どうしようもない。

とりあえず Task に変換して sleep しながら IsCompleted を見ればなんとかなるかなと思い以下のようにしてみた

$task = [Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync($selector).AsTask()

これはうまくいかない。AsTask() が存在しないと言われる。WindowsRuntimeSystemExtensions だからしかたないのかな? と思い、さらに以下のように展開してみる

$task = [WindowsRuntimeSystemExtensions]::AsTask([Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync($selector))

これはオーバーロードされているメソッドが見つからないというエラーになる。引数が実行時に型なしになるためだと思い、型指定をつけてみる

$task = [WindowsRuntimeSystemExtensions]::AsTask([Windows.Foundation.IAsyncOperation] [Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync($selector))

これは Windows.Foundation.IAsyncOperation が存在しないと言われてエラーになる。

[Windows.Foundation.IAsyncOperation,Windows.Foundation,ContentType=WindowsRuntime]

を冒頭に足してみるが、ダメ。Foundation だから? わからないけどとにかくダメ。Add-Type とかしてみてもダメ。

結論:PowerShell とかクソ

他の方法

C# でラッパーを書いてロードすればなんとかなる。が、ビルトインの機能でなんとかしたくて PowerShell を使っているのであって、C# で書くなら全部C#で書くわハゲ

  1. トップ
  2. tech
  3. Windows PowerShell から Windows Runtime API を呼びたかった

3M バーチュア V4 保護めがね 11672 - スリーエム(3M)

スリーエム(3M)

5.0 / 5.0

今まで100均の保護メガネを使ってたけど、ものすごく曇るので、本当に必要そうなときしかつけてないことが多かった。結構つけたりはずしたりするのが面倒。

上記のやつを買ってみたけど、ほんと全然曇らなくてびっくりする。しかし夏は暑いので、やっぱつけはずしは多少しないとダメだなって感じ。

100均のは捨てた。

  1. トップ
  2. tech
  3. 保護メガネを新調した

余談だけど、既存キーボードと併用して Karabiner を使えば実際に左右分割で使えるはず、と思ったが、どうもそれができなかった。修飾キーの共有が BLE キーボードだとうまくいかないみたいだった。Karabiner からもキーボードデバイスとして認識されず謎。これは未だ謎。入力はできてるけどキーボードデバイスとして認識されてない。

https://lowreal.net/2016/09/01/2

と書いたんですが、いろいろやってるうちにできるようになったので記録しておきます。

OS では認識されているにも関わらず EventViewer の Devices にも出なくて???と思っていましたが、どうやら PnP ID (Vendor ID / Product ID) がないと Karabiner に認識されないみたいでした。

といっても VendorID をとるのは難しいので、おそらく一生割り当てられないであろうIDで適当に設定しました (不正ですが)

PnP ID も UUID ベースにすればいいのに、完全登録制なのは不自由な感じがしますね。

  1. トップ
  2. tech
  3. Karabiner で自作キーボードが認識しなかった件

ペアリングして、waitForEvents でスリープしている時間の電流値、つまりアプリケーション的にはアイドル時の各OSごとの消費電流を測った。

というのも基本的に OSX でデバッグしていて殆ど他のOSで検証してなかったからです。

  • OSX: 0.0054mA
  • Windows 10: 0.8mA
  • Android 5系: 0.185mA

なんでこんなに違うんだろうという結果でした。Windows で消費電流が多いのはうすうす気付いてたんですが、あまりにも多過ぎる。

OSドライバが Input Report を READ しまくってる? Notification による実装になっていないとか? よくわからない。slave latency を鬼のように増やしても消費に影響がほとんどないので、強制的に起こすようなパケットがめっちゃ送られていそう。さっぱりわからない。

コネクションまわりのパラメータは実装上、

  • Max Connection Interval
  • Min Connection Interval
  • Slave Latency
  • Connection Supervision Timeout

ぐらいしかなく、どれを変えても変化がない。PnP ID によって専用ドライバが呼ばれるとかがあるのだろうか? と思ったけど、よくわからない。

USB ベースのHIDで実装するとそういうことになってる可能性もないことはない気がする。しかし Microsoft自身も BLE キーボードは発売しているし、こんなことになるはずはないと思う。

BLE のパケットスニファができればもうすこし原因に近付けるかもしれないけど、流れてるパケットがわかっても対処方法が思いつくとは限らないのでやる気があまり沸いてない。というか既に割と飽きてるので OSX で問題ない挙動のために頑張るモチベーションがない……

BLE + HOGP でまともに動いているデバイスの service / characteristics / descriptor のダンプなどをお持ちのかたはお知らせください……

現状の BLE 接続の市販キーボード

市販のBluetoothキーボードの大半は 3.0 です。BLE にしてもそんな意味ないですしね。

Designer Bluetooth® Desktop (デザイナー Bluetooth® デスクトップ)

マイクロソフト デザイナー Bluetooth デスクトップ 7N9-00023 : ワイヤレス キーボード マウス セット 長時間バッテリー Bluetooth ( ブラック ) - マイクロソフト

マイクロソフト

3.0 / 5.0

高いうえに日本語配列なので買う気はしないんだけど、評判が良い。そして、スペックシートがすごいしっかりしてて

  • Nordic nRF51822 (Bluetooth Low Energy)
  • Bluetooth Profile Support HID Over Gatt Profile (HOGP)
  • Keyboard: Up to 12 months typical ( 2 AAA alkaline batteries)

と書いてある。使用条件が書いてないのでなんともいえないけど、ディープスリープも実装されてそう。

Universal Foldable Keyboard (ユニバーサル フォルダブル キーボード)

マイクロソフト 薄型キーボード Bluetooth対応/ワイヤレス/折りたたみ Windows/Androidタブレット/iPad, iPhone対応 Universal Foldable Keyboard GU5-00014 - マイクロソフト

マイクロソフト

3.0 / 5.0

これも高い…… Bluetooth 4.0 の HID 接続って書いてあるので、おそらく HOGP だけど、スペックシートには詳しく書いてない。

  • Rechargeable 3.7V 165mAh (min.) Lithium battery
  • 3 months typical

OS X でもペアリング可能っぽい。

英語配列なら買ってみてもいいかと思ったけど、国内だと英語版は売ってない。まじひどい。「製品のイメージは英語版です。実際の製品は日本語のキー配列となります」とか書いてあるので写真に騙されないように。

ただ、技適は国際モデルで共通のようで、国内でも相互承認(MRA)による工事設計認証で認証が通っており、製品にも技適マークがある模様。なので輸入さえすれば法的にも問題なく使えるっぽい。

マイクロソフトの製品の技適検索は数字しかないからわかりにくい。技術データシートに Model
number: 1695, Universal Foldable Keyboard. FCC ID: C3K1695 とか書いてあって、モデル番号がわかる。

Amazon.com だと現時点で$53だった。どうせ日本に発送しないんだろ、、と思ったらちゃんと発送してくれる。送料は最低で$6。結構いいんじゃないか?

Apple Magic Keyboard 2

Apple Magic Keyboard (US配列) MLA22LL/A - アップル

アップル

2.0 / 5.0

まじで高い…… BLE らしいけど、詳細は書いてない。

  1. トップ
  2. tech
  3. BLE Nano (nRF51) HOGP で接続中のアイドル電流

必要なもの

arm-none-eabi-* とsrecord が必要。platformio を使ってるなら arm-none-eabi は ~/.platformio/packages/toolchain-gccarmnoneeabi/bin/ に入ってるので、パスを通すか Makefile を修正すればいい。

srecord は brew からインストールできる。

brew install srecord

エクスポートと make

"GCC (ARM Embedded)" でエクスポートする。

とりあえず make してみると、リンク以外はうまくいった。

vfprintf.c:(.text.__ssputs_r+0x46): undefined reference to `__wrap__malloc_r'
vfprintf.c:(.text.__ssputs_r+0x66): undefined reference to `__wrap__realloc_r'
vfprintf.c:(.text.__ssputs_r+0x72): undefined reference to `__wrap__free_r'

と言われてリンクできなかった。

Makefile のうち -Wl,--wrap,_malloc_r -Wl,--wrap,_free_r -Wl,--wrap,_realloc_r を消せばうまくいった。

Makefile は標準的な mbed とリンクしていることを想定していて、うまくいかないところがある。

mbed の代わりに mbed-dev をインポートしている場合以下の書きかえが必要

# SOFTDEVICE = mbed/TARGET_RBLAB_BLENANO/TARGET_NORDIC/TARGET_MCU_NRF51822/Lib/s130_nrf51822_1_0_0/s130_nrf51_1.0.0_softdevice.hex
SOFTDEVICE = ../mbed-dev/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/Lib/s130_nrf51822_1_0_0/s130_nrf51_1.0.0_softdevice.hex

これで、make merge すると .build/combined.hex ができる。これを書きこめば使える。

メモリを32kb使えるようにする

mbed のオンラインコンパイラは BLE Nano v1.5 に対応しておらず、常に RAM を 16kb でしか使えない。実際にアプリケーションで使えるのは6kb程度となっていて、おそろしくキツい。

エクスポートしたらやりたいほうだいなので、リンカスクリプトを以下にように書きかえる。

--- mbed-dev/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/TOOLCHAIN_GCC_ARM/TARGET_MCU_NRF51_16K_S130/NRF51822.ld	2016-09-01 13:30:12.000000000 +0900
+++ NRF51822.ld	2016-09-01 22:39:00.000000000 +0900
@@ -3,7 +3,7 @@
 MEMORY
 {
   FLASH (rx) : ORIGIN = 0x0001C000, LENGTH = 0x24000
-  RAM (rwx) :  ORIGIN = 0x20002800, LENGTH = 0x1800
+  RAM (rwx) :  ORIGIN = 0x20002800, LENGTH = 0x5800
 }
 
 OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

メモリ量チェック用に main の最初にこんなのを書いている。

	{
		const uint32_t reason = NRF_POWER->RESETREAS;
		NRF_POWER->RESETREAS = 0xffffffff; // clear reason
		// reset cause should be shown everytime
		serial.printf("init [%x] sp:%x\r\n", reason, GET_SP());
	}

以下にようになった

# LD スクリプト変更前
init [4] sp:20003fd0
# LD スクリプト変更後
init [4] sp:20007fd0

16kb のときメモリの最後は 0x20004000、32kb のときメモリの最後は 0x20008000 になるはずなので、main の最初の sp がこの値なら問題なく 32kb 使えているはず。

オンラインコンパイラでなんとかできないか

試行錯誤したけどダメだった。ARM は 0x00000000 から読んで MSP として初期化されるので、ここを書きかえれば初期スタックポインタの位置を移動できるはずだけど、生成された hex ファイルから該当部分だけを書きかえてもダメだった。

書きかえてブートさせてみると main までこないので、何か完全にハズれているらしい。リンカの段階でほかにも値を計算して書いてるのかもしれない。

  1. トップ
  2. tech
  3. BLE Nano のオンラインプロジェクトをエクスポートして GCC でコンパイルして RAM 32kB 使えるようにする

ぐっすり寝てると犬に噛まれて殺される。

BLE で接続を維持しつつ、waitForEvent ( sd_app_evt_wait() ) でイベントが起きるまで寝ているケースで、予期せず Watch Dog Timer が発動するという罠にひっかかったので共有いたします。

前提

  • SoftDevice を使って BLE 接続を確立している
  • sd_app_evt_wait() してアプリケーションイベントをずっと待っている
  • WDT を SLEEP 時には PAUSE になるように設定している
  • 途中でアプリケーションの割り込みが入らない (タイマーとかを使っていない)

Pause する設定というのは以下のようなことです。

NRF_WDT->CONFIG = WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos;

予期しないWDTのタイムアウト

SLEEP 時には WDT を止まるようにしていて、アプリケーションは sd_app_evt_wait() で寝ています。よってWDTは働かないことを期待しましたが、本来の WDT の設定時間よりも遥かに長い時間を経過したあと、WDT によりアプリケーションがリセットされました。

原因

結論からいうと sd_app_evt_wait() で寝ていても、SoftDevice は無線アクティビティのために極めて短時間ですが CPU を起こして活動しているため、WDT の設定の「SLEEP 中は PAUSE」の状況にあてはまらない時間が発生します。おかげで、長い時間をかけて本来のタイムアウトに近付き、WDT がタイムアウトします。

対策

アプリケーションレベルでも、十分に短い間隔で割り込みを発生させて、明示的に WDT をリセットする。

普通はメインループで WDT のリセットを書いてますから、単に sd_app_evt_wait() の直前にワンショットなタイマーをかけて一定時間後に割り込みをかければ、割り込み関数で何もしなくても目的を達成できます。


今回3秒のタイムアウトを設定していましたが、このケースで実際に WDT が発生するまでには10分〜30分以上の時間 (無線アクティビティの頻度による) かかりました。なのでかなり安全目にふって1分ごとに起きてWDTをリセットするようなコードにしたところ、問題が発生することはなくなりました。

ケースが限定されていますが非常に見つけにくいバグだと思いました。悩みすぎて死ぬかと思った。

備考:WDT は一度動くと止められない

一度 TASKS_START = 1 すると止められません。STOP するタスクはありません。

RREN を一時的に 0 にすれば止まるかと思いましたが無理です。

The watchdog must be configured before it is started. After it is started, the watchdog’s configuration registers, which comprises registers CRV, RREN, and CONFIG, will be blocked for further configuration.

備考:WDT のタイムアウトのカウンタはとれない

WDT のカウンタがどれぐらいすすんだか? を調べたかったのですが、取得する方法がないようです。

  1. トップ
  2. tech
  3. BLE Nano (nRF51822)、waitForEvent ( sd_app_evt_wait() ) 中に予期せず WDT が発動する場合

500 Can't connect to lowreal.net:443 (certificate verify failed)

まず思いつく現状の不満とかを考えて、それを盛りこんで作ろう、という方針を決めた。つまり

  • 左右分割にしたいなあ
  • UNIX 配列準拠がいいなあ
  • Bluetooth がいいなあ

あたりが最初の方針になる。


できるだけ MacBook 標準のキーボードでも十分開発できるぐらいの状態を保ちたいので、奇抜なキー配置のものは例え身体に良くても精神に悪いので使いたくない。

市販品で近いものを探すと、Kinesis freestyle2 あたりがイメージに近い。これをキーリマッパでカスタマイズすれば実用的にはいいかもなとは思った。

 -

3.0 / 5.0

あと、普通の(普通ってなんだ?)左右分割キーボードの内側のキーにも不満がある。左右分離の場合、タッチタイピングで左手に所属するキーを右手では決して打てなくなる。

しかしゆるふわタッチタイパー的には、中央付近のキーは右手でも左手でも打ちたい。つまりキーをオーバーラップさせたいが、そんなキーボードはこの世に存在していない。僕は「タッチタイピングの矯正」とか別にしたくなくて、ゆるふわでいきたい。

ErgoDox の設計の分析

まずErgoDox はキー数が少なくて UNIX 準拠にはできない。キー配列の画像を見ればわかると思うが、右側にキーが足りない。例えば HHKB なら P の右には [ と ] と DELETE があり3つのキーが続くが、ErgoDox だと1つしかキーがないので無理。ソフトウェア的になんとかするしかない。

なお ErgoDox は設計段階で左右の基板が共通となっている。両面基板をリバーシブルに使うことで両手に対応させている。これにより基板の製造コストはかなり減る (特に小ロットで外注する場合は半額にできる)。ただし、設計には左右対象であるという制約がつく。

一般的なキーボードは右手担当のキーが多く、左右非対称になっている。左右対象という制約をつけると、一般的なキーボードからかなり離れることになる。これはエルゴノミクス的には正しい気はするがよくわからない。ただ、キーが少ないのは現実的に不便だと思う。

余談だけど、右側のキーが多いことも考えると世の中のキーボードは右利き用ではないかと思った。左利きはキーボードにおいても不利を強いられていないだろうか? (僕は右利きなのでそんなこと思いもよらなかったんだけど)

仕様

そういったことを踏まえて自作するキーボードの以下のように仕様とした。

キー配列の仕様

  • UNIX キーボードを2分割した形を基本にする。つまり HHKB とほぼ同じで、Ctrl キーはAの左、ESC は 1 の左など。
  • 矢印キーはどうしても欲しい (HHKB への大きな不満のひとつ)
  • F1〜F12キーもできれば欲しい (HHKBへの小さな不満のひとつ)
  • 中央のキーを1列分オーバーラップさせる
レイアウトの詳細


HHKB からの変更点

  • スペースキーが2分割に
  • F1〜F6, F7〜F12 キーの新設
  • 矢印キーの新設

まず「ぼくのかんがえたさいきょうのキーボードはいれつ」を Inkscape で書いてレイアウトを検証した。軽く書くというよりは CAD 的にちゃんと書いた。のちのち Inkscape からエクスポートして KiCAD で読みたかったため。

備考

  • 僕は右手のスペースキーをほぼ全く使わないので、実質飾りである。
  • Caps Lock キー? そんなものはない。写真にある Caps Lock は Shift キー(キーが足りなかったので)
  • 矢印キーを置くところがなくて右手親指付近に置いている (が、これはちょっと邪魔だった。あと1キー分左にずれていたほうがいい…)

技術仕様

同時に、いくつかこうしたいという仕様も決めた

  • NiMH 単3バッテリー2本で半年ぐらい持つ (普通の製品ぐらい持つこと)
  • 左右ボード間の接続は有線
  • USB に逃げたくなったとき逃げられるようにしておく
    • 制御用のボードは分離して設計すること

一方、躯体については殆ど考えてなくて、いきあたりばったりで基板を作ってから3層構造にした。製作編で詳しく書く。

なお省電力であればあるほどいいけど、そこそこ電波が出るデバイスなので、BLE が生きるぐらいの省電力にはならないだろうという気はしてた。

左右のキーボード間も無線にしたかったが、セキュアにしようと思うと BLE Nano 2台でそれぞれHIDキーボードにしてしまうのが楽ということになってしまう。今回はそういうことはしたくなかったので、ここは有線で妥協した。

ただ、左右どちらも回路構成が完全に一緒なので、単に制御用の基板を2つ用意して取り付ければそれぞれHIDキーボードとして使うことができるようにはした。

つづく → 500 Can't connect to lowreal.net:443 (certificate verify failed)

  1. トップ
  2. tech
  3. 自作キーボードの製作 — コンセンプトとキーレイアウトおよび技術仕様編

ErgoDox ではないナニか。オープンソースかつ Bluetooth 接続のキーボード | tech - 氾濫原

回路設計

仕様とともに回路の設計もしはじめた。単にスイッチがいっぱいついてるだけなので、回路的には難しいところは一切ないといってもよい。問題はPCとのインターフェイス部分になる。

キースイッチ

キースイッチは Cherry MX 系がいいなと思った結果 Gateron (Cherry MX 互換) にした。

  • 安い
  • 品質もそれほど悪くはない (というか ErgoDox Lite に採用されてる)
  • 入手性が良い (いろんな色が ebay 経由で買える状態だった)

あたりで決定した。

BLE Nano

Bluetooth 接続したいので、技適を通っている Bluetooth モジュールを考える。ちょうど BLE Nano という製品が技適を通っていて mbed 対応だったため、これをメインMCUとして使用することとした。 mbed で公式にサポートされているUSBボードと比べて圧倒的に小さく、ライターとともに購入してもなお安いというのもあった。ただし BLE Nano は名前の通り Blueooth LE (4.0) のモジュールなので、多少の不安はあった。

I2C GPIO MCP23017

キーマトリクスのために I2C GPIO IC を使うことにした。これはMCP23017という Microchip 社のものとした。BLE Nano の GPIO は少ないので、左右で1つずつの MCP23017 を使い、この2つを同一の I2C バスに接続する。MCP23017は3bit分スレーブアドレスを変えられるようになっているので、2つのアドレスをわける。ちなみに ErgoDox にも MCP23018 というほぼ同じものが使われている。MCP23017 は秋月で取り扱いがある。

消費電力削減のためハード設計の段階で MCP23017 の割込み生成を使うこととした。MCP23017 側でピン変更を検知した場合 INT ピンを通じて BLE Nano 側に割込みを発生させる。MCP23017 は2つ使うが、割込み生成は1つで良いので、割込みピンを BLE Nano 側でプルアップし、2つのMCP23017はオープンドレインで割込みを生成するように設定する。

割り込みを使わない場合、常時I2Cで通信してキーの状態を調べなければならないが、割り込みを使えば何も変化がないときにはデバイス全体をほぼ完全にスリープさせることができる。BLEによる無線接続は必然的にバッテリ駆動となるので、これは必要だと思った。

左右キーボード間の接続コネクタ

左右分割キーボード間の接続は

  • SCL
  • SCK
  • VDD
  • GND
  • INT

と5ピン必要になった。しかし

  • 5ピン以上
  • 小型・
  • 対称・
  • ケーブルの入手性が良い

あたりを満たすコネクタというのが壊滅的に存在しない。4ピンであれば ErgoDox と同様にTRRSコネクタ(4端子ミニプラグ)が使えるが、INT ピンを入れて5ピンにするのは譲れない仕様なため困難が生じた。VDD を INT と併用するという考えも浮かんだが、キー割込みはI2C経由で読むまで解除されないので厳しいと思った。

結局のところはLANコネクタ(8P8C・RJ45)とした。USB Type-C コネクタがナウいので、可能なら使いたいと思ったが、現状では入手性が悪く、ケーブルも高価なのでやめた。LANコネクタは比較的大きいということ以外は要件を満たしている。ケーブルも安いので優秀なコネクタだと思う。

回路図やボードレイアウトの作成

Eagle だとフリー版にはボードサイズに制限があってキーボードぐらいの大きさのものは作れないので、いよいよ KiCAD に移行することとした。なので KiCAD の使いかたレベルから学習する必要があった。これについては別途エントリを書いてきた。

最初とても辛かったが、なんとか慣れてきた。いままで Eagle でやっていたことをすべて KiCAD で可能にするためにかなりヤックシェイビング的なことをした。特にPCB Millingまわりの環境再構築が大変だった。

最終的には pcb2gcode にパッチ送ったりしてなんとかなる環境をつくった。

回路図



Root.sch / Main.sch / KeyModule-L.sch / KeyModule-R.sch となっている。これはKiCAD で複数ボードプロジェクトを運用する
に詳細を書いた。

左・右のそれぞれのキーマトリクスは BLE によるファームウェアの部分と独立させたかった (つまりBLEではなくUSB接続とした場合でも基板自体の変更がいらないようにしたかった) のでメインの制御基板を別にしてある。

ボードレイアウト



Inkscape から左と右にわけてレイアウトを DXF ファイルを出力し、pcbnew にインポートした。キーの配置はこの状態から DXF のガイドにあわせて手動で移動させて行なった。

レイアウトの仕様は以下のようにした

  • PCB Milling 前提
    • 少しでも作りやすくするため、デザインルールで最小幅を0.3mmに
  • 基本1層基板 (裏面配線のみ)
  • ジャンパ部分は極力わかりやすくする

結構大きな基板になるので格安PCBサービスでも結構高額になってしまう。なので手元でできる PCB Milling (CNCフライスによる基板切削)でやることにした。

しかしこの制約はかなり厳しくて、なかなかレイアウトが決まらなかった。両面にすればよかったが、基板作成の手順が倍以上になるのでそうしなかった。

試行錯誤してオートルートさせてなんとかした。ジャンパなしはできなかったので、ある程度ジャンパを許容してオートルートさせてから、自力でジャンパしやすいように、直線にしたり100mil単位になるようにとかと修正を加えた。

基板の製作



左側

まず先に左だけ作って様子を見ることにした。左側だけでもブレッドボードに組んだ制御基板と配線すれば使うことができるため、出来を確かめることができる。

余談だけど、既存キーボードと併用して Karabiner を使えば実際に左右分割で使えるはず、と思ったが、どうもそれができなかった。修飾キーの共有が BLE キーボードだとうまくいかないみたいだった。Karabiner からもキーボードデバイスとして認識されず謎。これは未だ謎。入力はできてるけどキーボードデバイスとして認識されてない。


それはともかく、使ってみると結構基板がたわんでしまうことがわかったので、下側のプレートにナットだけを増やして力を受けるようにした。キースイッチの実装面には隙間がほとんどないので、裏側から短いビスでナットを止めて隙間を埋めて力をうける構造になっている。


最終的な構造をいまいち考えれてなかったが、結局3層を単純に重ねる形とした。つまり、化粧板・基板・裏板を15mmのビスで共締めしている。

化粧板は特に強度が必要ないので1mm、裏板には多少強度がいるので2mmとした。結果として以下のような層になった。

  • 化粧板 1mm
  • ナット 5mm
  • 基板 1.6mm
  • ナット 3mm
  • 裏板 2mm
  • ナット3mm

できるだけ薄くて机にキーが近いほうがいいけど、このぐらいが限界だった。

化粧板と裏板の切り出し

いずれも外形カットと穴開けが必要だったため、基板と同様にCNCフライスで切り出している。

この際、CAD は KiCAD の pcbnew をそのまま流用した。KiCAD のユーザー定義レイヤーが2層あるので、ここに切削ラインを書いて、ガーバーに書き出し、外形レイヤー扱いで pcb2gcode で gcode 化した。

ただし、pcb2gcode は現時点では「外形カット」しかできないので、内側に書いたラインも外側を削る挙動になってしまう。pcb2gcode 側に対応をいれたかったが面倒なので、図面の段階でエンドミル径分を考慮して線をひいた。

右側

左側を作った結果をうけていろいろ改良を加えて右側を作った。

以下らへんを改良

  • 角丸に
  • 固定用のビス穴を大幅に増やした

やっぱ角丸にしないとこういうのはきついよなと思った。左側はあとからリューターで削って角丸にしたけど、右側は切削時点で角丸になるように外形を描きなおした。

固定用の穴は多めにあったほうが圧倒的にしっかりするのでケチらないで開けたほうが良い。実際、右側は左側と比べてかなりしっかりした構造にできた。

なお右側は左側に比べてキーの数がかなり多く、基板のサイズもぎりぎりになっている。配線もこちらのほうが大変。基板の原点をちょっと適当に設定しまって、基板は上側が実は足りてない(配線的には問題なし)

作ってみて

これで左右が揃ったので、ファームウェアのクオリティはともかく、ちゃんと打てるようになった。

基本的に HHKB と完全に一緒のキー配置なので、ほとんど違和感なく使える。ただ、物理カールキーをつけたのに関わらず、HHKB のカーソルキー移動に思ったよりも慣れていることが発覚した。Fn キー相当の位置に未割当キーはあるので、ファームウェアで対応した。

カーソルキーを制限のあるなか、なんとか配置したのはよかったが、思ったよりも右手の親指と干渉してしまった。あと1キー分左にあったほうが良いかもしれないけど、ぎりぎりすぎる。右のスペースキーは僕は使うことが全くなくて飾りなので、そこに別のキーを配置してもいいかもしれない。

なお自作する場合、CNCフライスの加工限界によって基板のサイズに制限があり、左右分割でないとそもそも作るのが難しいというのを感じた。

あと細くて短いLANケーブルがなかなかなくて難儀する。多少長くても細いほうが取り回しはよさそう。あんまり長いケーブルだとI2Cのドライブ範囲を超えるのでバグりやすくなる。

つづく → 自作キーボードの製作 — ファームウェアの実装編 | tech - 氾濫原

  1. トップ
  2. tech
  3. 自作キーボードの製作 — 回路設計とアートワーク・ハードの製作編

500 Can't connect to lowreal.net:443 (certificate verify failed)

とにかくこれが一番大変だった。BLE + mbed + HID キーボードでちゃんと動くもののサンプルっていうのが見つからないので、そこそこ動くサンプルから頑張らないといけない。

ほとんど作り終わってから気付いたけど先に言っておくと、mbed のオンラインコンパイラ環境で BLE Nano の開発をするのは筋が悪い。RedBear も Keil 使って Nordic SDK 直接使って開発しろみたいなことを言ってる。ただ、今回はどうしても、キーカスタマイズとかのために開発環境をゴリゴリ用意するというのが嫌だったので、オンラインコンパイラに拘った。

依存と罠

まず、現状の mbed ライブラリと nRF のSDK のどちらにもあるヘッダファイルが競合していて、まともにコンパイルできなかった。mbed を依存からはずして、mbed-dev をインポートして、該当するヘッダファイルをリネームして対応した。しょっぱなからひどい目にあった。

BLE_HID というライブラリもあって「お、これいいじゃん」という感じだけど、500 Can't connect to lowreal.net:443 (certificate verify failed) にある通り、さっぱりペアリングできなくてハマった。

BLE_HID は結局中の構造をいじることが多かったので、プロジェクト内にファイルをコピーして依存から消している (競合するので)。

セキュリティまわり

これまたハマりまくるポイントで困る。上記のようにそもそもペアリングできないというのもあったし、普通に使えるレベルとするにはペアリング情報を覚えてて自動再接続してほしいところだけど、どうやるかさっぱりわからなかった。

結論からいうとセキュリティまわりの処理が終わったら該当デバイスをホワイトリストに入れれば良い。しかし BLE_API のホワイトリストまわり、experimental 扱いになっていて不穏な空気を感じる。

BLE と OS X

まず、よくわからないけど OS X が BLE キーボードをキーボードとしてちゃんと認識しない (具体的にはアイコンが汎用デバイス扱いになるし、バッテリーステータスもちゃんと表示されない。ちゃんと GATT で提供してるのに……)

ただ、ペアリングするとちゃんと入力できるので対応されてないわけではない。

あと、Bluetooth の環境設定が頻繁に固まる。ジョブズが生きていればこんなことには……

Apple Magic Keyboard (US配列) MLA22LL/A - アップル

アップル

2.0 / 5.0

Apple Magic Keyboard (Wireless Keyboard ではなく) は BLE 接続らしいけど、どうなってるのかよくわからない。DeviceInformationService で PnP ID を偽称してみたりしてみたけど、うまく認識しなかった。このキーボード、思いのほか高価なので試しに買ってみて調べるみたいなこともできずモヤモヤした。

BLE と Windows

Windows とのペアリングは非常にスムーズで問題がなかった。ちゃんとキーボードとして認識されるし、OS X よりマトモ。ただ Windows でもバッテリーステータスを確認する方法がわからなかった。

BLE と Android

さすがにスマフォは BLE の対応がしっかりしていて、まともにペアリングできるし、ちゃんと使える。

nRF Connect で GATT 情報を見ると OS X 上で LightBlue を使って見るよりも多くの情報を得られる。BLE に関してはスマフォも活用してデバッグしたほうが捗る。

特にハマったところ

別途エントリに起こしたけど、ここらへんでそれぞれハマってる。

あとエントリに起こしていないけど、とにかくメモリが足りなくて苦労する。現状の BLE Nano (v1.5) は 32kb RAM があるのだけど、mbed がサポートしておらず、16kb しか使えない。そして SoftDevice とかの使用を除いて 6kb ぐらいしかアプリケーションが使えるメモリ量がない。この中で抽象化された mbed の BLE_API を使うと本当にすぐにメモリが枯渇する。

メモリが枯渇してもハードフォルトでランダムに stuck したりする感じなので、おそろしく原因究明が難しい。ヒープが上書きされても致命的でなければ動き続けるわけだし、ヒープの正確な使用容量を知るよしはないし、SoftDevice の割込みのために一定量のスタックメモリも残しておかなければならないしで、とにかくつらい。32kb になれば余裕になるので、さっさと 32kb 対応してほしい。

現状のメモリ量だと、たとえばマウスをサービスしたいと思っても不可能だと思う。

消費電力の削減


最初にやったのはこれだった。製品に近いぐらいにはしたいという気持ちがあったのと、消費電力削減はテスターで見てすぐ効果が数値でわかるので好きというのもある。

別途エントリにした 16MHz HFCLK をオフにするというのが一番支配的だったけど、他にも細かいところに気をつかったつもりではある。

必要ないペリフェラルはできるだけ止めているつもり。UART だけデバッグの都合でスリープに入る前後のタイミングで止めてからスリープに入って起きたら再開するというコードになっている。ただし、UART も RX は一切行わないので止めている。

また、無接続ピンもプルアップして中途半端な状態にならないようにしている。中途半端な電位になると、他のところとの電位差で無駄に電流が流れたりするので気をつける必要がある。これは直接レジスタをいじっていて、mbed の DigitalIn とかは使ってない。メモリ節約のため。

キー入力間でチャタリング防止のつもりで5msのインターバルをいれているけど、これも安易に wait_us(5000) とかにせず、Timeout による割込みをしかけて waitForEvent して寝るという実装にしてある。CPU をスリープさせない限り消費電力は減らないので、起きている時間をとにかく短くしたい。

起きている時間をとにかく短く、という視点で、I2C の速度を 250khz に上げてある。100khz よりも2.5倍早いので、I2Cで消費する電力は半減ぐらいになる。ただ、電源電圧が低いので速度をあげると安定性が失われる。オシロで波形を見ると結構なまっている。

BLE のコネクションパラメータで一番効果が高いのは slave latency だった。このパラメータは、ホストからのリクエストを一定個数無視するというもので、スリープ中の消費電力を結構減らせる。

一方、空中線電力は下げてもそれほど効果がなかった。コンスタントに消費はあるが、極めて短時間なので平均的には支配率が低いように感じた。

安定性改善

前述したが特定の変数を宣言するだけで stuck するみたいな挙動で原因がわからずとても苦労した。結局メモリ不足でヒープが上書きくらってたみたいだけど、そんなに使ってるつもりないのに死ぬのでなかなかメモリ量が問題と思いあたらなかった。

また MIC エラーもとにかくハマっていた。メモリ使いすぎの件があったので、これもそういう系なのだと思って原因をさがしていたが、そうではなかった。500 Can't connect to lowreal.net:443 (certificate verify failed)

レイヤー

めんどくさくて最初は実装してなかったけど、思いのほかHHKB式のカーソルキーにも慣れていることがわかって、仕方なしに実装した。

レイヤーの部分はいちいち実機デバッグするのが面倒だったので、ユニットテストを雑に書いてある。

バッテリーレベルを mV で見れるようにする

カスタムUUIDを定義して、mV 単位のバッテリーレベルもとれるようにした。カスタムUUIDやってみたかったのと、やっぱ % 単位で見れても実際どうなのか不安なので……

まとめ

まだちょろちょろいじってはいるけど、ほぼ安定した。「もうこれ安定しないんじゃないか」「USB接続に方針を変えたほうがいいんじゃないか」と思うこともあったが、なんとかなったと思う。

  1. トップ
  2. tech
  3. 自作キーボードの製作 — ファームウェアの実装編

ポリスチレンはカッターで切れるのと、プラモデル用の接着剤が使えるのが良い点だなあ。

ASUS ZenFone 2 の Android M (6.0) アップグレードは遅延 | tech - 氾濫原

遅延していた OS アップデートですがようやくきました。8月31日付けで配信開始したようです。リリースノート

ただし WW のみ。JP はいつ……

  1. トップ
  2. tech
  3. ZenFone2 にようやく Android M (Mashmallow) アップデートが降ってきた

ほかにもやりたいことがあるのだが、ここ数ヶ月やってることがなかなか「これでよし」とならなくて、とりかかれない。ぶっちゃけそろそろ飽きてきたのでさっさと日記に書きだして一旦おわりにしたい。

ここ数ヶ月ぐらいキーボードを作っていた。そのためにいろいろ yak-shaving としかいいようがないことも多々していた。

いろいろ書くことが多いので、細かい設計などについては別途エントリを分ける。

  • コンセンプトとキーレイアウトおよび技術仕様の決定
  • 回路設計とアートワーク・実際の製作
  • ファームウェアの実装

あたりをそれぞれ別途詳細なエントリを書く。だいたいの人は細かいことはどうでもいいと思うので、概要のみこのエントリにまとめる。

コンセプトや特長

UNIX ベースのキーレイアウト (というかHHKBをベース) とし、違和感なしに分割キーボードとする。

キー配列

  • UNIX キーボードを2分割した形を基本にする。つまり HHKB とほぼ同じで、Ctrl キーはAの左、ESC は 1 の左など。
  • 矢印キーはどうしても欲しい (HHKB への大きな不満のひとつ)
  • F1〜F12キーもできれば欲しい (HHKBへの小さな不満のひとつ)
  • 中央のキーを1列分オーバーラップさせる (ゆるふわタッチタイピングに必要)

特長

  • Bluetooth 4.0 (Bluetooth LE) HID over GATT 接続。Windows と OS X で接続可能
  • Bluetooth 経由によるファームウェアアップデート
  • mbed オンラインコンパイラによるファームウェア開発環境
  • 全てオープンソース (MIT)
  • 1900mAh の NiMH 充電池で約6ヶ月のバッテリーライフ
    • キー入力時 3.3mA、非アクティブ時10uA〜100uA(OSの挙動による)

FOTA/DFU (Firmware On The Air / Device Firmware Update) かつオンラインコンパイラによりキーカスタマイズのために環境構築が不必要。あるいは MK20 による書きこみなので、3ピンの配線でUSBからマスストレージクラス経由で書きこみ可能。

技術的な仕様

UNIX配列のBluetoothキーボードというのが希少で前々から欲しいと思っていたので、接続インターフェイスは Bluetooth としてみた。

インターフェイスとして RedBear BLE Nano というのを使うことにした。国内技適にも通っており結構安い。mbed が開発環境に使える。名前の通り BLE (Bluetooth 4.0) 接続になる。ただ、なんというか、この選択(無線化)は割と悪夢の始まりだった。

回路設計


BLE Nano はピン数が少ないため、I2C GPIO 拡張の MCP23017 を2つ使っている。消費電力削減のため GPIO の割込みを活用している。キーの部分は単にキーマトリクスなので特におもしろいところはない。

ステータスLEDを1つだけつけている。これはペアリングステータスを示すため。キーボードにLEDあっても見ることないし消費電力の無駄なのでほとんど消灯させておく。

基板設計

KiCAD のプロジェクトは github に置いてあります。https://github.com/cho45/Keble

PCB Milling (CNCフライスによる基板切削) でやることを前提としたので、片面基板+最低限のジャンパで構成した。

複雑ではないが配線数は多いので、片面という制約をつけると結構厳しい。がオートルータでなんとかなった。製作しやすくするため、デザインルールで最小幅を0.3mmとした。

製作

とりあえず左を作ってファームウェアと共に実装を検証し、それから右側を作った。そのため、右に比べると左側のクオリティが明らかに低い。

設計がちゃんとできている前提で、無心ではんだ付けをするだけ。振動とたわみの負荷がSMDにかかるのがなんとなく嫌で全てリード部品としたため、特に難しいところはない。

基板以外に、バックプレート(2mm)とフロントプレート(1mm)をさらにプラ版から切り出している。なので、3層構造になっている。側面はない。

ファームウェアの実装

mbed レポジトリ

mercurial:

hg clone https://developer.mbed.org/users/cho45/code/keyboard/

前述の通りだけど、今回は mbed のオンラインコンパイラで全て実装した。BLE Nano + mbed でセキュアペアリングして HID デバイスとして動かす例なんてのは例がさっぱりなくて大変苦労した。

HID キーボードとして簡単に動かすぐらいまでは、既にやってる人がいるので難しくない。しかし、実用キーボードとして安定して動かすようにするまでがかなり辛かった。

70%ぐらいの完成度まではすぐできるけど、そこから90%ぐらいまで完成度を上げるには大変な労力がいる。ハマったポイントが蓄積されていて、使えるぐらいに安定して動くコードがある状態にできたので、まぁ良かった…… ハードウェアよりもファームウェアの実装の知見のほうが遥かに価値があると思う……

基本的に mbed 環境でがんばるってのが筋が悪いのだけど、なんとなくオンラインコンパイラにこだわって意固地になって苦労している感じ。

現状の実装クオリティ

主観的には90%といったところと思ってる。温度感としては「ブログ書くぐらいなら全く問題がなく、仕事で使うのにもまぁまぁ使える」ぐらい。

仕事で1日使ったところ、数回再接続(約5〜10秒ぐらい)が必要になったのと、1度完全に刺さった(WDTで復帰せず、リセットで復帰)。0dBmで送信しているので、普通の距離なら物理要因で接続が切れることはないと思うので、他の再接続もファームウェアのバグだと思うが原因不明。

自分の主観的には割と稀ぐらいまできて普通に使える、バリバリ集中してコード書きまくる人だとイライラするかもしれない。

キーマップが HHKB と同じ (Fnキーによる矢印キーも実装してある。レイヤーってやつ)なので、基本的にどっちも全く違和感なく使える。

部品とコスト

  • BLE Nano Kit: 3300円 (Red Bear Store)
  • Gateron Brown キー 108 セット: 3300円 (ebay) 余ります
  • キーキャップ 104 セット: 5700円 (ebay) 余ります (無刻印・メタキー用)
  • キーキャップ 104 セット: 3500円 (AliExpress) 余ります (有刻印)
  • 生基板 1.6×150×250 2枚: 1000円 (monotaro)
  • 1mm プラバン B00CF9RSTU : 540円 (Amazon)
  • 2mm プラバン B001Q0ZHTW : 550円 (Amazon)
  • ビス・ナットなど 500円ぐらい

キーキャップ2セット買ってるがメタキー用の無刻印のものと通常キーの刻印ありのものを分けたかったからで、1セット+必要なキーだけとかならもっと安くはできるはず。いずれにせよキーキャップの原価支配率が高い。これだけ買ってるのに右シフトキーサイズのキーが Caps Lock しかなくて、しかたなく代用している。

基板切削を自力でやっているので注意がいる。もし基板を外注するなら、左右別のデザインにするとサイズ的に5枚組み1万ぐらいはかかるはず。とはいえ、外注しても2〜3万ぐらいの原価なので、趣味で作るのはまぁまぁ現実的といえる。基板をシェアするともだち(笑)がいるならもうちょっと安くて楽をできる。(趣味で、というのは自分の作業コストをゼロとして見積るという意味です)

製作期間

ヤパチーで ErgoDox を見て触発されて、ErgoDox について調べた(買わないけど) | tech - 氾濫原 このエントリを書いた時点でさいきょうのキーボード自作の実現性について考えていたので、そこから約1ヶ月半ぐらい。ebay や aliexpress が部品調達のメインで、かなり待ち時間があるので、実働は1ヶ月ぐらいかな。休日も平日も夜中にしか開発できないので、やる気があれば半月ぐらいで形になりそう。主観的には(大変すぎて)3ヶ月ぐらいずっとやってるつもりだったけど、案外早くできたっぽい……

ぶっちゃけ1ヶ月ぐらいじっくり取り組むと飽きる。

現時点での感想

キーボード自作は結構面白い。というか奥が深いと感じる。自分が一番よく触るインターフェイスを自分である程度作れるというのは満足度が高い。

いきなりイチから盛り盛りで作ったわりには良くできたと思うが、特にハードウェア部分は製品レベルではない。自分で作って自分で使うぶんには十分といえる。

「さいきょうのきーぼーど」追い求めると沼にハマるので、ほどほどにしたほうがよさそう。

フルキーボード作るのは結構コストがかかるので、既存のキーボードと組合せる前提で、カーソルキーだけとか、ファンクションキーだけ、みたいな無線キーボードなら安く作れて良さそう。そして十分実用にできると思われる。

  1. トップ
  2. tech
  3. ErgoDox ではないナニか。オープンソースかつ Bluetooth 接続のキーボード

BLE Nano は書きこみ器セットで購入しても $32.90 とかなりお得。BLE Nano Kit - Product 単体 なら $17.90。RedBear は香港みたい。公式の通販から最低送料のオプションで買っても割とすぐ届く。

ただのビーコンとして使うには高価に感じるかもしれないが、PCとの低消費電力無線通信デバイスでこの価格帯のものは殆どない。

そしてARM かつ、mbed を開発環境につかえる。なお Arduino からも書きこめる。ナイス。

載っている BLE モジュールも nRF51822 という界隈でデファクトスタンダートみたいなやつなので比較的情報が豊富。

そして小さい。小さいのは正義。その分IOは少ないが割となんとかなる。

  1. トップ
  2. tech
  3. なぜ BLE Nano にご執心なのか

0x3d はMessage Integrity Check (MIC)が失敗した、というエラーらしい。ホスト側で発生する。デバイス側から送られてきたメッセージのセキュリティチェックエラーのようだ。

ということで、デバイススタックの SoftDevice のバグでは? と思うところだけど、そうではないらしい。どうやら mbed と相性が悪いらしい。

解決の糸口

0x3d が出るのは最初はファームウェアのバグでどこかが stuck しているからだと思っていた。しかしどうも stuck していなくても 0x3d が起こることがある。で、そろそろ Nordic スタックのバグを疑ってみる。しかしそこのバグなら既にハマっている人がいるはずなのでググる。

すると Implement BLE security · Issue #44 · lancaster-university/microbit-dal · GitHub あたりに Nordic のバグじゃね? みたいな話がでてくる。そこからサブイシューがつくられている。


MIC failures observed with secure BLE · Issue #61 · lancaster-university/microbit-dal · GitHub

Problem is caused by mbed-classic disabling interrupts when a timer interrupt is triggered. This was too long for the underlying BLE stack to code with during critical radio events.

と原因と、さらに解決策がいくつか示されている。一番簡単なのが3つめなので、このプロジェクトでは3つめが採用されたっぽい。しかし肝心のコミットへのリンクはないので頑張って探す。

Merge branch 'secure-ble' · lancaster-university/microbit-dal@3c31479 · GitHub このコミットをつらつら眺めていくと、どうやら対策コードっぽいものが見つかった。

	// configure the stack to hold on to CPU during critical timing events.
 	// mbed-classic performs __disabe_irq calls in its timers, which can cause MIC failures 
 	// on secure BLE channels.
    ble_common_opt_radio_cpu_mutex_t opt;
    opt.enable = 1;
    sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);

ここで SoftDevice の API を呼んで、無線アクティビティがあるときはCPUを完全にブロックしてアプリケーションの実行を止めるらしい。簡単なコードだ。

コピペしてみるとエラーの発生がなくなった。まじ良かった……

備考:エラーのログ

Apple が提供している Bluetooth Explorer の Event Log を開きっぱなしにしておけば、接続情報について常にデバッグログに残る。

  1. トップ
  2. tech
  3. BLE Nano (nRF51) + mbed でセキュリティ付きペアリングをして 0x3d エラーがでる