ルンバをESP32でBluetooth通信で動かす方法
更新情報: 最終更新日 2019/07/24 記事を追加
2019年度のアルバイト学生さんがまとめてくれた内容です。間違い等があるかも知れません。お気づきの点がございましたら、ご連絡下さい。
PCからESP32を介して、Bluetooth通信でルンバを動かす方法について書く。

環境
- ルンバ:iRobot Create 2
- ESP32:ESP32-DevKitC ESP-WROOM-32開発ボード
(秋月電子通商ページ(通販コード:M-11819)) - ESP32のコードエディタ(バージョン):ArduinoIDE(Arduino 1.8.9)
- ライブラリ:Arduino-esp32(バージョン 1.0.2)のBluetoothSerial
- PCのOS:Windows10
ルンバのマニュアル:https://www.irobotweb.com/-/media/MainSite/PDFs/About/STEM/Create/iRobot_Roomba_600_Open_Interface_Spec.pdf?la=en
ルンバを動かすにあたり、必要な知識は5つ!
これらの説明の後に、全体のプログラムコードと手順を書く。
ルンバのシリアルポート

ルンバのシリアルポートは7ピンあるが、機能は5種類。
- ピン1(, 2):電源供給に使うピン。ルンバの充電状況によって出てくる電圧値が異なるので、レギュレータを挟んで5Vに整えてからESP32の5Vピンにつなぐ。(PCとESP32 を有線でつなぐ場合は、つながない)
- ピン3:ルンバへのインプット。ESP32からの波形をそのまま入れてもよい(HIGHと認識される最低電圧が2.0Vのため)し、トランジスタなどでレベル変換して5Vの波形にしてから入れてもよい。
- ピン4:ルンバからのアウトプット。抵抗を挟むなどして5Vから3.3Vに落とし、ESP32に入れる。
- ピン5:ボーレート変更用のピン。Sleep状態からルンバを起こすのにも使う。ピン3と同様に、ESP32から3.3Vの波形をそのまま入れるか、5Vにレベル変換して入れる。
- ピン6(, 7):グラウンド。ESP32のGNDピンとつなぐ。
全体の回路としては例えばこんな感じになる。

- 使用している電子部品
ルンバのOIモード4つ(Manual p.7)
ルンバのOpen Interface(OI)モードには、4つのモードがある。
- Off Mode
- バッテリー交換後や電源を入れたときのモード
- 115200 bps でのStartコマンドのみ受け付ける
- (Manual p.4の方法でデフォルトのボーレートを変更した場合は、19200bpsでのStartコマンド)
- Passive Mode
- センサー値が受け取れる
- モーター等のアクチュエータが動かせない
- 5分間入力がないと、節電のためSleep状態になる。BRCピンへのパルス入力でSleepから起こせる。
- Safe Mode
- タイヤが浮いているなどの一部の状況を除いて、ルンバをコントロールできる
- 充電されない
- 使い終わったら、ルンバを持ち上げてタイヤを浮かせ、Passive Modeにする。これにより、充電ドックに入れると充電されるようになる。
- Full Mode
- ルンバをコントロールできる
- 充電されない
つまり、ルンバを走らせたいなら Safe Mode にする!充電したいなら Passive Mode にする!ということ。
ルンバに送るコマンドの仕組み
ESP32からルンバへ送る命令には、コマンドを使う。
コマンドでは、最初に 1 byte の opcode(コマンド番号)を送る。コマンドによっては、後に続けて補足情報 1~4 byte を送るものもある。
コマンドはルンバのマニュアルに載っている。
例)Drive Directコマンド(Manual p.14, Opcode: 145)の場合

(マニュアル p.14より)
文章から、このコマンドは左右のタイヤについて別々に速度(単位 mm/s)を指定して動かすコマンドだとわかる。
上部のOpcodeとData Bytesより、このコマンドは 5 byte から成るとわかる。中央部のSerial sequenceを見ると、 5 byte の内訳がわかる。
- 1 byte 目 Opcode の「145」
- 2 byte 目 右タイヤの速度の上位 1 byte
- 3 byte 目 右タイヤの速度の下位 1 byte
- 4 byte 目 左タイヤの速度の上位 1 byte
- 5 byte 目 左タイヤの速度の下位 1 byte
このように、コマンドでは 16 bit の数値を上位 8 bit と下位 8 bit に分けて送る。コマンドを送る度に逐一分けるのは面倒なので、関数化する。
HardwareSerial Roomba(2); // ESP to Roomba, rx:16, tx:17
void roomba_send_num(int num){ //numを二つの8bitに変換してルンバに送信
Roomba.write(hex_convert_to8_high(num));
Roomba.write(hex_convert_to8_low(num));
}
//整数を8bitに分けるプログラム ビットシフトを使っている
unsigned int hex_convert_to16(int a, int b){
return (unsigned int)(a << 8)|(int)(b);
}
unsigned int hex_convert_to8_high(int a){
return (unsigned int)(a >> 8)&0x00FF;
}
unsigned int hex_convert_to8_low(int a){
return a^(hex_convert_to8_high(a) << 8);
}
これらの関数を用いて、先ほどの Drive Direct コマンドも関数化してしまうとこんな感じ。
void roomba_drive(int right,int left){ //直進
Roomba.write(byte(145));
roomba_send_num(right); //Velocity right
roomba_send_num(left); //Velocity left
delay(100);
}
ルンバの主なコマンド
ルンバを前進・後進、回転させるにあたり、このページに記載のコードでは次のコマンドを用いている。
- Startコマンド (Manual p.8, Opcode: 128)
- OIをスタートする命令。
- 他のすべてのコマンドよりも先に送らなくてはいけない。
- Baudコマンド(Manual p.9, Opcode: 129)
- ボーレートを変える命令。
- ルンバのボーレートはデフォルトで115200bpsであるが、ESP32は115200bpsで通信できない。そのため、Baudコマンドでルンバのボーレートを変える。
- デフォルトのボーレートを115200bpsから19200bpsに変える手法が他にある。(Manual p.4)
- Safeコマンド(Manual p.10, Opcode: 131)
- ルンバのOIモードをSafe Modeにする命令。
- 起動時や充電後などのPassive ModeからSafe Modeに変えるのに使う。
- Drive Directコマンド(Manual p.14, Opcode: 145)
- 左右のタイヤのそれぞれに対して速度を指定し、走らせる命令。
- 左右同じ値を指定することで直進
- Driveコマンド(Manual p.13, Opcode: 137)
- 速度と半径を指定して回転させる命令。
- 速度に0を指定することで、移動中のルンバをストップさせるのに使用。
他にも、音を鳴らすなど色々なコマンドがある。
ルンバにコマンドを送る前に必要な処理
① Passive ModeのSleepから起こす
- "To disable sleep, pulse the BRC pin low"(Manual p.7, Passive 内参照)
- このページのプログラムでは500msのlowパルスを送信している
② ボーレート変更(115200 → 19200)
(任意。このページのプログラムでは行わない。19200 bps以外のbaud rateにするなら手順⑤で115200から直接変えてOK)
- "After turning on Roomba, wait 2 seconds and then pulse the BRC pin low three times."(Manual p.4, Method 2 内参照)
③ StartコマンドでOI開始 ←重要!すべてのコマンドよりも前に送る必要あり
- Startコマンド(Manual p.8, Opcode: 128)
- Baudコマンド含め、全コマンドよりも前にStartコマンドを送るのがとても大切。
- × Baudコマンド → Startコマンド
- そもそもOIが開始されていないのでBaudコマンドが受け付けられない。ボーレートが変更されないので、変更後のボーレートでStartコマンドを送っても正しく届かない。
- 〇 Startコマンド → Baudコマンド
- OIが開始されてからBaudコマンドが届けられ、ボーレートがきちんと変更される。
- × Baudコマンド → Startコマンド
④ Safe Modeにする
- Safeコマンド(Manual p.10, Opcode: 131)
- ルンバを走らせることができるようになる
⑤ ボーレート変更
(任意。このページのプログラムでは、115200 → 9600。)
- Baudコマンド(Manual p.9, Opcode: 129)
- ボーレート変更後、一旦ルンバとのシリアル通信を切って再度接続
⑥ ①,③,④を再度行う
(⑤のボーレート変更を行った場合のみ)
ルンバを動かすプログラム
プログラムは次の2つのファイルに分かれている。
- roomba_esp_Bluetooth.ino : メイン部分
- roomba_function.ino : ルンバへの命令の関数群
roomba_esp_Bluetooth.ino:
/* roomba_esp_Bluetooth.ino(最終更新日:2019/07/23) */
/* ESP32とBluetooth通信してルンバを動かすプログラム。
* roomba_function.ino と同じフォルダに入れてください。
* BluetoothSerial bt: PC to ESP32
* HardwareSerial Roomba: ESP32 to Roomba
*/
#include <BluetoothSerial.h>;
/* Bluetooth通信 */
BluetoothSerial bt; // PC to ESP. Alternative to "Serial" after Serial.begin()
const char* bt_name = "ESP32_BluetoothSerial";
/* ESP32とルンバの通信 */
HardwareSerial Roomba(2); // ESP to Roomba, rx:16, tx:17
int ddPin = 4;
int iB;
int v = 100; // 速度
void setup() {
Roomba.begin(115200); // ルンバはデフォルトが115200bps
Serial.begin(9600);
bt.begin(bt_name);
pinMode(ddPin, OUTPUT);
bt.println("start");
wakeUp(); // Passive ModeでのSleepから起こす
startSafe(); // StartコマンドでOI開始 & Safe Modeにすることで、移動指示可能にする
//baud rateの変更(115200→9600)
Roomba.write(129); // 必ずStartコマンド(128)の後に行う!
Roomba.write(byte(5)); //9600に変更
Roomba.end(); //一旦切る
Roomba.begin(9600); //9600でスタート
wakeUp(); // Passive ModeでのSleepから起こす
startSafe(); // StartコマンドでOI開始 & Safe Modeにすることで、移動指示可能にする
bt.println("setup completed -------");
}
void loop() {
// ESP32 to Roomba
if (bt.available() &amp;amp;amp;gt; 0){
iB = bt.read();
if (iB == 'a'){
roomba_drive(v, v);
/*Roomba.write(145);
Roomba.write(255);
Roomba.write(56);
Roomba.write(255);
Roomba.write(56);*/
bt.println("ahead");
}else if(iB == 'b'){
roomba_drive(-v, -v);
bt.println("back");
}else if(iB == 'c'){
roomba_moter_stop();
bt.println("stop");
}else if(iB == 'r'){
roomba_drive_turn_clockwise(v);
bt.println("turn right");
}else if(iB == 'l'){
roomba_drive_turn_counterclockwise(v);
bt.println("turn left");
}else if(iB == 's'){
startSafe();
bt.println("safe");
}else if(iB == 'p'){
startPassive();
bt.println("passive");
}else if(iB == 'w'){
setup();
bt.println("wakeup");
}
}
}
roomba_function.ino:
/* roomba_function.ino */
/* ルンバへの動作命令などの関数群。
* roomba_esp_Bluetooth.ino と同じフォルダに入れてください。
*/
void wakeUp(void){ //起動
digitalWrite(ddPin, HIGH);
delay(100);
digitalWrite(ddPin, LOW);
delay(500);
digitalWrite(ddPin, HIGH);
delay(2000);
}
void startSafe(){ // Startコマンド(128)でOI開始 & Safe Modeにする(131)
Roomba.write(128); //start
Roomba.write(131); //safe mode
bt.println("changed to SAFE mode");
delay(100);
}
void startPassive(){ //passivemodeに移行
Roomba.write(128); //start
delay(100);
//bt.println("startPassive開始したよ");
}
void roomba_drive(int right,int left){ //直進
Roomba.write(byte(145));
roomba_send_num(right); //Velocity right
roomba_send_num(left); //Velocity left
delay(100);
}
void roomba_moter_stop(){ //モーターを止める
Roomba.write(137);
roomba_send_num(0); //Velocity 0mm/s
roomba_send_num(0); //Radius 0 速度が0なのでなんでも良い
//bt.println("モーターを止める");
delay(100);
};
void roomba_drive_turn_counterclockwise(int num){ //反時計回り 引数は速さ
Roomba.write(137);
roomba_send_num(num); //Velocity 100mm/s
roomba_send_num(1); //Radius 1
//bt.println("反時計回り");
delay(100);
};
void roomba_drive_turn_clockwise(int num){ //時計回り 引数は速さ
Roomba.write(137);
roomba_send_num(num); //Velocity
roomba_send_num(-1); //Radius
//bt.println("時計回り");
delay(100);
};
void roomba_send_num(int num){ //numを二つの8bitに変換してルンバに送信
Roomba.write(hex_convert_to8_high(num));
Roomba.write(hex_convert_to8_low(num));
}
//整数を8bitに分けるプログラム ビットシフトを使っている
unsigned int hex_convert_to16(int a, int b){
return (unsigned int)(a << 8)|(int)(b);
}
unsigned int hex_convert_to8_high(int a){
return (unsigned int)(a >> 8)&0x00FF;
}
unsigned int hex_convert_to8_low(int a){
return a^(hex_convert_to8_high(a) << 8);
}
このプログラムでは、ルンバとESP32の通信にHardwareSerialを使っている。
ESP32-DevKitCでは、IO16,IO17ピンがUART2のrx, txにデフォルトで設定されている。HardwareSerial Roomba(2); と定義すると、IO16ピンで受信、IO17ピンで送信することができる。
ルンバをBluetooth通信で動かす手順
上記のプログラム(roomba_esp_Bluetooth.ino, roomba_function.ino)を用いる。 (Unityを通じても、ArduinoIDEのシリアルモニタを通じてもルンバを動かすことができる。Unityを用いる場合は、Unity内にESP32とのシリアル通信のプログラムが必要。また、Unityで通信するときにArduinoIDEのシリアルモニタを開いてはいけない(Port busyになる)。)
① ルンバを充電する
- 充電ドックに入れて、ルンバを充電する。
- ルンバはPassive Modeでないと充電されない。Passive Modeにするには、ルンバを持ち上げてタイヤを浮かせるのが手っ取り早い。
② ESPの回路と、ESP32-ルンバのコネクタを用意する
- ESP32が出した3.3V波形を、5Vにレベル変換してルンバに送る
- ルンバは0-5VのTTL通信(Manual p.3),ESP32は3.3V通信
- ルンバからの電源は、レギュレータで5Vにする
- 整流されておらず、バッテリー残量によって電圧値が変わる
- 「ESP32→ルンバ」が2ピン(BRC, RXD),「ルンバ→ESP32」が1ピン(TXD),ルンバからの電源供給ピン(Vpwr, GND)
③ESPにプログラムを書きこむ
④ ESPをルンバとつなぐ
- PCとUSBケーブルでつないだまま、ESP32をルンバにつながないように注意
- USBケーブルを抜いてから、ルンバとコネクタでつなぐ
- ESP32にPCとルンバの双方から電源供給されてしまうため
- つないだときに、ルンバが「ピッ」と音を鳴らすことがある
- Startコマンド(Manual p.8)により、Off ModeからPassive Modeに変わったときに鳴る
- すでにPassive Modeだと鳴らない

⑤ BluetoothでESP32と接続する
- ⑥で「Port busy」といわれてシリアルモニタを開けなくならないように、ArduinoIDEを先に開いた状態で、Bluetooth通信をつなぐ
- PCのBluetoothをオンにし、Bluetoothデバイスの追加から「ESP_BluetoothSerial」を探して接続
⑥ ArduinoIDEでシリアルモニタを開く
- ESP32をBluetooth接続するとCOMポートが2つ追加されるので、シリアルモニタを開くCOMポート番号はどちらなのかを確認する。確認方法は次の手順。
- Windowsの「設定」→「デバイス」(→「Bluetoothとその他のデバイス」)→「その他のBluetoothオプション」(関連設定のリスト内に青い字で書いてある。PCのBluetoothがオンになっていないと押せない)
- Bluetooth設定のウインドウが開く
- COMポートのタブを開き、ポートの名前を確認する。末尾に'ESP32SPP'とついている方が、シリアルモニタを開くときのCOMポート。
(下の図の例では、COM9)

⑦ シリアル通信でESPに命令
- 「a」送信で、ESP32から「ahead」という返信が返ってきてルンバが前進する(「b」→後進,「r」→右回り,「l」→左回り,「c」→停止)
- 動かない場合は「w」を送信して、初期化する(数秒かかる)
