2013年10月27日日曜日

ラジコン(の様な何か)できたよ~ ヽ(゜ω゜)ノ Ⅱ

ここ数ヶ月の休日をドブに捨てて何とかラジコンらしきものができてきたのでうp

↓概観。臓物を乗せただけ。これからボーナスをはたいた3Dプリンタが役に立つはず・・・

















↓コントローラ。androidにカメラ映像を出すのが鬼門だった。神が作りたもうたライブラリを導入することでなんとか解決。

















↓操作風景(コントローラ)、無駄にマルチタッチなどを使用。
ちなみにソナー(ではないが距離がわかるセンサ)とかコンパス、ジャイロのボタンなんかは未実装・・・(´・ω・) 一応単体動作は確認済みだけどまたそのうち実装すればいいよね・・・どうせコマンド送信するだけだし。。

 

 ↓操作風景(ラジコン側)。↑の動画を見てもわかるように操縦系がやや糞仕様。これでもだいぶましになったほうだが。

 

※諸事情(部屋の散らかり具合)により狭いエリアでの撮影となっております。







2013年10月20日日曜日

android(SimpleMjpegView)でwebcamからのストリーミングを見たときのメモ

休日2日(18時間程度)つぶしてどうにかウェブカメラ映像をタブレット上で見れるようになってのでその際のメモ、公開されているライブラリを取り込んだだけだが・・・

一応ライブラリ(SimpleMjpegView)のライセンスに基づく告知

This software is based in part on the work of the Independent JPEG Group.
Moreover, for decoding MJPEG, some codes in OpenCV are utilized.
Therefore, before downloading, you have to agree to the following license.
//////////////////////////////////////////////////////////////////////////////////
IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.
                    Intel License Agreement

            For Open Source Computer Vision Library
Copyright (C) 2000, Intel Corporation, all rights reserved.
Third party copyrights are property of their respective owners.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
  • Redistribution's of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
  • Redistribution's in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  • The name of Intel Corporation may not be used to endorse or promote products
    derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall the Intel Corporation or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.

 ライセンス告知ここまで


参考文献:
1、 neuralassemblyのメモ 様:
http://neuralassembly.blogspot.jp/2012/12/androidwifimjpeg.html
今回はTakashi Kanamaru 氏が公開されているありがたいライブラリ「SimpleMjpegView」を自分のプロジェクトに使う。
 
2、yahoo知恵袋 Android NDKを使った開発環境の構築
http://note.chiebukuro.yahoo.co.jp/detail/n136598
今回の話は実質的にNDKに関するものなので重要。


・今回の目的 : 自作androidアプリに(ゲームボーイ的な感じで)ウェブカメラからのリアルタイム映像を映す

・手法 : 数日粘ったが自分の手には負えそうも無かったので参考文献1のライブラリを利用させて頂くことに

・利用させて頂き方 : 
※事前にNDKが使える状態になっていること

1、 https://bitbucket.org/neuralassembly/simplemjpegview からライブラリをダウンロード、展開→eclipseへプロジェクトをインポート

2、インポートしたプロジェクトが正常に動く(ウェブカメラのストリーミングが見られること)ことを確認する


3、問題なければMjpegView.java と MjpegInputStream.javaを自分のプロジェクトのsrcにコピー。jniフォルダもコピー(自分は他にndkを使っていなかったから問題なかったが自作ndkがある場合はmakeファイル内容などを上手くマージしたりする必要があるかもしれない)

4、 コマンドプロンプトで自分のプロジェクトのフォルダに移動(cd (プロジェクトフォルダパス))して「C:\\android_ndk\\android-ndk\\ndk-build.cmd(筆者の場合)」を実行。要はjniフォルダ内のcファイルをビルドする。旧バージョンだとcygwinからやらないといけないようだが参考文献1のリンクなどを参照のこと

※重要 : ここで半日詰まった。参考文献2にもあるがjniフォルダ内のcファイルとヘッダファイル内の一部を自分のプロジェクト名に(規則にのっとって)改名する必要がある。↓
「JavaとC/C++(或いはその他の言語)によるプログラムの間で相互に連携をするためには、JNI(Java Native Interface)という仕様に沿う必要があります。この仕様では、C/C++側の関数名は頭にJava_を付け、その後ろにパッケージ名、クラス名、 メソッド名を並べ、ドットをアンダーバーに置き換えた名前にします。」(参考文献2より引用)

コピーしてきた時点でcファイルとヘッダファイル内の関数名はインポートしたSimpleMjpegViewプロジェクトのパッケージになっているので自分のパッケージ名にすべて変更する。これをしないとリンクエラーが発生する(そんなnativeメソッド無いよ。というエラーが発生する)。まとめて変更するための情報が参考文献2にあるが今回は手動で変換した

5、インポートしたプロジェクト内のMjpegActivity.javaのソースを参考に必要な部分を自分のライブラリに書き足す。最低限以下の部分をコピペすれば動作はする(ポーズなどで落ちるが)
5-1 onCreate内
     mv =(MjpegView) findViewById(R.id.mjpegView1);
         if(mv != null){
                mv.setResolution(640,480);
            }
            new DoRead().execute(URL);

 5-2 activityクラス内、onCreateなどと並列に
    public class DoRead extends AsyncTask<String, Void, MjpegInputStream> {
        protected MjpegInputStream doInBackground(String... url) {
            //TODO: if camera has authentication deal with it and don't just not work
            HttpResponse res = null;
            DefaultHttpClient httpclient = new DefaultHttpClient();
            HttpParams httpParams = httpclient.getParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, 5*1000);
            Log.d(TAG, "1. Sending http request");
            try {
                res = httpclient.execute(new HttpGet(URI.create(url[0])));
                Log.d(TAG, "2. Request finished, status = " + res.getStatusLine().getStatusCode());
                if(res.getStatusLine().getStatusCode()==401){
                    //You must turn off camera User Access Control before this will work
                    return null;
                }
                return new MjpegInputStream(res.getEntity().getContent());
            } catch (ClientProtocolException e) {
                e.printStackTrace();
                Log.d(TAG, "Request failed-ClientProtocolException", e);
                //Error connecting to camera
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "Request failed-IOException", e);
                //Error connecting to camera
            }
            return null;
        }

        protected void onPostExecute(MjpegInputStream result) {
            mv.setSource(result);
            if(result!=null) result.setSkip(1);
            mv.setDisplayMode(MjpegView.SIZE_BEST_FIT);
            mv.showFps(false);
        }
    }


結果:
そりゃあ所詮無線のカメラなので若干のラグはあるがかなりスムーズ。Takashi Kanamaru 氏は間違いなく神



以上。結局NDKの基本的なところが全くわかっていなかったのが時間をとった原因だったり・・・



2013年10月13日日曜日

秋月サーボの大事な覚書き(arduinoなんかで使う際の設定値)

みんな大好き秋月サーボ(GWSサーボ)の使い方についてのメモ

参考文献:
1、GWS(サーボの会社)にあるサーボのアプリケーションノート(初見だと場所がわかりにくい)
http://www.gws.com.tw/english/product/servo/sat%20form.htm

2、Arduino 日本語リファレンスのservoのページ

要点 : 秋月で売ってるGWSのサーボを使うときはServoライブラリのデフォルトのパルス幅(min:544、max:2400)を使ってはいけない。特にmin側はぶっ壊れるかもしれないので注意

参考文献1によるパルス幅:

0.8~2.2 マイクロ秒 (800 ~ 2200ms)

※以下、個人的なメモ
 
筆者用設定値(スペック超えているため多分サーボが壊れる。自己責任で・・というよりこの設定を使用してはいけない):

GWSMICRO/2BBMG/F (小さめの1500円のやつ) → 800~2500
・筆者環境でこの設定じゃないと180度回らなかったので破損覚悟で設定。

GWSPIC+F/BB/F(最小サイズの900の方) → 800~2200
・そもそも180度回らない。目視で120度くらいか。2500で異音がしたのでスペック値に

以上

2013年8月19日月曜日

夏休みの工作 ~pythonにおけるセンサ出力値のビット演算~

大した話ではないけど半日詰まったのでメモ。


I2Cなんかから1バイト×2(Hbit、Lbit)のような形でデータを受け取って数値に変換するときの注意点。

例:
Hbyte : 11101110
Lbyte : 00011111

→2つあわせて 1110111000011111 を作ってから数値に変換する。Hの方を左に8bitシフトしたり256を掛けたりしてから足す(あるいはHとLのorをとる)

特にこのあたりでは問題は無いが、2の補数を取って負の値を出したいときpythonでは正常に処理できない場合がある。

例:
H : 10000000
L : 11001100

あわせて 1000000011001100(符号付10進数で-32564) となる。この値をそのままpythonで扱うとpythonの中では32972として扱われる(符号なし扱いのため)。何bitの符号付整数か(どのビットが1だと負扱いなのか)がpythonに認識できないので当然こうなるといえばこうなる。しかしpythonに例えばこのビット列は16ビット符号付整数として扱ってね、とやる方法もちょっとわからなかったので普通にxorを使って2の補数を算出する処理を実装する。これで何ビット整数として扱わせるかもこちらで指定できる。

(プログラムのイメージ)

if H & 0b1000000=0b10000000:   ←Hバイトの頭が1(負数)だったら。ここの数値の桁数でビットを指定している。

   val= (H <<8) | L ←HとLを合体 1000000011001100
   val_inv=val ^ 0b1111111111111111 ←「^」はxor(pythonでは累乗は**なので注意)16bitの全部1の値とxorをとってすべてのビットを反転する。
  val_inv = val_inv+1    ←1足して2の補数にする。この時点で正しい値の絶対値になっている
 out =val_inv*(-1) ←マイナスをつける

以上。これで上の例だと outが-32564になる。
面倒だけどこれをやらないとpythonでは符号付出力のI2Cセンサがまともに使えないので地味に重要。

2013年8月10日土曜日

夏休みの工作 ~python-arduino連携でI2Cを辛うじて使う(今日の分)~

センサーとかモータードライバ(まだ碌なのがないが)を少ないPIN数で使うためにI2Cが必要だったのでありがたい外部ライブラリを勝手に拡張してみた。

今回の参考資料:Tristan Hearn氏が作ってくださった以下のライブラリ。どうやらNASAの方でお勤めの方っぽい((((゜Д゜;))))。ちなみにライブラリはMITライセンス。
 ↓入手場所
https://github.com/thearn/Python-Arduino-Command-API

↓MITライセンスに基づく告知(ここから)
Copyright (c) 2012-2013 Tristan A. Hearn <tristanhearn@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

↑(ここまで)

今回は上記ライブラリのソースを勝手に弄ってしまうお話です。MITライセンス万歳。

↓python側のプログラム。これを自分のプログラムの上のほうに差し込んでおきます。要上記ライブラリとそれを動かすのに必要なもの

from Arduino import Arduino
class Arduino_C(Arduino):
    def __init__(self,baud=9600,port=None,timeout=2):
        Arduino.__init__(self, baud, port, timeout)

    def i2c_write_1(self,device,data):
        print "i2cwrite"
        cmd_str=''.join(["@i2cw1%",str(device),"%",str(data),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")
        print "i2c return",hex(int(rd))
    def i2c_write_2(self,device,data1,data2):
        print "i2cwrite"
        cmd_str=''.join(["@i2cw2%",str(device),"%",str(data1),"%",str(data2),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")
        print "i2c return",hex(int(rd))

    def i2c_read_1(self,device,location):
        cmd_str=''.join(["@i2cr1%",str(device),"%",str(location),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd = self.sr.readline().replace("\r\n","")

        output=int(rd)
        return output

    def i2c_read_2(self,device,location):
        cmd_str=''.join(["@i2cr2%",str(device),"%",str(location),"$!"])
        try:
            self.sr.write(cmd_str)
            self.sr.flush()
        except:
            pass
        rd1 = self.sr.readline().replace("\r\n","")
        rd2 = self.sr.readline().replace("\r\n","")
        output=[int(rd1),int(rd2)]
        return output

↑筆者の頭脳とやる気の関係で1バイトまたは2バイトの書き込み、読み込みの4つの関数を追加しただけ、せめてバイト数ぐらいは可変にすべきだが・・・しょうがないよね、夏風邪引いてるしね。

引数のdeviceとかdata、locationとかは16進数(pythonでも0x00表記)でやると頭で変換しなくていいから楽。

↓以下、arduino側のソース(prototype.ino)に追加した部分のその1。serial_parser関数より上のどこかに差し込む。

void i2c_write_1b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int wdata = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(wdata);
    Wire.endTransmission();
    Serial.println(id);
}

void i2c_write_2b(String data){
    String sdata[3];
    split(sdata,3,data,'%');
    int id = Str2int(sdata[0]);
    int wdata1 = Str2int(sdata[1]);
    int wdata2 = Str2int(sdata[2]);
    Wire.beginTransmission(id);
    Wire.write(wdata1);
    Wire.write(wdata2);
    Wire.endTransmission();
    Serial.println(id);
}

void i2c_read_1b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int location = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(location);
    Wire.endTransmission(false);
    Wire.requestFrom(int(id),1,false);
    int out1 = Wire.read();              
    Wire.endTransmission(true);
    Serial.println(out1);
   
}

void i2c_read_2b(String data){
    String sdata[2];
    split(sdata,2,data,'%');
    int id = Str2int(sdata[0]);
    int location = Str2int(sdata[1]);
    Wire.beginTransmission(id);
    Wire.write(location);
    Wire.endTransmission(false);
    Wire.requestFrom(int(id),2,false);
    int out1 = Wire.read();              
    int out2 = Wire.read();
    Wire.endTransmission(true);
    Serial.println(out1);
    Serial.println(out2);
}

↑ここまで

↓その2。serial_parserのcmd==(コマンド)のelse ifが続いているあたりに自然に差し込む。

else if (cmd =="i2cw1"){
   i2c_write_1b(data);
  }
    else if (cmd =="i2cw2"){
   i2c_write_2b(data);
  }
  else if (cmd =="i2cr1"){
   i2c_read_1b(data);
  }
  else if (cmd =="i2cr2"){
   i2c_read_2b(data);
  }

↑ここまで

明らかに命令の種類が足りてないような気がするが、ストリナとかで売ってるようなジャイロとかならこれである程度読み書きできるはず。読み込んだデータバイトの処理(2つのバイトを接続したりとか単位換算とか)なんかはpython側で。

いつかHearn氏が正規(?)のI2C実装をしてくれることを祈りつつ今回は終了。

P.S : prototype.ino のsetup() 内に Wire.begin(); を入れないと何も機能しないので注意(10分ほどはまった)

2013年7月29日月曜日

メモ:いつものI2C

ArduinoでI2Cを使うときによく使うコピペ用

↓ここから
//書き込み
Wire.beginTransmission();
Wire.write(0x);

Wire.endTransmission();


//読み込み、パターンはデータシート確認のこと
Wire.beginTransmission();
Wire.write(0x);
Wire.endTransmission(false);
Wire.requestFrom(int(),1,false);
output = Wire.read();

Wire.endTransmission(true);

//連続読み込み(6つのレジスタから1byteずつ読み込む場合)
byte addr[6]={0x,0x,0x,0x,0x,0x,};
byte read_data[6]={0,0,0,0,0,0};   //2byte以上のときは2次元配列に

for(int i=0;i<6;i++){
Wire.beginTransmission( );
Wire.write( addr[i] );
Wire.endTransmission(false);
Wire.requestFrom(int( ),1,false);
read_data[i] = Wire.read();   
Wire.endTransmission(true);
  }

//データを8ビットシフトして他のbyteとつなげる。便利

int hl_joint(byte high,byte low){
  int high_data = int(high) << 8;
  int output = high_data | int(low);

  return output;
}

//レジスタ6つのデータを読み込んで繋げて出力する関数。3軸センサーに。
//devはデバイスのアドレス、xyzは出力を入れる配列(出力の型は自由)
void read_xyz(byte dev,int xyz[]){

byte addr[6]={0x,0x,0x,0x,0x,0x};
byte read_data[6]={0,0,0,0,0,0};   //2byte以上のときは2次元配列に

for(int i=0;i<6;i++){
Wire.beginTransmission(dev);
Wire.write( addr[i] );
Wire.endTransmission(false);
Wire.requestFrom(int(dev),1,false);
read_data[i] = Wire.read();   
Wire.endTransmission(true);
  }
// read_data[0]=Low_byte ,read_data[1]=High_byte・・・の場合

for(int j=0;j<3;j++){
 xyz[j]=hl_joint(read_data[2*j+1],read_data[2*j]);
}

}

とりあえず以上

2013年7月15日月曜日

夏休み前の工作(2) ~ArduinoでI2Cモータドライバを動かしたときのメモ~

秋月電子とかストロベリーリナックスで売ってるDRV8830を使ったモータードライバモジュールをかろうじて使えるようになったのでそのあたり+ArduinoでI2Cを使うにあたっての自分用メモ

この記事の内容によって生じたことの責任は取りかねますのであくまで自己責任でお願いします。


1、重要な参考文献: mixture-art@Q 様の記事(「Arduino I2C」でぐぐるとトップに出る)

http://mixture-art.net/arduino%E3%81%A7i2c%E9%80%9A%E4%BF%A1%E3%82%92%E3%82%84%E3%82%8B%E9%9A%9B%E3%81%AE%E3%83%A1%E3%83%A2/

↑参考にしないとI2C用ライブラリ(Wire)で適切なデータ受信ができなくなる。じぶんでC言語とかで解決するハメに・・・

2、DRV8830の日本語データシート(ストリナのDRV8830ドライバのページにリンクがある)

↑見ないと使い方がわからない(秋月のドライバのトリセツは情報が不十分)


とりあえず以下が最低限動くソースとコメント↓

ArduinoIDEに最初から入ってる 「master_writer」をベースに改造

#include <Wire.h>

byte m_driver=0x64;  ←A0 A1をオープンにしてるとドライバのアドレスは
「1100100」=「01100100」→16進数にすると「0110(←6) 0100(←4)」=64(HEX) となる。重要

↓モータの速度を設定する関数。適当
void setmotor(byte val,byte ori){  ←モータ出力設定(VSET)とブリッジ制御用の2ビットを入力
  Serial.print("setmot start\n");
  Wire.beginTransmission(m_driver); ←「書き込みモード」でドライバと通信開始。この命令を使うと勝手にアドレス(7桁)の末尾に「0(Write)」が加えられて通信される(長期間詰まった)
  Wire.write(0x00);  ←サブアドレス。レジスタ0(アドレス:0x00)のアドレスを送信
  byte sp6=val << 2; ←引数valを2ビット左にシフト
  byte mot = sp6|ori; ←方向の2ビットとorをとる。これで送信用のデータバイトができる。  Serial.println(mot,HEX); ←なんとなくシリアルに出力
  Wire.write(mot);  ←データバイトを送信。ちゃんと通ればモータが回ったりする。

  Wire.endTransmission();    ←通信終わり。「STOP」ビットをドライバに送信する。 
}

void fault_clear(){ ←faultレジスタ(0x01のレジスタ)内の障害状態をリセットする。フェールの種類によってはリセットしないとドライバが使用できない場合がある。
  Serial.print("fault_clear\n");
  Wire.beginTransmission(m_driver);
  Wire.write(0x00);       
  Wire.write(B10000000);     ←第7ビットに1を書き込むとフェールのログが消える。
             
  Wire.endTransmission();

}

void setup()
{
  Wire.begin();
 
  Serial.begin(9600);
  Serial.println("\n set ");
  Serial.println(m_driver,HEX);
 
  fault_clear();    ←最初に一応フェールをリセットしておく
}


ここからメインループ
void loop()
{
↓適当にモータを動かしてみた。第1引数は0x3fあたりが最大、第2引数は0x0,1,2,3のどれかを入力。詳しくはデータシート参照。待ち時間は別に2秒とかじゃなくていい(多分不要)
 delay(2000);
 setmotor(0x1E,0x0);
 delay(2000);
 setmotor(0x1E,0x0);
 delay(2000);
 setmotor(0x3E,0x0);
 delay(2000); 
   

↓ここからデータ読み出し。非常に重要な箇所あり(知らないとI2Cデータ受信できない)

  Serial.print("0x01 read start\n");
  Wire.beginTransmission(m_driver);  ←受信だけど書き込みモードでデータ通信開始、ドライバの仕様。
  Wire.write(0x01);        ←読みたいレジスタのアドレス
  Wire.endTransmission(false); ←参考文献より。これを入れないとドライバとの通信が維持できない。

  Wire.requestFrom(int(m_driver),1,false);  ←読み込みモードで通信開始。こちらは勝手に末尾に「1(read)」が付け加えられる。ドライバのマニュアルに「アドレス(書き込み)、アドレス(読み込み)」とか親切に書いてくれているが、そのアドレスは末尾の0,1を加えたアドレスなのでそれをこことかbeginTransimission()に入れても通信できない。こんな馬鹿なところでハマってしまってドライバを数ヶ月放置してた・・・(つд;)

  byte out = Wire.read();     ←普通にread。
  Serial.println(out,HEX);  ←シリアルに出力して内容を確認

  Wire.endTransmission(true);    ←通信終了。()内のtrueは必要。これも参考文献より
  delay(2000);

↓レジスタ0も同様にreadしてみる。
 Serial.print("0x00 read start\n");
  Wire.beginTransmission(m_driver);
  Wire.write(0x00);       
 Wire.endTransmission(false);
  Wire.requestFrom(int(m_driver),1,false);
  byte out2 = Wire.read();              
  
  Serial.println(out2,HEX);

  Wire.endTransmission(true);   
  delay(2000);
}










あと、配線について。電流センス抵抗に適切な抵抗をつないでGND接続しないといけないが、制限とか気にしないのであればここはGND直結でいい(直結(か小さい抵抗をGND接続)しないとドライバが動作しない。注意)。Faultnのピンは何も接続しなくても動作した。

以上。上記の情報でモータを動かせる・・・はず。動作確認したが動かせるモーターと動かせないモータがあった(ドライバのせいでは無く、多分電流流れすぎか何かで何らかの制限がかかってると推測)

 今回ArduinoのWireライブラリの親切設計(自動的にW,Rビットを付加)についてよく理解してなかったのがひどかった。しかしこれで他のI2Cデバイスも普通に使えるようになるはず( ´д`)b