USB-CAN変換アダプタCANtact-mini

2年ぶりの更新ですが, まずは生存報告, 生きてます.
生きるのって大変ですね.


とある事情でUSB-CAN変換アダプタが必要になりそうでしたのでつくってみました.
当初はハードウェアからファームウェアまですべて自作する計画でしたが, 調べていくうちにCANtactと呼ばれるUSB-CAN変換アダプタを発見. ハードもファームもいい感じに完成しておりクリエイティブ・コモンズで公開されていたので, 方針変更. CANtactをベースにサイズを小型化したCANtact-miniを作者さんのEric Evenchick氏に感謝しつつ再設計してみました.

回路図

f:id:Strobo:20160919174537p:plain

基板

f:id:Strobo:20160919174633p:plain
f:id:Strobo:20160919171615p:plain

Linuxから使う

f:id:Strobo:20160818231430j:plain:right:w320

CANtactはPCから見るとUSBシリアル変換デバイスとして見え, SLCANと呼ばれるプロトコルでお話することでシリアル-CANの変換を実現しています.

LinuxにはSLCANのドライバが内蔵されているのでcan-utilsをインストールするだけでCAN通信を行うことができます.以下にLinuxで簡単なCAN通信を行うコマンドを書きます.

CANインターフェースの設定

modprobe can
slcand -o -c -s6 /dev/ttyACM0 can0
ifconfig can0 up

送信

cansend can0 123#DEADBEEF

受信

candump can0

Windows, Macから使う

f:id:Strobo:20160820235310j:plain:right:w320

Win, MacにはSLCANのドライバは実装されていないのでアプリレベルでSLCANを実装する必要がありますが, CANardと呼ばれるPython製のライブラリを利用する事でCANtactを使用できます.

BeagleBoneBlackで通信距離(ほぼ)無限のLTEラジコンB3Rを作った

f:id:Strobo:20140814124452j:plain
AR.Drone等のカメラ映像を見ながら操作できるラジコン製品が増えています。このラジコンは通信にWiFiを使うことで機体の操作だけでなく同時に映像の送信も可能になっています。しかしWiFiで通信している以上、通信可能範囲は操作端末の周囲数百メートルが限界です。そこでWiFiではなくLTEを使用したラジコンであるBeagleBoneBlackRover略してB3Rを開発しました。LTEで通信しているのでLTEの電波が入る範囲ならば操作端末の位置に関わらずラジコンの遠隔操作が可能です。例えば日本にあるLTE圏内のラジコンをインターネットを通して海外から遠隔操作することも可能になります。

オンボードカメラの映像


B3Rの構成

B3Rの構成は以下の図の様になっています。
f:id:Strobo:20140814030043p:plain
以下各要素について解説していきます。

ラジコン

f:id:Strobo:20131106203053j:plain:w320:right
Hobbykingから半完成のラジコンTurnigy Trooper SCT 4x4 1/10 Brushless Short Course Truckを購入しました。半完成モデルなので通信機とコントローラとバッテリーが付属しておらずそのままでは動作しません。しかし今回はその通信機を自分で開発するので問題ありません。

ESC

ESCは購入したラジコンに付属していたのでそのまま使用します。BBBからPWM信号をESCに入力するとモーターの速度が制御できます。このESCには電源スイッチが繋がっておりこのスイッチでESCの電源を操作できる。今回はBBBからESCの電源を操作したいので、スイッチを取り外しFETを繋いでBBBからESCの電源を操作できるようにした。これでBBBの電源が切れると同時にFETがOFFになりESCの電源も切れるので多少安全になる。ただしBBBがFETに繋がっているポートをHighにしたままフリーズした場合はFETはOFFにならないのでESCも停止しない。この方法はもう少し改良が必要かもしれない。
このESCには通信機やサーボモーター用に6Vの電源も用意されています。この6V出力はステアリング用サーボーモーターの電源とした。

PDU

PDUはDC-DCコンバータ、電流、電圧検出回路で構成されています。DC-DCコンバータはOKL-T/6-W12N-CでBBB, USBハブ, webカメラ, LTE-USBモデムに電力を供給します。
電流、電圧検出回路はバッテリー電圧と電流を計測する回路。BBBのADCは最大1.8Vなのでバッテリーの電圧を分圧してBBBで計測できるようにしている。電流検出は0.01Ωのシャント抵抗とLT6106CS5を使用してESCの定格である30Aまでの電流を計測する回路とした。

f:id:Strobo:20140814125710j:plain:w640

Battery

B3Rのバッテリーは7.3V2200mAhを2個並列に接続している。これは動作時間を長くする為。もう一つはバッテリーが一つの場合、モーターが回転し始める瞬間にBBBが再起動してしまう問題を回避するためです。
BBBが再起動する問題はモーターが回転を始める瞬間は電源電圧が一瞬低下することが原因。電源電圧がDC-DCコンバータの最低動作電圧の6Vを下回ってしまうとその先に繋がっているBeagleBoneBlackが停止してしまう。BeagleBoneBlackの停止するとESCも停止する。ESCが止まるとモーターも止まり。その後電源電圧が回復しBBBが起動する。この様な流れでモータが回転を始める瞬間BBBが再起動してしまうように見えていた。今のところバッテリーを並列に繋いでいる事で回避出来ているが、保護回路無しのリチウム電池の並列接続はあまりいいやり方ではない。本当なら電源を別にするべきかなと。

BBB

B3R全体の制御を行う部分でBeagleBoneBlack Rev.A5Cを使用しています。ディストリビューションは細かいカスタマイズが可能なGentooを使用しています。Gentooを使用すれば不必要なプロセスを止めやすくBBBの消費電力を減らすことができます。全体の制御プログラムはGoで、UDP通信やストリーミングを行う部分はCで開発しました。
BeagleBoneBlackのセットアップ等は以下のサイトを参考にしました。
カーネル/VM式 ARMマイコン入門(関東版)
Gentoo Linux Documentation -- Gentoo on the BeagleBone Black
Raspberry PiでL-03Dを使ってみる

USB HUB

BBBのUSBポートは1つしかない為2つ以上のUSB機器を使用したい場合はUSBハブを使用する必要があります。そしてBBBのUSBポートの電流供給能力は800mAありますがUSBハブ, webカメラ, LTE-USBルータを同時に使用すると800mA を超えてしまい、BBBのUSB機能が停止してしまいます。そこでUSBハブを改造してDC-DCコンバータから5V電源を直接供給できるようにしました。これによりBBBのUSBポートに3つの機器を同時に接続しても問題なく動作するようになりました。

USB-LTE Router

USB-LTE RouterにL-03Dを使用しiijmioのsimを差してインターネットに接続しています。前回の記事UDP hole punchingの実装をしてみる - ELECTLOGICで書いたようにiijmioの回線ではグローバルIPアドレスが割り振られません。これでもTCPなら双方向で通信できますが、UDPの場合はそれができません。そこでUDPホールパンチングを使用してプライベートIP環境での双方向UDP通信を実現しました。これにより低遅延での操作が可能です。

WebCamera

WebCameraはLOGICOOL ウェブカム HD画質 120万画素 C270を使用。映像をYUV422形式でBBBに取り込み。YUV422からYUV420へ変換した後VP8でエンコード。その後UDPで操作端末へ送信しています。

Servo Motor & Motor

これらは購入したラジコンに付属していた物をそのまま使用した。問題があるとすれば、モーターが回転する時の音がうるさいこと。

ケース

f:id:Strobo:20140814124533j:plain:w320

ケースはアルミ板とアクリル板を組み合わせて設計しました。防水設計にはなっていないので雨天時は動かせません。アクリルに透明なシール エーワン ラベルシール 光沢フィルム・透明 ノーカット 10シート 28791にロゴを印刷して貼付けています。さらに野外で動かす物なのでUVカットの透明フィルム エーワン UVカット透明カバーフィルム 6シート 35041をロゴの印刷されたフィルムの上に重ねて貼付けています。

f:id:Strobo:20140814125915j:plain:w640
アクリルカバーを外したところ。右上のPDUから出ている白い配線は電源スイッチへ繋がっている


まだいくつかの改良や追加したい機能などがありますが、映像のストリーミングと最低限の操作が出来る様になったのでひとまず公開してみました。次はGPSと測域センサを載せて自動操縦をさせたい。

UDP hole punchingの実装をしてみる

ラジコンをLTE回線を使って制御できたら面白そうだなと思いいろいろ調べてる。

BeagleBone BlackL-03Dを組み合わせてL-03DにIIJmioIIJ BIC SIMを差せばインターネット経由でBeagleBone Blackを遠隔操作をする事が出来るはずです。あとはBeagleBone Blackをラジコンに載せればラジコンをインターネットを経由で遠隔操作できます。しかしこれには一つ問題があります。その問題とはIIJmioのsimを使用した場合BeagleBone Blackに割り振られるIPv4アドレスがプライベートIPアドレスとなってしまう事です。プライベートIPv4アドレスが割り振られたという事はBeagleBone Blackに割り振られたIPv4アドレスが分かったところでインターネット側からの通信はIIJmioのNATに阻まれてBeagleBone Blackにアクセスする事は通常出来ません。

この場合UDPホールパンチングというテクニックを使用するとプライベートIPアドレスが割り振られたBeagleBone Blackと直接通信する事ができます。UDPホールパンチングについては他のサイトに詳しい解説があるのでここでは省略します。そこでUDPホールパンチングを行うサーバーとクライアント(以下nodeと呼ぶ)を自分で実装しgithub.comに公開しました。

strobo/holepunch · GitHub

holepunchの解説

ビルドコマンド等についてはレポジトリ内のREADMEを参照ください。
このholepunchはlibeventを使用していますのでまずlibeventをインストールしてください。
次にPCを3台用意します。一つはUDPホールパンチングの仕組み上必要となるマッチングサーバーです。このサーバーだけはグローバルIPアドレスが必要です。残りの二つは実際に直接通信するPCとなります。この二つのPCは双方ともプライベートIPでも問題ありません。

このholepunchの実装ではstunを使用せず独自のプロトコルで通信を行います。通信相手の識別には任意で設定可能な文字列keyを使用します。この文字列keyの一致したPC同士で直接通信をする事ができます。

おおまかな通信手順
  1. nodeAがkeyをマッチングサーバーに送信
  2. マッチングサーバーは送られてきたkeyとnodeAのIPアドレスとポート番号を記録します
  3. nodeBがマッチングサーバーにnodeAと同じkeyを送信
  4. マッチングサーバーは今までに記録されたkeyと送信されたkeyが一致するかを確認し一致すれば双方に双方のnodeのIPアドレスとポート番号を通知します。一致しなければ新たなkeyとしてnodeBのIPとポート番号を記録します
  5. nodeA,B双方がマッチングサーバーから通知されたIP:ポートに対して通信を行う
実際に使用する

サーバ上で

$ ./holepunch server

nodeA上で

$ ./holepunch node example.com foo

nodeB上でも同じコマンドを実行

$ ./holepunch node example.com foo

通信が確立したら、各nodeのターミナル上で文字を入力しEnterで入力した文字列を相手に送信する事が出来ます。


Android+ポケモンキーボードでモバイルLinux環境を作る


Android+ポケモンキーボードでモバイルLinux環境を構築する話です。当然root化とbusyboxのインストールが必要です。
今回はGalaxy Nexusにインストールした。他のAndroid端末でも同じようにインストールできると思うけど、手順どうりにやってもうまく動かないことがあるのでその時はググるなりしてください。

chroot環境の構築

まずArch Linux ARMフォーラムを参考にchroot環境を構築します。

Arch Linux ARM • View topic - Installation in an Android Chroot

私はArchLinux派なので、ArchLinuxをインストールしていますがDebianなど他のディストリビューションがいい。もうすでにインストールしてるよって人は、他のディストリのインストール方法をググるなり読み飛ばすなりしてください。


まずAndroid上で以下のコマンドを実行してArchLinuxをインストールします。
途中ddコマンドで750MBのイメージファイルを作成していますが、容量は各自で適当に決めてください。私はいろんなパッケージをインストールするつもりなので2GBの容量で作成しました。

su
cd /sdcard
mkdir arch
dd if=/dev/zero of=alarm.img seek=749999999 bs=1 count=1
mke2fs -F alarm.img
mknod /dev/loop1 b 7 0
losetup /dev/loop1 alarm.img
mount -t ext2 /dev/loop1 arch/
cd arch
wget http://archlinuxarm.org/os/ArchLinuxARM-armv5te-latest.tar.gz
tar xzf ArchLinuxARM-armv5te-*.tar.gz
rm ArchLinuxARM-armv5te-*.tar.gz
cd ..
umount /sdcard/arch
losetup -d /dev/loop1

これでArchLinuxARMのインストールは完了。

次に起動スクリプトを/sdcard/startarch.shとして配置します。
この起動スクリプトはarchlinuxarmのフォーラムの書き込みを参考に作成しました。

startarch.sh
perm=$(id|cut -b 5)
if [ "$perm" != "0" ]; then 
 echo "This script requires root! Type: su"; exit;
fi

echo "Starting up ArchLinux"

if [ ! -b /dev/loop1 ]; then
 mknod /dev/loop1 b 7 0
fi

losetup /dev/loop1 alarm.img
mount -t ext2 /dev/loop1 arch/
cd arch
mount -o bind /dev/ /sdcard/arch/dev
mount -t devpts devpts /sdcard/arch/dev/pts
mount -t proc proc /sdcard/arch/proc
mount -t sysfs sysfs /sdcard/arch/sys
mount -o bind /mnt/sdcard /sdcard/arch/media/sdcard

busybox sysctl -w net.ipv4.ip_forward=1
echo "nameserver 8.8.8.8" > /sdcard/arch/etc/resolv.conf
echo "nameserver 8.8.4.4" >> /sdcard/arch/etc/resolv.conf
echo "127.0.0.1 localhost" > /sdcard/arch/etc/hosts
echo "Arch is configured that can be accessed from the IP:"
ifconfig wlan0
echo " "

PATH_=$PATH
export PATH=$bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin:/bin:/usr/local/sbin
export TERM=xterm
export USER=root
export HOME=/root
export SHELL=/bin/bash

/system/xbin/busybox chroot . /bin/bash
export PATH=$PATH_
cd ..
umount /sdcard/arch/media/sdcard
umount /sdcard/arch/dev/pts
umount /sdcard/arch/dev
umount /sdcard/arch/proc
umount /sdcard/arch/sys
umount /sdcard/arch
losetup -d /dev/loop1

echo "Shutting down ArchLinux"

準備が終わったのでいよいよArchLinuxを起動してみます
ArchLinuxを起動するには

su
cd /sdcard
sh startarch.sh

で起動できます。

あとは普通のArchLinuxとして操作できます。pacman -S vim などで好きなパッケージを入れてください。ArchLinuxのパッケージマネージャはpacmanです、pacmanの詳細はArchLinuxのwikiを読んでください。
何度も言いますが、端末によってはうまく動かないので、そのときは各自で起動スクリプトを書き換えるなどしてください。

ポケモンキーボードと接続

さて、これでLinuxのインストールはできたので。スクリーンキーボードでコマンドを打って操作することはできるようにはなった。ただ使いづらくて仕方ないのでキーボードを接続します。

今回はDSのゲーム、ポケモンタイピングに付属するキーボードを使った。このキーボードはBluetooth接続でiPhone, PCなどにも普通に接続でき、スタンドまで付属していてamazonで2000円程度。他のbluetoothキーボードと比べても安い方だ。接続方法はFnキーを押したまま電源スイッチをオンにして、AndroidiPhone, PCからBluetooth機器を検索すればNintendo Wireless Keyboardとして認識されるのでそのまま接続。次に画面に出る数字をキーボードに入力してEnterを押せば接続が完了する。

Androidポケモンキーボードを接続すると。USキーボードとして認識されるのでJISキーボードとして認識してもらうためにキーアサインを変更しないといけない。
Androidのキーアサインについては以下のサイトを参照してください。

.klと.kcmファイルを用意すれば未知のキーボードにも対応できそうだ。


ポケモンキーボードでの注意点はESCキーだ。ESCキーは全角/半角キーと一緒になっていてESCを入力するときはFn+全角/半角を押さないといけない。これはvimを使用するときには不便なので、全角/半角キーの機能を殺して全角/半角キー = ESCキーとして動作するようにキーアサインを変更した。
というわけでキーアサインを変更する以下の2つのファイルを@hiromo6さんの作成されたファイルをベースに作成しました。

それぞれのファイルを

  • /system/usr/keylayout/Nintendo_Wireless_Keyboard.kl
  • /system/usr/kaychars/Nintendo_Wireless_Keyboard.kcm

となるようにコピーする。このとき/systemはデフォルトでは書き込みできないので。/systemを書き込み許可にして再マウントしなきゃいけない。Galaxy Nexusの場合は以下のコマンドで/systemを再マウントできる。

mount -o remount,rw /dev/block/platform/omap/omap_hsmmc.0/by-name/system /system

他のandroid端末の場合は別のコマンドになるので各自調べて下さい。

ターミナルエミュレータ

ターミナルエミュレータにはConnectBotの強化版であるVX ConnectBotを使った。が、ctrlキーの挙動が変(ConnectBotの仕様?)なのでソースコードを一部書き換えた。もちろんソースコードを変更するのでandroidの開発環境が必要です。
まずvx / connectbotからソースコードを落としてきてsrc/sk/vx/connectbot/service/TerminalKeyListener.javaの133行目付近のhardKeyboardHiddenをtrueに変更する。

diff --git a/src/sk/vx/connectbot/service/TerminalKeyListener.java b/src/sk/vx/connectbot/service/TerminalKeyListener.java
index a5a7af9..555d42c 100644
--- a/src/sk/vx/connectbot/service/TerminalKeyListener.java
+++ b/src/sk/vx/connectbot/service/TerminalKeyListener.java
@@ -130,7 +130,7 @@ public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceCha
         */
        public boolean onKey(View v, int keyCode, KeyEvent event) {
                try {
-                       final boolean hardKeyboardHidden = manager.hardKeyboardHidden;
+                       final boolean hardKeyboardHidden = true;
 
                        // Ignore all key-up events except for the special keys
                        if (event.getAction() == KeyEvent.ACTION_UP) {

正直に言うとなぜtrueにするとうまく動くのかわかってないです。詳しく書くと、hardKeyboardHiddenがctrlキーの挙動に関わっているようで、これがtrueだとctrlキーは正しく動く。Android起動直後にVX ConnectBotを起動した時はtrueなのですが、一旦HOMEに戻ってもう一度VX ConnectBotを起動するとfalseになっているという謎挙動をするので、強制trueにしています。これで今のところ大きな問題は出てないので大丈夫だと思います。


これで持ち運びできるLinux環境の完成です。Galaxy NexusはUSBホストも持っているのでUSB変換コネクタを接続すれば、Androidに対応していないUSB機器でもArchLinux側が対応していればArchLinux側から操作できます。たとえばAndroid上で動作しているArchLinuxからUSBメモリにアクセスするには

mount /dev/block/sda /mnt

USBメモリの中のファイルにアクセスできます。

ポケモンキーボードを使うことでほぼPCと同等のLinux環境をAndroidで実現することができました。これならRaspberry piにも負けない小型PCとして使えるのではないでしょうか。