背景

現在は 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 ですが)

サンワサプライ Bluetooth 4.0 USBアダプタ(class1) MM-BTUD43 -

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 にプロトコルスタックがあるので不要です。自分ではインストールしたことありません。かなり古いレビューがずっと残ってるので注意が必要です。

サンワサプライ Bluetooth 4.0 USBアダプタ(class1) MM-BTUD43 -

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 であることを表記しています。これも手元にあって、確認済みです。

Logitec Bluetooth USBアダプタ コンパクト Ver4.0 Class1 LBT-UAN04C1BK -

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 を書こうとして挫折した