2015年2月22日

[mbed]初mbedはNUCLEO-F411RE

スイッチサイエンスさんから、mbedを買った。
Cortex-M3は2つあるし、M0は1つあるので、M0+にするかM4にするか迷った末、M4にした。
ST Nucleo Board STM32F411RET6 - スイッチサイエンス
RS Onlineの方が本体価格は安かったんだけど、ブレッドボードもほしかったので。
一緒に、イーサネットをつなぐ部品も買った。まあ、使うかはわからんけど。
mbed用イーサネット接続キット - スイッチサイエンス

私の予定では、ブレッドボードを2枚横につなげて、そこにmbedを載せるつもりだった。
が、横につなげるとピンの幅が合わないのね・・・。
まあ、ばらばらにすれば使えるから、よしとしよう。

埋め込み画像への固定リンク

 

PCにUSBでつなぐと、ST-Linkとかのドライバがないと言われる。
mbedのページに行って、アカウントを追加して、デバイスとしてF411REを追加すると、情報ページが出てきた。
そこにFirmwareを新しくしてね、とあるのでリンク先に飛ぶと、先にST-Linkのドライバを入れる、というリンクが出てきたので、そこからダウンロードして実行すると、ドライバが追加された。
Firmwareのファイルも、ダウンロードして実行すると、焼かれた(USBスニファを起動していたためか、最初はNUCLEOに焼いているFirmwareを読めなかった)。

あとは、作ったアカウントにプロジェクトを新規作成し、ひな形としてLED点滅のを選び、適当に時間を変更してコンパイル。
そうするとbinファイルがダウンロードされるので、それをUSBメモリとして見えているNUCLEOに置くだけ。
焼いてる間は、USBコネクタ近くのLEDがちかちかするみたいだ。

LEDは3つあり、1つはUSB通信の状態とかを表すやつ、1つがユーザ用、もう1つが電源状態用になっている。
で、この電源LEDなんだけど、とてもまぶしい。
基板の文字が読めないくらいだ。
これはマイコンとはつながっておらず、電源から直につながっている。
R32をはずせばいいんだけど、それもなぁ。
というわけで、シールを上から貼る、というアナログな方法で対応した。
まあ、一種のOut of Bandな対策ですな。

そういえば、Nexus5でUSBメモリをマウントできたので、Androidから焼けるかどうか試そうとした。
が、Nexus Media ImporterがFAT12に対応してないらしく、マウントができなかった。
残念。

USBのパケットキャプチャ

今まで、USBのパケットキャプチャにSnoopyProを使っていた。
よかったのだけど、Windows7にしてから動かなくなったように思う。
まあ、元々がXPで動作確認と書いてあるから仕方ないのだが・・・。

以前、TIのBLEスニファをWireSharkで見る際、USBpcapを使った。
これは、USBのパケットをWireSharkで見たかったので使ったのだけど、キャプチャする対象がハブかポートかわからないけど大きなまとまりでしかできなかった。
なので、場合によっては意図してないパケットまで入り込んでしまい、ちょっとわずらわしい。

今回見つけたのは、Free Device Monitoring Studio。
これはなかなかよかった。
フリー版の制限で12分までってなってるが、それなら困りはしない。
が・・・さっき気付いたんだが、Grace Periodってことで4日間だけなのか。
がーん。
4日間はフル機能が使える、という意味だったらよいのだが・・・。

でも、こっちのサイトを見ると、お金のことは書いてないな。
そもそも、上のページでも「Zero price software」とあるしなぁ。
しばらく使ってみよう。

[libusb]Enumerationができていなかったのは、私が悪かった

前回、USB_NODE_CONNECTION_INFORMATION_EXのメモリ確保について書いたが、よくよく考えるとそれが原因ではないと思う。
もしメモリが足りないのだったら、APIがエラーを返すべきなのだ。
メモリが足りない→APIは未接続で返す、ということにはならないだろう。

 

デバッグ出力を追加して見ていったのだが、どうもNoDeviceConnectedで返しているときはVIDやPIDが0x0000になっていて、usbviewでNoDeviceConnectedを返しているタイミングと同じようだ。
libusb_open()で失敗しているのはこんな深いレベルじゃなくて、単に判定のしかたが足りてないだけか?
うわっ・・・私のEnumeration、甘過ぎ・・・?

 

こんな順番で、対象の機器を探していた。

  1. libusb_init()
  2. libusb_get_device_list()
  3. libusb_get_device_descriptor()
    1. VIDとPIDが一致するまで、ぐるぐる回す
    2. break
  4. libusb_open()

この4番でopenに失敗するとみなしていた。
しかし今回のデバイスは、デバイスマネージャーで見ると同じ名前で複数出てくる。
zadigでも、Interface0、Interface1、みたいに出てきていた。
だからかわからないが、最初にVIDとPIDが一致しただけで判断するのではだめなようだ。

  1. libusb_init()
  2. libusb_get_device_list()
  3. libusb_get_device_descriptor()
    1. VIDとPIDが一致するまで、ぐるぐる回す
      1. libusb_open()
      2. 成功したらbreak

こんな感じで、できた。
libusb、疑ってすまんかった。

2015年2月21日

[libusb]usbviewとlibusbのこまごまとした違い (1)

【これまでのあらすじ】
あるUSBデバイスをlibusbで見てみよう、と軽く考えたhiro99ma。
いつものようにzadigでWinUSBドライバに変更したものの、libusbのオープンすらできない。
どうもEnumerationあたりに原因がありそうなのだが・・・・?

 

まだ解決には至っていないが、整理がてらメモを残しておく。

今はWinUSBのサンプルとしてusbviewを見つつ、libusbのEnumerationとどう違うのかを見ていっている。
一応、私のUSBスキルの話をしておくと、無いに等しい。
ときどきネタとしてlibusbのことを書いているが、だいたいは動いている前提でバルク転送するだけしかやってないのだ。
動かないときはzadigでドライバを差し替えるだけ。
今回のように、実装にせよ内部のことを見るのははじめてだ。

で、素人っぽくAPIの使い方を見ていったのだが、そんなに違いはなさそう。
ただ、APIに渡すパラメータがいくつか違う。

最初に気付いた違いは、サイズだ。
libusbはUSB_NODE_CONNECTION_INFORMATION_EXのsizeofをそのまま設定しているのだが、usbviewではsizeof(USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof(USB_PIPE_INFO) * 30)という大きい値を渡している。
なんかUSB_NODE_CONNECTION_INFORMATION_EX構造体の定義を見ると、こうなってた。

typedef struct _USB_NODE_CONNECTION_INFORMATION_EX {
  ・・省略・・
  USB_PIPE_INFO PipeList[0];
}USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX;

確かこの記述方法は、C99あたりで採用されたものじゃなかったろうか。
つまり、一番最後に可変なメンバを置き、malloc()などでサイズを自由に確保するやつだ。

大ざっぱな書き方をすると、こんな感じか。

struct image_x {
  uint8_t r;
  uint8_t g;
  uint8_t b;
  uint8_t alpha;
};

struct image {
  int width;
  int height;
  image_x  pix[0];
};

int width = どっかから横幅を取得;
int height = おなじく縦幅を取得;
image *pImageArray = (image *)malloc(sizeof(image) + sizeof(image_x) * width * height));

なんとなく、イメージが伝わればよいが。
以前は、配列要素に「0」を指定できなかったので、上記をこうやって書いていたそうだ。

int width = どっかから横幅を取得;
int height = おなじく縦幅を取得;
image *pImageArray = (image *)malloc(sizeof(image) + sizeof(image_x) * (width – 1) * height));

[1]とする以上、1つ分のメモリは確保されるので、それを差し引いた分を確保するという意味だ。

 

細かい中身はよいとして、比較で気になったのは、libusbはこのメンバをスタックで確保していたのに対し、usbviewはポインタで確保してmalloc()で割り当てていたというところだ。
だから、IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EXで取得した値すべてを渡すにはメモリが足らずに失敗していた、という筋書きじゃなかろうかと。

・・・違うね。
少なくとも、サイズだけが原因ではないようだった(暫定でメモリを増やして試した)。
DeviceIoControl()自体は成功していて、このデバイスに対しては戻ってきたサイズが小さいものなぁ。

 

今回は私の負けだが、それで終わりではないぞ。

[winusb]USBView sampleをVisualStudio 2013 Communityでビルド

WinUSBでUSBのEnumerationをしてみれば、libusbでエラーになっているところの意味がわかるかも。
そういうわけで、VisualStudio CommunityでUSBViewをビルドしてみた。

 

いるもの

 

WDKが入ってない状態でビルドすると、ApplicationForDriversが足りない、みたいなエラーが出る。
インストーラでインストールすれば、特に設定とかしなくてもビルドが通った。
いい時代になったものだ・・・・。

[libusb]libusb-1.0をcygwinでビルド

念のため、libusbを自分でビルドしてみよう。
今はcygwinのインストーラで入ってきたdllを使ったのだけど、もしかしたら実行している環境でビルドすると何か違うかもしれん。
まあ、そんなことはないんだろうけどさ・・・。

ソースがgithubにあった。
libusb/libusb

cygwinでビルドする場合は、INSTALLを見る。configure→make、というおなじみの流れのようだ。
あれ・・・configureがないやん。
よくわからんが、configure.acとかあるんで、作り出すことができるようだ。
configureの作り方(autotoolsの使い方) - メモ。。メモ。。
今は、これにlibtoolizeというコマンドを実行せんと、ltmain.shがない、というエラーが出るようだ。
最初に実行すればよいのかな?
出てから実行したので、順番はよくわからん。

あと、gccのバージョンによるのかもしれんが、stricmp()がないと怒られた。
これは、windows_usb.hの#defineを_stricmp()からstrcasecmp()に置き換えた。
c++ - g++ error: ‘stricmp’ was not declared in this scope (but OK for 'strcmp') - Stack Overflow

これでビルドできた。
が、結果は変わらん。
まあ、そうよね。

[libusb]Win8+特定デバイスでうまくいかん

うまくいかんシリーズだ。

あるUSBデバイスをlibusb-1.0で動かそうとした。
プラットフォームは、Windows8。
cygwinのインストーラでlibusb-1.0を選択してインストール。
ドライバはzadigを使用して、WinUSBを使う(libusb-win32などを選んでしまいがちだが、libusb-1.0はWinUSBベースで動いているはず)。

で、ここまではよかったのだ。
あとは、libusbでデバイスを検索して、オープンして、アクセスするだけ。
だけなんだけど、libusb_open()でエラーになった。
戻り値は-12で、LIBUSB_ERROR_NOT_SUPPORTED。

しかし、libusbのデバッグ出力を有効にして動かすと、USB機器の一覧(Enumeration)をしているときに、既にエラーとなっていることがわかった。
win32でDeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX)を投げているのだが、それで取得した状態がNoDeviceConnectedとなっているのだ。
NoDeviceConnectedなデバイスをlibusb_open()すると、LIBUSB_ERROR_NOT_SUPPORTEDになるというしくみ。

 

それだけだと、なんかlibusbの使い方がおかしいのかな、と思うところだが、そう言いきれないところが残っている。

まず、同じプログラムをWindows7 sp3 64bitのcygwin(32bit)で動かしたのだが、それでは問題がない。
どちらかといえば、Win7で動くことを確認したからWin8でやろうとした、というところだ。

ではWin8でlibusbがうまく動かないかというと、そうではない。
PaSoRiをつないで動かしたのだが、そこではエラーが出るわけでもなく、ちゃんと動いた。
動くのを確認し、PaSoRiをつないだまま対象のデバイスをUSB接続し、その状態でPaSoRiを制御しようとすると、同じエラーが出つつもちゃんと動いた。
つまり、出ているエラーというのはUSBデバイスを列挙する段階で出ているだけで、制御対象にしようとしたデバイスから出ていたわけではないということになる。

ということは、単にDeviceIoControl(IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX)が特定のデバイスで失敗するだけではないか、と思った。
その確認をするのは、ちょっと私では荷が重い。
なので、ひとまずMicrosoftが出しているUSB機器を見るツール、usbview.exeを使ってみた。
・・・うん、これは特定デバイスであっても情報がちゃんと表示されている。
うーむうーむ・・・。

 

昔の私も、同じようなことを調べていたみたいだ。
hiro99ma blog: libusb_open()でLIBUSB_ERROR_NOT_SUPPORTEDが返ってくるが、わからん
当時は、WinUSBドライバじゃなくてlibusb-win32ドライバを使っていたのでだめだったようだ。
hiro99ma blog: libusb-1.0はzadigを使うべし
しかし、今回は状況が違う。
動くデバイスと動かないデバイスがあるし、PCに因るのかOSに因るのかはわからないが動いているPCと動いていないPCがある。


エラーが起きているタイミングは、libusb_init()後にlibusb_get_device_list()している間だ。
つまり・・・まだどのUSBデバイスを使いたいとか決める前。
サンプルだってそれ以上のことをしていないし、サンプルをlibusb_set_debug(NULL,3)して動かすとエラーが出ている。
libusb/listdevs.c at master · libusb/libusb

というわけで、この問題はlibusbよりもWinUSBの方を先に見ていった方が早いように思った。

2015年2月14日

[qt]今、Qtは5.4.0が最新(2015/02月)

フォルダの整理をしていたら、Qtがあった。
そういえば、最近なにもしてなかったな・・・。
今はどうなってるんだろう?

手元にはバージョンが5.1.1になっている。
サイトを見ると、5.4になってた。
当時のメンテナンスツールはまだ使えるのだろうか?
先にアップデートしろ、と言われたので実行して立ち上げ直すと、日本語になった。

image

コンポーネントの選択を見ると、5.4があった。
が・・・どれをチェックすればよいのかよくわからん。

image

 

アップデートしてQt Creatorを起動したが、大きく変わったような感じはしない。
以前は、インストールすると拡張子に関連づけたアプリの設定まで奪ってしまったような気がするが、今回はインストーラが走るときに選択できたような気がする(なんとなく進めていたので、記憶が薄い。。)。

Qt 5.1.1で作っていたプロジェクトがあったのでビルドさせたが、MinGWだと普通にビルドできた。
Visual Studio 2013 Communityをインストールしているのでmsvcも試したが、こちらはエラーがたくさん出た。
まあ、そっちのことは何も考えずに作ってたからね・・・。
ともかくビルドできているので、コンパイラのパスを自動的に取得したりするところは動いているようだ。


今は、製品が4つあるようだ。

  • Community : ライセンス費無しで使える
  • Indie Moble : 毎月20ユーロ/25ドル
  • Professional : 毎月139ユーロ/174ドル
  • Enterprise : ご相談

Linux版では、BLEも対応しているらしい。
そっちは興味があるな。
Web開発機能にフォーカスした「Qt 5.4」がリリース | SourceForge.JP Magazine

 

gitoriousにQtプロジェクトがあるので、リンクを載せて今回の記事は終わろう。
Qt by Digia

[ble]long attributeのNotification

前に書いたが、NotificationやIndicationはデータサイズがATT_MTU-3までしか通知できない。
ATTの仕様としては、Core_v4.2 Vol.3 Part F 3.2の「3.2.9 Long Attribute Values」あたりに記載があり、1回のパケットではATT_MTU-1が最大だけど、それより大きいサイズのAttributeを定義することもできて、それは"long attribute"と呼ぶそうだ。その最大長は512byte(octだけど、面倒なのでbyteって書く)。

S110のGATT Serverではこうなってた。

/** @defgroup BLE_GATTS_ATTR_LENS_MAX Maximum attribute lengths
 * @{ */
#define BLE_GATTS_FIX_ATTR_LEN_MAX (510)  /**< Maximum length for fixed length Attribute Values. */
#define BLE_GATTS_VAR_ATTR_LEN_MAX (512)  /**< Maximum length for variable length Attribute Values. */ 
/** @} */

 

ATT_MTU-3くらいのAttributeにしておくと設計とかはやりやすいし、データの取得なんかも楽になるんだけど、まあそうも言っていられない場合がある。
今回みたいに、NDEFのデータを読ませたい、とかなると、256byteくらいは確保しておきたい。

そうなると、Attributeのデータ長と、Notificationできるデータ長が異なることになる。
こういう非対称な場合、どうしたらいいんだろうか?
nRF51 SDKのAPIはNotificationするデータを設定できるようになっているので、必ずしもAttributeのデータを送らなくてもよいのだ。

よいけど、お作法としては同じデータの先頭から渡せる分だけを載せるのがよいのだろう。
だからこそ、「3.4.7.1 Handle Value Notification」の注意書きに、「long attributeが取得したいならこのnotificationの後でRead Blob Requestを使いなさい」ということが書いてあるのだろう。

2015年2月11日

[ble]CCCDへなぜ書き込みをしたいのか?

GATT Tableを書きながら、まだまだBLEの勉強中。
今日はCCCD。

Client Characteristic Configuration Descriptionの略で、CCCD。
NotifyでCentralに通知したいときには準備する、くらいの認識だったが、もう少し調べてみた。

CCCDは2byte分のValueを持つCharacteristicだ。
中身はビットで、Notifyを許可するか、Indicateを許可するか、を表すことになっている。
だから、NotifyするCharacteristicにはCCCDを用意するのだ。

Permissionは、Readについては認証など不要で許可する。
Writeについては、アプリレベルでの認証というかがいるようだ。ただ「or is implementation specific」ともあるので、Writeは許可しなくてもよいのかもしれない。

Writeを許可するとどんなよいことがあるかというと、Notify/Indicateの有無をCentralから制御できることになる(Peripheralの意思に因るが)。
Central側が「もう来ないで!」と言って、Peripheralのアプリがそれを受け入れたら、PeripheralはNotify/Indicateを送らなくすることができる。そうすると、お互いの電力消費を抑えることができそうだ。

もしCCCDでNotify/Indicateが許可されていなければ、nRF51822ではsd_ble_gatts_hvx()でNRF_ERROR_INVALID_STATEを返すらしい。
How to know if the Notification/Indication of a Characteristic is enabled? - Nordic Developer Zone
なので「間違えてNotifyしてしまった!」ということは心配しなくてもよいが、戻り値をAPP_ERROR_CHECKマクロでチェックしてしまうとまずいかもしれない。

 

今日のまとめ。

  • CCCDは、現在Notify/Indicateができるかどうかを表している
  • 許可されていない場合に通知しようとすると、nRF51 SDK/SoftDeviceでエラーにしてくれる
  • CCCDに書込みを許容していると、Centralからも制御できる(CCCDのデフォルト値は通知不可)
  • 書き込みの際はアプリなどへの承認が必要っぽい

そうか、デフォルトで通知しないようになってるから、対向アプリで通知開始ボタンを押してやらないといけないのだな。


どうでもよいが、L2CAPとかI2CとかE2Pとかは省略したがるのに、CCCDはC3Dにならないのは不思議だ(発音の関連か)。
私は「I2C」は「あいつーしー」だし、「E2P」は「いーつーぴー」だ。

でも入社したときの上司が「E2P」を「E二乗P」と呼んでいたので、私は発音だけで「EGOP」と覚えてしまっていた。
まあ「いーすくうぇあぴー」を日本版にするとそうなるんだろうな。
インターネットで調べるのが普通になりつつある時代だったけど、さすがにEGOP-ROMでは検索はできなかったし、会話だけだと誰と話しても気付かれなかったし。
懐かしい話だ。