ルンバとのシリアル接続は 5V の TTL 接続で行います。そのため、通常のシリアル端子とルンバを直に接続することはできません。
今回は FTDI 社の USB-Serial 変換チップ FT232RL の評価基板を用いて PC と接続するためのケーブルを作成します。ルンバの RXD, TXD, GND を接続すれば通信できます。
- 用意するもの
- FTDI 社 FT232RL の評価基板 (秋月電子通商より入手可能, リンク)
- Roomba に使えるコネクタ (千石電商より入手可能, リンク)
- ユニバーサル基板
- 2.54 pitch 単列ソケット
- 導線, 熱収縮チューブなど
- 回路図
Roomba 側コネクタのピン配置 (Top view)
PC-Roomba 用ケーブルの配線
- 作り方
- DIN コネクタにケーブルをハンダで取り付け、熱収縮チューブで覆います。
- ユニバーサル基板に FT232RL 評価基板用のソケットを配置し、RXD, TXD, GND と DIN コネクタとを接続します。
- FT232RL の評価基板を取り付けて、完成です。
DIN コネクタにケーブルを取り付けた状態
FT232RL の評価基板
評価基板のソケットを取り付け、配線を行った状態
FT232RL を取り付け、基板の余白を切り取る前の状態
作成した変換ケーブル (ケーブルはもう少し長い方が便利です)
作成したケーブルを用い、実際に PC からルンバにコマンドを送信してケーブルの動作を確認します。まずは PC からの送信が動作するかの確認のために、ルンバに音を鳴らせてみます。
ルンバとの通信プロトコルの詳細は参考文献から確認できます。
以下は C++ と Lua 版の音を鳴らすプログラムです。
ルンバで音を鳴らせるプログラム (C++)
#include <string>
#include <iostream>
using namespace hrk;
using namespace std;
int main(int argc, char *argv[])
{
static_cast<void>(argc);
static_cast<void>(argv);
const char* device = "/dev/ttyUSB0";
enum { Roomba_default_baudrate = 115200 };
if (!serial.
open(device, Roomba_default_baudrate)) {
cout <<
"Serial::open(): " << serial.
what() << endl;
return 1;
}
const char full_command[] =
"\x80"
"\x84";
serial.
write(full_command,
sizeof(full_command) - 1);
delay_msec(100);
const char sing_command[] =
"\x8c\x0\x1\x43\x05"
"\x8d\x0";
serial.
write(sing_command,
sizeof(sing_command) - 1);
delay_msec(100);
const char passive_command[] =
"\x80";
serial.
write(passive_command,
sizeof(passive_command) - 1);
#if 0
const char power_command[] =
"\x85";
serial.
write(power_command,
sizeof(power_command) - 1);
#endif
return 0;
}
ルンバで音を鳴らせるプログラム (Lua)
lib/robot/example/roomba.cpp を利用します。
-- ルンバに音を鳴らせる
local serial = Serial()
local Roomba_default_baudrate = 115200
if not serial:open("/dev/ttyUSB0", Roomba_default_baudrate) then
print("Serial::open(): " .. serial:what())
return 1
end
local full_command = "\132"
serial:write(full_command, #full_command)
print(#full_command)
delay(0.1)
local sing_command = "\140\000\001\067\032" .. "\141\000"
serial:write(sing_command, #sing_command)
print(#sing_command)
delay(1.0)
local passive_command = "\128"
serial:write(passive_command, #passive_command)
print(#passive_command)
実行方法
% ./roomba sing_roomba.lua
ここまでで、PC からルンバへの送信が確認できました。
次は、受信のテストを兼ねてルンバのバッテリー情報を取得してみます。
ルンバのバッテリー情報を取得するプログラム (C++)
#include <string>
#include <iostream>
using namespace hrk;
using namespace std;
namespace
{
bool print_charging_state(
Serial& serial)
{
const char send_command[] =
"\x80"
"\x95\x01\x15";
serial.
write(send_command,
sizeof(send_command) - 1);
enum {
Receive_size = 1,
Timeout = 100,
};
unsigned char receive_data;
int n = serial.
read(reinterpret_cast<char*>(&receive_data),
Receive_size, Timeout);
if (n != Receive_size) {
cout << "receive data failed." << endl;
return false;
}
string message;
switch (receive_data) {
case 0:
message = "0 Not charging";
break;
case 1:
message = "1 Reconditioning Charging";
break;
case 2:
message = "2 Full Charging";
break;
case 3:
message = "3 Trickle Charging";
break;
case 4:
message = "4 Waiting";
break;
case 5:
message = "5 Charging Fault Condition";
break;
default:
message = "invalid receive data.";
break;
}
cout << message << endl;
return true;
}
bool print_battery_current(
Serial& serial)
{
const char send_command[] =
"\x95\x01\x17";
serial.
write(send_command,
sizeof(send_command) - 1);
enum {
Receive_size = 2,
Timeout = 100,
};
unsigned char receive_data[Receive_size];
int n = serial.
read(reinterpret_cast<char*>(receive_data),
Receive_size, Timeout);
if (n != Receive_size) {
cout << "receive data failed." << endl;
return false;
}
short current_mili_ampere;
current_mili_ampere =
((static_cast<unsigned short>(receive_data[0]) << 8) & 0xff00) |
(receive_data[1] & 0xff);
cout << "current: " << current_mili_ampere << " [mA]" << endl;
return true;
}
bool print_battery_charge(
Serial& serial)
{
const char send_command[] =
"\x95\x01\x19";
serial.
write(send_command,
sizeof(send_command) - 1);
enum {
Receive_size = 2,
Timeout = 100,
};
unsigned char receive_data[Receive_size];
int n = serial.
read(reinterpret_cast<char*>(receive_data),
Receive_size, Timeout);
if (n != Receive_size) {
cout << "receive data failed." << endl;
return false;
}
unsigned short current_mili_ampere;
current_mili_ampere =
((static_cast<unsigned short>(receive_data[0]) << 8) & 0xff00) |
(receive_data[1] & 0xff);
cout << "current charge: " << current_mili_ampere << " [mAh]" << endl;
return true;
}
}
int main(int argc, char *argv[])
{
static_cast<void>(argc);
static_cast<void>(argv);
const char* device = "/dev/ttyUSB0";
enum { Roomba_default_baudrate = 115200 };
if (!serial.
open(device, Roomba_default_baudrate)) {
cout <<
"Serial::open(): " << serial.
what() << endl;
return 1;
}
print_charging_state(serial);
print_battery_current(serial);
print_battery_charge(serial);
return 0;
}
ルンバのバッテリー情報を取得するプログラム (Lua)
lib/robot/example/roomba.cpp を利用します。
-- ルンバのバッテリー情報を取得して表示する
local serial = Serial()
local Roomba_default_baudrate = 115200
if not serial:open("/dev/ttyUSB0", Roomba_default_baudrate) then
print("Serial::open(): " .. serial:what())
return 1
end
-- !!! not implemented
local send_command = "" .. ""
-- "\x80"
-- "\x95\x01\x15";
serial:write(send_command, #send_command)
local Timeout = 100
local receive_data = connection_read(serial, 1, Timeout)
local messages = [
"0 Not charging",
"1 Reconditioning Charging",
"2 Full Charging",
"3 Trickle Charging",
"4 Waiting",
"5 Charging Fault Condition",
]
if receive_data > #messages then
print("invalid receive data.")
return 1
end
print messages[receive_data + 1]
実行方法
% ./roomba battery_state_roomba.lua
参考文献