ラベル Linux の投稿を表示しています。 すべての投稿を表示
ラベル Linux の投稿を表示しています。 すべての投稿を表示

2014年5月18日日曜日

BrickPi で EV3 Sensor Adaptor for NXT のお試し

 本日のがらくたは、Mindsensors社の「EV3 Sensor Adaptor for NXT」です。いつもこの手のものをお世話いただいている ROBO Product さんより購入しました。

EV3 Sensor Adapter for NXT or Arduino

 このアダプタは、EV3用のセンサをNXT コントローラで利用できるようにするための変換アダプタです。中身としては、EV3のUART(シリアル)信号をNXT コントローラで利用できる100kbpsのI2C通信と変換しているのだと思われます。Mindstorms社のサイトを見ると、EV3のセンサをNXTコントローラだけでなく、NXShieldやBrickPiでも使えるようになると書いてありますが、ライブラリやサンプルが提示されているのは、2014年05月中旬現在では、NXTブロックでのグラフィックプログラミングとNXCだけのようです。

 そこで、EV3 のセンサをこのアダプタを使って、BrickPi で利用できるか試してみようと思います。BrickPi は基本NXTコントローラ互換のセンサポートなので、EV3のセンサも使えるようになれば、手持ちのEV3センサが活用できるようになります。今回は、NXTのセットにはなくEV3(の教育版)で追加された「ジャイロ」とNXT版よりも距離精度が上がっている「超音波センサ」をターゲットにしてみます。


 ジャイロセンサをBrickPi の S1ポートに接続しました。


 超音波センサはBrickPi の S2ポートに接続しました。


 参考にした資料は、Mindsensors社のサイトに有るマニュアルNXC用のライブラリのソースです。

 いきなりですが、動かしてみた結果です。ジャイロは「角度モード」で水平に回転させたときに回転した角度が直接読み取れます。初期位置から相対的に時計回りに+、反時計回りに-の角度が出力されます。
 超音波センサは「距離cmモード」にするとmm単位で対象との距離が出力されます。


 ジャイロ、超音波センサともに角度や距離がそのまま割りとリニアリティもよく出力されるので、非常に使いやすいです。ただ、BrickPiからアダプタを使ってのアクセスでは、I2Cデバイスとしてのアクセスなので、手順は一手間あります。今回は、関数化などをしていない状態ですが、ソースをこちらに載せておきます。

// S1ポートにEV3 Sensor Adaptor + EV3 ジャイロを接続。
// S3ポートにEV3 Sensor Adaptor + EV3 超音波センサを接続。

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#include "wiringPi.h"

#include "tick.h"
#include "BrickPi.h"

#define ESA_I2C_ADDRESS    0x32 // EV3 Sensor Adaptor のデフォルトI2Cアドレス
#define ESA_I2C_DEVICE_NO   0x00 // EV3 Sensor Adaptor の I2Cバス上の番号
#define ESA_I2C_REGISTER_COMMAND 0x41 // コマンドレジスタ
#define ESA_I2C_REGISTER_MODE  0x52 // モードレジスタ
#define ESA_I2C_REGESTER_CHANNEL0 0x54 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL1 0x56 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL2 0x58 // 0チャネルセンサ値格納
#define ESA_I2C_REGESTER_CHANNEL3 0x5A // 0チャネルセンサ値格納

#define MODE_Color_ReflectedLight 0x00 // カラーセンサ反射光モード
#define MODE_Color_AmbientLight  0x01 // カラーセンサ周囲明るさモード
#define MODE_Color_MeasureColor  0x02 // カラーセンサ色モード
#define MODE_Gyro_Angle    0x00 // ジャイロセンサ角度モード
#define MODE_Gyro_Rate    0x01 // ジャイロセンサ角速度モード
#define MODE_InfraRed_Proximity  0x00 // 赤外線センサ近接度モード
#define MODE_InfraRed_Beacon  0x01 // 赤外線センサビーコンモード
#define MODE_InfraRed_Remote  0x02 // 赤外線センサ遠隔モード
#define MODE_Sonar_CM    0x00 // 超音波センサcmモード
#define MODE_Sonar_Inches   0x01 // 超音波センサインチモード
#define MODE_Sonar_Presence   0x02 // 超音波センサ存在検出モード


int main(int argc, char *argv[])
{
 int result;
 uint8_t mode;
 int value;
 unsigned int tmp;

 // -- BrickPiの初期化。
 // タイマのクリア。
 ClearTick();
 
 // BricPi初期化(ポートオープン)。
 result = BrickPiSetup();
 if (result) return 0;

 // 通信アドレス設定。
 BrickPi.Address[0] = 1;
 BrickPi.Address[1] = 2;

 // -- センサポートの設定。
 // S1ポートをI2Cセンサに設定。
 BrickPi.SensorType[PORT_1] = TYPE_SENSOR_I2C;
 // S1ポートのI2Cスピードを100kbpsに設定。
 BrickPi.SensorI2CSpeed[PORT_1] = 0;
 // S1ポートのI2C接続デバイス数を1に設定。
 BrickPi.SensorI2CDevices[PORT_1] = 1;
 // S3ポートをI2Cセンサに設定。
 BrickPi.SensorType[PORT_3] = TYPE_SENSOR_I2C;
 // S3ポートのI2Cスピードを100kbpsに設定。
 BrickPi.SensorI2CSpeed[PORT_3] = 0;
 // S3ポートのI2C接続デバイス数を1に設定。
 BrickPi.SensorI2CDevices[PORT_3] = 1;

 BrickPi.SensorSettings[PORT_1][ESA_I2C_DEVICE_NO] = 0;
 BrickPi.SensorI2CAddr[PORT_1][ESA_I2C_DEVICE_NO] = ESA_I2C_ADDRESS;
 BrickPi.SensorSettings[PORT_3][ESA_I2C_DEVICE_NO] = 0;
 BrickPi.SensorI2CAddr[PORT_3][ESA_I2C_DEVICE_NO] = ESA_I2C_ADDRESS;

 if (BrickPiSetupSensors())
  return 0;

 // S1ジャイロセンサのモード設定。
 BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 書込バイト数
 BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 読込バイト数
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGISTER_MODE; 
 BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][1] = MODE_Gyro_Angle;
 //BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][1] = MODE_Gyro_Rate;
 // S3超音波センサのモード設定。
 BrickPi.SensorI2CWrite[PORT_3][ESA_I2C_DEVICE_NO] = 2; // 書込バイト数
 BrickPi.SensorI2CRead[PORT_3][ESA_I2C_DEVICE_NO] = 1; // 読込バイト数
 BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGISTER_MODE; 
 BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][1] = MODE_Sonar_CM;
 result = BrickPiUpdateValues();
 if (!result)
 {
  mode = BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];

  while (1)
  {
   // センサー値取得
   BrickPi.SensorI2CWrite[PORT_1][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
   BrickPi.SensorI2CRead[PORT_1][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
   BrickPi.SensorI2COut[PORT_1][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
   BrickPi.SensorI2CWrite[PORT_3][ESA_I2C_DEVICE_NO] = 1; // 書込バイト数
   BrickPi.SensorI2CRead[PORT_3][ESA_I2C_DEVICE_NO] = 2; // 読込バイト数
   BrickPi.SensorI2COut[PORT_3][ESA_I2C_DEVICE_NO][0] = ESA_I2C_REGESTER_CHANNEL0; // Ch0
   result = BrickPiUpdateValues();
   if (!result)
   {
    // S1ポート、ジャイロ値算出。
    tmp = (int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][0];
    tmp += ((int)BrickPi.SensorI2CIn[PORT_1][ESA_I2C_DEVICE_NO][1]) << 8;
    if (tmp > 32767)
    {
     value = (int)tmp - 65536;
    }
    else
    {
     value = (int)tmp;
    }

    switch (mode)
    {
    case 0:
     printf("Gyro angle: %d\n", value);
     break;
    case 1:
     printf("Gyro rate: %d\n", value);
     break;
    case 2:
     printf("Gyro N/A\n");
     break;
    default:
     break;
    }

    // S3ポート、超音波センサ値算出。
    value = (int)BrickPi.SensorI2CIn[PORT_3][ESA_I2C_DEVICE_NO][0];
    value += ((int)BrickPi.SensorI2CIn[PORT_3][ESA_I2C_DEVICE_NO][1]) << 8;

    printf("Ultrasonic distance: %d\n", value);
   }

   usleep(100000);
  }
 }

 return 0;
}


 関数化やクラス化をすれば、NXTのセンサとおなじ感覚で、もっと楽に使えるようになると思いますが、それは、また今度。



2014年3月23日日曜日

Adafruit の 2.8inch Raspberry Pi 用タッチスクリーン液晶

 今日のガラクタは、Adafruit の Raspberry Pi用2.8inch タッチスクリーン液晶です。いつものごとく、使用目的もないまま入手・・・。まあ、消費税増税前の駆け込み無駄遣いということで・・・。

 入手先は、スイッチサイエンスさんです。Raspberry Piくらいの大きさの段ボール箱に入っていました。


 中身は、液晶本体と、Raspberry Piの拡張コネクタに接続すためのメスコネクタと、同コネクタ信号をスルーして他のものをつなげる用のオスコネクタ。液晶の基板にはプッシュボタンのパターンも有るんですが、部品は入っていませんでした。必要なら自分で用意するみたいです。


 とりあえず、両コネクタをハンダ付け。



 液晶モジュールの基板への固定は両面テープです。


 ハード的なインストレーションは、これで終わりです。ソフトのインストール、設定の方が手間がかかりますが、Adafruitに丁寧なチュートリアルがあるので、躓くことはないと思います。


 無事、表示されて、タッチスクリーンも動作しています。あとは、使い道を考えるだけだ・・・。

2014年2月9日日曜日

BrickPi お試し その3 (PORT_AとBのモータが動かない件の調査)

 BrickPi を使い始めて、すぐ(多分、初日、もしかすると、初期不良?)、モータのPORT_AとPORT_Bが効かなくなりました。はじめてサンプルプログラムを動かした時は動いたような?動かなかったような?はじめて動かした時はモータポートのCとDを使ったような気もするし・・・。そのあたりは曖昧なんですが、結果的に今はPORT_AとPORT_Bはまともに動きません。PORT_Aは片方向には回転するんですが、動きがおかしいです。また、PORT_Bは全く回転しません。(もちろん、同じモータをPORT_CかDにつなげば期待通りの動きをします。)



 そこで、まずは、公開されているBrickPiの回路図を参照してみました。実際のボード上の部品を見ていてなんとなく予想がついてはいましたが、PORT_AとB側とPORT_CとD側で回路がわかれていて、それぞれ別のモータドライバIC(SN754410)による実装となっています。となると、PORT_AとB側のこのSN754410が怪しいので、ロジアナで入出力を調べてみることにしました。


 まずは、正常なPORT_Cの入出力をロジアナでモニタしてみます。


 上から3つA0~A2が入力信号で、A0とA1がPWM入力とEnable信号の組み合わせ、A2が方向入力です。A2の方向入力の切り替えで、A3とA4の出力信号が変化しています。A3の位相が反転(On/Offのデューティ比が反転)して、同時にA4側のレベルが反転しています。結果的にモータは同じ速さで逆向きに回転することになります。これが、正しい動き。

 次に問題のPORT_AとPORT_Bを同じように調べてみました。






 A2の方向切り替え信号に怪しい波形が出ています。HブリッジICへの入力の時点ですでにおかしい(AtmelのコントローラATMEGA328からの出力)のでしょうか?


 ただ、もう片方は、PWM信号を入力している(Enableにもなっている)のに出力がHに張り付いたままになっていて、こちらはHブリッジICが機能していないように見えます。

 まずは、試しにSN754410 を入手して、交換してみるという感じでしょうか?SN754410自体は割りとポピュラーなHブリッジドライバICなので、秋月等でも手に入ると思われます。通販で部品入手したりして、調査の続きは来週だな・・・。



2014年2月4日火曜日

BrickPi お試し その2

 先日、BrickPi を入手して、ひと通りの設定を行いましたので、簡単なプログラムで動作を試してみようと思います。試すにあたって、LEGOの機構というか駆動モデルとして、この辺りを組み立ててみました。が、Instruction通りに組んでもずれていたりするので、適当に修正してやる必要があります。今回は、NXTモータの代わりにEV3を使ったので、取付軸や穴の形状も少し違うのでそこも適宜変更しました。
 Bluetooth接続したDualShock3コントローラの左右のアナログスティックの上下方向でロボットの左右のクローラのスピードをコントロールするようなプログラムを動かしてみました。


#include <stdio.h>
#include <math.h>
#include <time.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>

#include <stdlib.h>
#include <unistd.h>
#include <linux/input.h>
#include <linux/joystick.h>

#include "tick.h"
#include <wiringPi.h>
#include "BrickPi.h"

#define MAX_ANALOG_RANGE (32767)
#define MIN_ANALOG_RANGE (-32767)
#define ANALOG_RANGE (MAX_ANALOG_RANGE - MIN_ANALOG_RANGE)

#define MAX_MOTOR_SPEED (300)

// grobal valiable.


// function prototype.


// main.
int main()
{
 int result;
 int motor1, motor2;
 int fd_j;
 char inp;
 int *axis;
 struct js_event js;
 int speedL , speedR;
 int i;

 speedL = 0;
 speedR = 0;
 axis = (int*)calloc(4, sizeof(int));
 for (i = 0; i < 4; i++)
  axis[i] = 0;

 // Open Joystick's file.
 if ((fd_j = open("/dev/input/js0", O_RDONLY)) < 0)
 {
  printf("Error in Opening Joystick.\n");
 }

 ClearTick();

 result = BrickPiSetup();
 if (result) return 0;

 BrickPi.Address[0] = 1;
 BrickPi.Address[1] = 2;

 // select the ports to be used by the motors.
 motor1 = PORT_D;
 motor2 = PORT_C;
 // enable motors.
 BrickPi.MotorEnable[motor1] = 1;
 BrickPi.MotorEnable[motor2] = 1;

 // setup the properties of sensors for the BrickPi.
 result = BrickPiSetupSensors();
 // set timeout.
 BrickPi.Timeout = 3000;
 BrickPiSetTimeout();
 
 if (!result)
 {
  while(1)
  {
   // read joystick.
   if (read(fd_j, &js, sizeof(struct js_event)) == sizeof(struct js_event))
   {
    switch (js.type & ~JS_EVENT_INIT)
    {
    case JS_EVENT_AXIS:
     axis[js.number] = js.value;
     break;
    default:
     break;
    }

    // axis values to speed.
    printf("axis[1]: %6d, axis[3]: %6d    ", axis[1], axis[3]);
    speedL = (int)((double)axis[1] / (double)ANALOG_RANGE * (double)MAX_MOTOR_SPEED * -2.0);
    speedR = (int)((double)axis[3] / (double)ANALOG_RANGE * (double)MAX_MOTOR_SPEED * -2.0);
    printf("SpeedL: %3d,   SpeedR: %3d\n", speedL, speedR);

    BrickPi.MotorSpeed[motor1] = speedL;
    BrickPi.MotorSpeed[motor2] = speedR;

    BrickPiUpdateValues();

   }

   // sleep for 10ms
   //usleep(10000);
   
  }
 }

 return 0;
}

 DualShock3と
 
   sudo sixad -start

で、コントローラのPSボタンを押して接続して、プログラムを実行します。
 とりあえず、それっぽく動いているんですが、実は、はじめモータをポートAとBにつないで動かしていたんですが、途中で動きがおかしくなって、ポートCとDに差し替えて動かしています。もしかして、もう壊れた?まだ、2~3日しか使ってないのに・・・。ハードがおかしくなったのか、週末に切り分け・調査を行ってみます。

2014年2月1日土曜日

BrickPi を入手しました。

 KickStarter 等で(自分の中で)少し話題になっていた BrickPi を入手できたので、試してみました。今回は、 ROBO PRODUCT さん から入手しました。 ROBO PRODUCT さんは、LEGOのマニアックなセンサーとかその筋の人にはたまらないものを扱っている販売店さんです。自分も以前に、NXShield-Mx とか、 PFMate とか Motor Multiplexer とか、日本では入手しにくい感のあるマインドセンサー社とかデクスタインダストリー社の製品をお世話してもらっています。

 まずは BrickPi のサイトにある Getting Started に従って、組み立てと設定です。

 今回は、 ROBO PRODUCT さん から BrickPi Starter Bundle というBrickPiを始めるのに便利な一式セットがあったのを入手しました。無線LANドングル等は自分の使いたい別のものがあったりするので、無駄になるパーツもあるのですが、何より「BrickPiの環境を構築済みのwheezy raspbianイメージ入りSDカード」がついてくるので、セットアップの手間が省けるだろうということで・・・。
 この部分が楽しいんだ!(まあ、自分もわりとそうなんですが、今回は、手抜きしました)という人は自分で構築するほうがいいかもしれません。

 まずは、届いたダンボールを開梱。




 この時点でちょっと残念だったのが、バッテリーボックスが単三×8本をセットして006Pで使うようなスナップ電極が付いている秋月とかでも売っているような普通のものだったこと。バッテリボックスもMindstorms のペグ用の穴が開いていてビームに固定できるようになっていると思ったんですが、そうはなっていませんでした。せっかく、本体(BrickPiのアクリルケース)はペグ用の穴が切ってあってビーム等とジョイントできるようになっているのに、電池ボックスの固定方法が提供されないのは片手落ちで残念なところ。ROBO PRODUCTさんで扱っているマインドセンサー社の電池ボックスはペグ穴の開いた羽根がついていて、そういったものを期待していたんですが・・・。

 では、組み立てを行います。


 ベースとなる上下のアクリル板は表裏に保護シート(紙)が貼ってあるので、剥がします。



 下側のアクリル板(ロゴの入っていない方)にRaspberry Piを固定するための短い支柱を2本固定します。



 この時点で、ネジを完全に閉め込んで支柱を固定してしまうよりは、少し、ゆるめておいて、Raspberry Pi を載せた後、順次、締めあげて、固定したほうがいいでしょう。


 次にRaspberry Piを2本のシャフトに合わせて固定します。(写真がぶれてしまいました・・・スミマセン)。先ほど、少しゆるめておいた裏側のネジとRaspberry Pi側のネジの両方4本ともを忘れずに締め上げます。


 Raspberry Pi を固定したら、次は長い支柱4本を下側プレートの四隅にネジ止めします。ここも完全に締め込まずに、あとで、全体を交互に締めていくようにしました。



 次にBrickPiの本体というかシールドというかをRaspberry Piに挿します。コネクタの位置の関係で向きを間違えて挿すということも無いかと思います。ピンがずれないように確認して、コネクタを奥まで差し込みます。ただ、コネクタをおくまでさしても、このシールドがけっこうグラグラします。シールドの裏側のRaspberry PiのEthernetコネクタにあたる辺りにゴム足か何かを貼り付けておいたほうがいいかもしれません。


 最後に天板(ロゴの入ったアクリル板)を取り付けます。上の写真はまだ、それぞれのネジを締めていないので形が歪んでいますが、ネジを締めればカッチリ固定されます。





 ひと通りネジを締めて、外観が組みあがりました。次に、起動と各種設定に進みますが、自分は元のSDカードはバックアップ用に保存することにして、元のSDカードから win32diskimager を使って別のmicroSDカードにイメージを複製、スイッチサイエンスさんで買ったRaspberry Pi 用のmicroSDカードアダプタで設定しました。




 組み立てが終わったので、起動して、ソフトウェアの設定、開発環境・アプリのインストール等を行っていきます。通常、Raspberry Pi をインストールした直後は SSHが無効になっていて、 VNCもインストールされていないので、HDMIモニタとUSBキーボド・マウスと有線LANを接続して、起動して設定する必要があります。今回もそのようにして起動したのですが、どうやら、このBrickPiとして提供されるSDカードイメージはSSHの許可設定もVNCのインストール(とサービスの自動起動も)既に行っておいてくれている状態です。ですので、ルータ側などでDHCPで取得したアドレスがわかれば、いきなりネットワーク経由での接続が可能です。ちなみに SSH での接続の ユーザ名/パスワードは Raspbian のデフォルト同様 pi / raspberry です。VNCのポートは 5901 、接続パスワードは raspberr (yは付かない)です。VNCで接続した時の壁紙がこんな感じでカッコいいです。


 ここからは、SSHとVNC接続でのリモートでのセットアップ作業です。

 とりあえず、アップデート。

   sudo apt-get update
   sudo apt-get upgrade

 次に無線LANを設定しました。今回は BrickPi Starter Bundle  に入っている無線LANドングルは使わず、自分がいつもRaspberry Piで常用している Planex の BT-micro3H2X を使いました。USBに挿すだけで、Raspberry Pi の 「WiFi Config」でGUIから設定可能です。のはず・・ですが、USBに接続してリブートしても WiFi Config の設定画面に表示されません。 lsusb ではデバイスは見つけています・・・。おかしいと思い、ifconfig すると、なんとwlan0が存在します。しかし、そのIPは自分の環境では全くのでたらめな数値・・・。ここで、BrickPiの設定ガイドの無線LANのページを見たところ、どうやら固定IPを振るような設定が /etc/network/interfaces ファイルに記述されていました。これを

   auto lo
   
   iface lo inet loopback

   iface eth0 inet dhcp

   allow-hotplug wlan0
   ifae wlan0 manual
   wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
   iface default inet dhcp

のように書き換えて、再起動して再度「Wifi Config」を起動することで、GUIから無線LAN設定ができるようになりました。この辺りは、固定IPで使うかDHCPで使うか等によって適宜使い分けが必要かと思います。 これで無線LANが使えるようになったので、有線LANケーブルは外してしまいます。

 OSを素からインストールしている場合は、wiringPi等色々なものをインストールする必要がありますが、今回はSDカードを購入しているので、最低限必要なものは入っています。なお、このカスタムSDイメージはここからダウンロードできるものが同じと思われます。(未確認)
 まずは、普段 Raspberry Pi の環境にインストールしているものを過去のメモを参考にしてインストールして、自分好みの環境を作りました。この辺りは、使われる方のお好みで。OpenCVのコンパイルなんかは非常に時間がかかるので、必要になった時にやればいいと思います。自分は

   Geany (プログラミング用エディタ)
   Chromium (ブラウザ)
   OpenCV
   guvcview

等をインストールしました。
 次に、サンプルのソース類を git から落としてきます。python と C のサンプルを落としてきます。

   git clone https://github.com/DexterInd/BrickPi_Python.git

   git clone https://github.com/DexterInd/BrickPi_C.git

 それぞれ、BrickPi_PythonBrickPi_C フォルダが作成され、サンプルファイルなどが展開されます。BrickPi_Python フォルダに移動して、setup.py を実行します。

   cd BrickPi_Python
   sudo apt-get install python-setuptools
   sudo python setup.py install

 Python の場合はコードの冒頭で import BrickPi すれば、ライブラリが使えるとのこと。
 C の場合は BrickPi_C/Drivers フォルダに BrickPi.htick.h というヘッダファイルがあります。また、wiringPi の wiringPi.h もインクルードする必要があります。コンパイル時にはwiringPi のライブラリをリンクする必要があります。以下の様なコマンドラインでの一例が BrickPiのサイトにありました。

   gcc -o program "Test BrickPi lib.c" -lrt -lm -L/usr/local/lib -lwiringPi

 ためしに、サンプルソースをビルドしてみます。

   cd ~/BrickPi_C/Project_Examples/simplebot
   gcc -o simplebot_simple simplebot_simple.c -ltr -lm -I/home/pi/BrickPi_C/Drivers -L/usr/local/lib -lwiringPi

 これで、 同フォルダに simplebot_simple という実行ファイルができるはずです。
 また、エディタとして Geany を使いたい場合は、


 こんな感じでビルドのオプションのところに前述のオプションを入れておけば、OKです。ただ、Warningがいっぱい出ました。詳しくは調べていません。


 自分は、最近はもっぱら、「VisualGDB」を愛用しています。有償ですが、Windows上のVisual Studioから Raspberry Pi や Beaglebone Black をクロス開発することが出来ます。


 こちらもMakefileのセッテイングに先ほどのビルドオプションをそれぞれ該当するフィールドに入れておくことでビルド可能です。


 自分はCでの開発が多いと思いますので、この VisualGDB(Visual Studio)でのクロス開発(正確にはリモート接続のセルフコンパイルとWindows上での純粋なクロスコンパイルと両方の方法が選択できる) か Geany でのセルフ開発がメインになると思いますが、Web系アプリを応用したロボットなどのために Python の開発環境も用意しておきます。 自分は Raspberry Pi での Python 開発には Adafruit の webIDE を利用させてもらっています。Adafruitのサイトに構築方法が解説されています。

   curl https://raw.github.com/adafruit/Adafruit-WebIDE/alpha/scripts/install.sh | sudo sh

 下記のような画面が表示されてインストールには数分かかります。


 インストールが終わると、通常だと、 http://xxx.xxx.xxx.xxx/ (xxxはRaspberry Pi のアドレス)でアクセスできるんですが、Webサーバの port 80 はWeb系アプリを動かすときのためにとっておきたいので、ポートの設定を変更します。

   redis-cli
   HMSET server port 3000
   exit

 これで、ポート3000での受けに変わります。一旦、sudo reboot して再起動します。
    
   http://xxx.xxx.xxx.xxx:3000/ (xxxはRaspberry Pi のアドレス)

に他のPCのブラウザでアクセスするとIDEへのログイン画面が表示されます。初回のみBitbucketの登録画面が表示されるので、登録作業を行います。ログインすると、こんな感じの画面になります。


 Webブラウザですが、オートインデントや色表示等ができます。


 これで、CとPythonでの開発環境も準備出来ました。

 ところで、無線LANドングルですが、 BrickPi Starter Bundle  に入っているコンパクトなものを使わずに、なぜわざわざ出っ張りの大きい Planex の BT-micro3H2X を使ったかというと、このドングルは無線LANとBluetoothがコンボになっているため、貴重なUSBポートを1つ節約できるからです。

 では、次にBluetoothを使えるように準備します。

   sudo apt-get install bluetooth bluez-utils bluez-compat bluez-hcidump
   sudo apt-get install libusb-dev libbluetooth-dev

で、Bluetooth 周りのインストールを行い、

   /etc/init.d/bluetooth status

と入力して、"[OK] bluetooth in running." のメッセージが返るのを確認します。


 Bluetooth が使えるようになったところで、せっかくなので、ロボット用途を前提として、PS3のDualShock3 とペアリングします。以前のメモ を見ながら・・・
 まずは、ペアリングツールを準備します。

   mkdir sixpair
   cd sixpair
   wget "http://www.pabr.org/sixlinux/sixpair.c" -O sixpair.c

でおとしてきて、コンパイルします。

   gcc -o sixpair sixpair.c -lusb

 コンパイルが通ったら、一旦、USBケーブルでDualShock3 と Raspberry Pi を接続します。



   sudo ./sipair

とすると

   Current Bluetooth master: xx:xx:xx:xx:xx:xx
   Setting master bd_addr to: xx:xx:xx:xx:xx:xx

のような表示が出て、ペアリングが行われます。


 ペアリングが成功したら、DualShock3 と Raspberry Pi の USBケーブルを外します。



 次に QtSixA のソースコードをダウンロードして、sixadの部分をコンパイルします。

   cd ~
   mkdir QtSixA
   cd QtSixA
   wget "https://sourceforge.net/projects/qtsixa/files/QtSixA%201.5.1/QtSixA-1.5.1-src.tar.gz/downloard" -O QtSixA-src.tar.gz

 ダウンロードしたファイルを展開します。

   tar zxvf QtSixA-src.tar.gz

 sixad のディレクトリに移動して、コンパイルしてインストールします。

   cd QtSixA-1.5.1/sixad
   make
   sudo make install

 sixad を起動します。

   sudo sixad -start

 DualShock3 の PSボタンを押して接続します。"Connected Sony Entertainment Wireless Controller" と表示されて、接続されます。別のターミナルをもう一つ起動して、

   cat /dev/input/js0

で、コントローラを操作して、バイナリが変動する表示が行われればOKです。Ctrl+Cで中断します。

とりあえず、利用のための準備はこんなところでしょうか。次は時間を見つけて、モータや各種センサを単体で動作させて試験するのと、車輪かクローラタイプのロボットをLEGOで組んで動かせればと思います。いつになるか、わからないけどね。












2013年12月23日月曜日

LEGO Mindostorms EV3 と Arduino を接続してみた

 EV3のコア(コントロール)ブロックにはUSB host (Aコネクタ)端子があって、ARM CPU で Angstrom Linux が動作しています。であれば、Raspberry Pi のときのようにArduinoを接続できれば、Arduinoのアナログ入力を使って色々なセンサを利用したり、EV3モータ以外のモータやラジコンサーボをPWM制御することもできて、可能性が広まります。
 そこで、EV3のUSBにArduinoを接続してみたのですが認識されません("/dev/ttyACM0"ができません)でした。どうやら、EV3のAngstromでは、USB-Serialのドライバ(CDC-ACM)がデフォルトでは入っていないようです。なので、CDC-ACMドライバを組み込んでのカーネル再構築を試してみることにしました。

 まずは、カーネルを含めて、EV3用のCプログラムをコンパイルできる開発環境を用意する必要があります。これに関しては、Mindstorms教育版の日本の代理店のAfrelさんが「カメラとの接続方法」「ネイティブLinuxプログラミング方法」等を公開してくれていますので、そちらを参考にして準備して下さい。AfrelさんではEV3教育版やNXT,EV3の拡張キット、センサ・モータ類も扱っているので何度かお世話になっていますが、こういった、貴重な情報を公開してくれるのでありがたい存在です。

今回のArduino接続用のCDC-ACMドライバの組み込みも、基本的には「カメラとの接続方法」でのカーネルの再構築方法に沿って行います。

カメラとの接続方法」にしたがって、途中まで準備、あるいは既にカーネルを構築できる環境がある前提です。「カメラとの接続方法」の1.1の(10)のところを以下で読み替え、あるいは、平行して見ていただければと思います。

---------------------------------------------------


カーネルソースコードのルートディレクトリに移動します。

   cd ~/projects/extra/linux-03.20.00.13/

カーネルの設定を変更するために以下のオプションでmake menuconfigを実行します。

   make menuconfig ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-

 Device Driversを選択してEnterキーを押して下さい。


 次にUSB support でEnter


 USB Modem (CDC ACM) supportを選択してspaceキーを2回押して、<*>状態にします。


 ESCキーを何回か押して、以下の確認画面でYesを選択して、保存して終了します。


 configファイルに書き込まれた以降の操作は、また、「カメラとの接続方法」に準じます。

---------------------------------------------------


 こうして再構築したカーネルをmicroSDで起動したEV3に改めてArduinoを接続したところ、どうやら、"/dev/ttyACM0"として認識してくれるようになりました。

 そこで、簡単な動作確認として、EV3とUSB接続したArduino UnoのAnalog入力にPSDセンサを接続して、PSDセンサの出力電圧をAD変換したものをEV3側で受信するテスト動作をさせてみました。


 Arduino側で一定周期でADを読み込んで、シリアル通信で0x05を受信したタイミングで保持しているAD測定結果値をシリアル通信で送信します。
 EV3側では0.5秒おきくらいに10回0x05をArduinoに送信して、返ってきた受信バイナリからch0の測定値をcmに変換して、コンソールに表示しています。

 以下、Arduino側のソースです。
#include <Arduino.h>
#include <MsTimer2.h>

// global variables.
int adVal;
byte sendData[16];

// Callback function of MsTimer2's interruption.
void CallbackMsTimer2()
{
 for (int i = 0; i <= 5; i++)
 {
  // Read from analog pin.
  adVal = analogRead(i);
  // Configure send data.
  sendData[2 * i + 1] = (byte)((0xff00 & adVal) >> 8);
  sendData[2 * i + 2] = (byte)(0x00ff & adVal);
 }
}

void setup()
{
 // initialize global variables.
 adVal = 0;
 for (int i = 0; i < 16; i++)
  sendData[i] = 0x00;
 sendData[0] = 0x02;
 sendData[15] = 0x03;

 // initialize Serial IF.
 Serial.begin(9600);

 // Set MsTimer2 interval to 100ms.
 MsTimer2::set(100, CallbackMsTimer2);

 // Start MsTimer2.
 MsTimer2::start();
}

void loop()
{
 if (Serial.available() > 0)
 {
  if (Serial.read() == 0x05)
  {
   // Send data on received ENQ.
   for (int i = 0; i < 16; i++)
   {
    Serial.write(sendData[i]);
   }
  }
 }
}

 EV3側のソースです。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/ioctl.h>

// Definition of Serial communication.
#define BAUDRATE 9600
#define MODEMDEVICE "/dev/ttyACM0"

// global variables.
struct termios oldTermio; // for keep original setting.

// Prototype of function.
int Serial_begin(int brate, char* devicePath);
void Serial_end(int fd);
int Serial_write(int fd, uint8_t byte);
uint8_t Serial_read(int fd);
int analogToCentimeter(int analogValue);


int main(int argc, char *argv[])
{
 int fd = -1;
 uint8_t rcvByte[16];
 int data;
 int i, j;

 // Initialize serial port.
 fd = Serial_begin(BAUDRATE, MODEMDEVICE);
 if (fd < 0)
 {
  printf("Serial port initialize Error.\n");
  exit(-1);
 }

 for (i = 0; i < 10; i++)
 {
  Serial_write(fd, 0x05);

  usleep(500);

  for (j = 0; j < 16; j++)
  {
   rcvByte[j] = Serial_read(fd);
  }

  data = (int)( (((int)rcvByte[1]) << 8) | ((int)rcvByte[2]) );
  data = analogToCentimeter(data);

  printf("%d\n", data);

  usleep(500000);
 }

 return 0;
}


int Serial_begin(int brate, char* devicePath)
{
 int fd;
 struct termios newTermio;

 // open the device.
 fd = open(devicePath, O_RDWR | O_NOCTTY);
 if (fd < 0)
 {
  // failed to open device.
  perror(devicePath);
  exit(-1);
 }

 // get original setting and keep it.
 tcgetattr(fd, &oldTermio);

  // Initialize new_term_io' control code.
 newTermio.c_iflag = 0;
 newTermio.c_oflag = 0;
 newTermio.c_cflag = 0;
 newTermio.c_lflag = 0;
 newTermio.c_line = 0;

 memset(newTermio.c_cc, '\0', sizeof(newTermio.c_cc));
 
 //  Configure port.
 //  B9600 = 9600bps(default)
 //  CS8 = 8bit, no parity, 1 stopbit
 //  CLOCAL = local(non modem control)
 //  CREAD = enable read charactor
 switch (brate)
 {
 case 300:
  newTermio.c_cflag = B300 | CS8 | CLOCAL | CREAD;
  break;
 case 1200:
  newTermio.c_cflag = B1200 | CS8 | CLOCAL | CREAD;
  break;
 case 2400:
  newTermio.c_cflag = B2400 | CS8 | CLOCAL | CREAD;
  break;
 case 4800:
  newTermio.c_cflag = B4800 | CS8 | CLOCAL | CREAD;
  break;
 case 9600:
  newTermio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
  break;
 case 19200:
  newTermio.c_cflag = B19200 | CS8 | CLOCAL | CREAD;
  break;
 case 38400:
  newTermio.c_cflag = B38400 | CS8 | CLOCAL | CREAD;
  break;
 case 57600:
  newTermio.c_cflag = B57600 | CS8 | CLOCAL | CREAD;
  break;
 case 115200:
  newTermio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
  break;
 default:
  newTermio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
  break;
 }


 // Setting parity error is ignore.
 newTermio.c_iflag = IGNPAR;

 // Raw mode output.
 newTermio.c_oflag = 0;
 // Setting input mode. non-canonical, no echo,
 newTermio.c_lflag = 0;
 // inter-character timer
 newTermio.c_cc[VTIME] = 0;
 // Read block still received one char.
 newTermio.c_cc[VMIN] = 1;
 // Clear modem line.
 tcflush(fd, TCIFLUSH);
 
 // Apply new configure.
 tcsetattr(fd, TCSANOW, &newTermio);

 // Wait.
 sleep(3);

 // Return file descriptor.
 return (fd);
}

void Serial_end(int fd)
{
 if (fd >= 0)
 {
  tcsetattr(fd, TCSANOW, &oldTermio);
  close(fd);
 }
}

int Serial_write(int fd, uint8_t byte)
{
 ssize_t res;

 res = write(fd, &byte, 1);

 return (int)res;
}

uint8_t Serial_read(int fd)
{
 uint8_t c;

 read(fd, (char *)&c, 1);

 return c;
}

int analogToCentimeter(int analogValue)
{
    return (int)((6787 / (analogValue - 3)) - 4);
}

 EV3の実行結果をターミナルで見たところ。


 PSDセンサの前で手や板を前後させて、値がそれっぽく変化するのを確認しました。