複数のデバイス (ATmega328 付きの Arduino Pro Mini と GPS シールド付きの Arduino Uno) と通信する C++ プログラムを COM ポート経由で同時に作成し、boost::asio パッケージを使用しています。私は C++ の第一人者ではないので、いつどこで参照または値を渡すかを理解しようとして混乱することがあります。今回のケースもその一つだと思います。
通信用のシリアル ポートを設定するコードは次のとおりです。
std::string IMU_COM_PORT = "COM5"; // or whatever... it varies
int IMU_BAUD_RATE = 57600;
std::string ARDUINO_COM_PORT = "COM3"; // also varies
int ARDUINO_BAUD_RATE = 115200;
int main() {
std::vector<boost::thread *> sensorThreads;
int sensorThreadCount = 0;
using namespace::boost::asio;
// get the COM port handle for the IMU sensor
boost::asio::io_service IMU_io;
boost::asio::serial_port IMU_port(IMU_io);
IMU_port.open(IMU_COM_PORT);
if (!IMU_port.is_open()) {
cerr << "Failed to connect to IMU." << endl;
}
else {
IMU_port.set_option(boost::asio::serial_port_base::baud_rate(IMU_BAUD_RATE));
// the IMU has to be put into triggered output mode with a specific command: #o0
// to put it back into continuous output mode we can use this command: #o1
std::string IMU_init_cmd = "#o0";
boost::asio::write(IMU_port, boost::asio::buffer(IMU_init_cmd.c_str(), IMU_init_cmd.size()));
// now the IMU should be ready
sensorThreads.push_back(new boost::thread(testIMUThread, IMU_port));
sensorThreadCount++;
}
// get the COM port handle for the ARDUINO board with GPS sensor
boost::asio::io_service ARDUINO_io;
boost::asio::serial_port ARDUINO_port(ARDUINO_io);
ARDUINO_port.open(ARDUINO_COM_PORT);
if (!ARDUINO_port.is_open()) {
cerr << "Failed to connect to ARDUINO." << endl;
}
else {
ARDUINO_port.set_option(boost::asio::serial_port_base::baud_rate(ARDUINO_BAUD_RATE));
// now the ARDUINO w/GPS sensor should be ready
sensorThreads.push_back(new boost::thread(testGPSThread, ARDUINO_port));
sensorThreadCount++;
}
for (int i = 0; i < sensorThreadCount; i++) {
sensorThreads[i]->join();
delete sensorThreads[i];
}
}
私の 2 つのテスト スレッド関数は基本的に同じです。唯一の違いは、何か (私のコンピューター) がそれらからデータを読み取るのを待っていることを示すデバイスに送信するバイトです。
void testIMUThread(boost::asio::serial_port *port) {
while(true) { cout << readCOMLine(port, "#f") << endl; }
}
void testGPSThread(boost::asio::serial_port *port) {
while(true) { cout << readCOMLine(port, "r") << endl; }
}
そして、これらのテスト スレッドは両方とも、シリアルの読み取り/書き込みを行う単一の関数を使用します。
std::string readCOMLine(boost::asio::serial_port *port, std::string requestDataCmd) {
char c;
std::string s;
boost::asio::write(port, boost::asio::buffer(requestDataCmd.c_str(), requestDataCmd.size()));
while(true) {
boost::asio::read(port, boost::asio::buffer(&c,1));
switch(c) {
case '\r': break;
case '\n': return s;
default: s += c;
}
}
}
上記のコードはコンパイルされませんが、代わりに次のコードが生成されます (MSVC2010 の場合)。
Error 11 error C2248: 'boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>' C:\boost_1_51_0\boost\asio\basic_serial_port.hpp
根本的な原因を突き止めることができなかったので、少なくとも私にとっては、エラー メッセージはあまり役に立ちません。ここで何が欠けていますか?
ご協力いただきありがとうございます。
編集:ソリューション(Sam Millerに感謝)
参照渡しの使用 (つまりboost::ref()
)
sensorThreads.push_back(new boost::thread(testIMUThread, IMU_port));
sensorThreads.push_back(new boost::thread(testIMUThread, ARDUINO_port));
にmain()
なって
sensorThreads.push_back(new boost::thread(testIMUThread, boost::ref(IMU_port)));
sensorThreads.push_back(new boost::thread(testIMUThread, boost::ref(ARDUINO_port)));
次に、主力機能とスレッド機能がそのように変更されます
std::string readCOMLine(boost::asio::serial_port &port, std::string requestDataCmd) {
char c;
std::string s;
boost::asio::write(port, boost::asio::buffer(requestDataCmd.c_str(), requestDataCmd.size()));
while(true) {
boost::asio::read(port, boost::asio::buffer(&c,1));
switch(c) {
case '\r': break;
case '\n': return s;
default: s += c;
}
}
}
void testIMUThread(boost::asio::serial_port &port) {
while(true) { cout << readCOMLine(port, "#f") << endl; }
}
void testGPSThread(boost::asio::serial_port &port) {
while(true) { cout << readCOMLine(port, "r") << endl; }
}
一方、ポインタ渡しを使用する場合、スレッドの初期化は次のmain()
ようになります。
sensorThreads.push_back(new boost::thread(testIMUThread, &IMU_port));
sensorThreads.push_back(new boost::thread(testIMUThread, &ARDUINO_port));
そして、スレッドはこのように機能します
void testIMUThread(boost::asio::serial_port *port) {
while(true) { cout << readCOMLine(port, "#f") << endl; }
}
void testGPSThread(boost::asio::serial_port *port) {
while(true) { cout << readCOMLine(port, "r") << endl; }
}
しかし、すべての作業を行う関数、つまり読み取り/書き込み呼び出しに対して、(入力パラメーターの変更に加えて) 追加の変更を加える必要があります。
std::string readCOMLine(boost::asio::serial_port *port, std::string requestDataCmd) {
char c;
std::string s;
port->write_some(boost::asio::buffer(requestDataCmd.c_str(), requestDataCmd.size())); // this line
while(true) {
port->read_some(boost::asio::buffer(&c,1)); // and this line
switch(c) {
case '\r': break;
case '\n': return s;
default: s += c;
}
}
}
両方のソリューションがコンパイルされ、私の目的のために機能するようです。
ありがとう!