Question

I'm writing a C++ program to communicate with multiple devices - an Arduino Pro Mini w/ATmega328 and an Arduino Uno with GPS shield - simultaneously via COM port, and I'm using the boost::asio package. I'm no C++ guru, so I occasionally get muddled trying to figure out where/when to pass by reference or value; I think this is one of those cases.

Here's the code that sets up the serial ports for communication

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];
    }
}

My two test thread functions are basically identical, the only difference being the byte(s) they send to the devices indicating that something (my computer) is waiting to read data from them.

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; }
}

And both of these test threads make use of a single function that does the serial read/writes.

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;
        }
    }
}

The above code does not compile but instead produces the following (in 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

The error message is rather unhelpful - to me, at least - as I've not been able to track down the root cause. What am I missing here?

Thanks for your help.


Edit: Solutions (thanks to Sam Miller)

Using pass-by-reference (i.e. boost::ref())

sensorThreads.push_back(new boost::thread(testIMUThread, IMU_port));
sensorThreads.push_back(new boost::thread(testIMUThread, ARDUINO_port));

in main() become

sensorThreads.push_back(new boost::thread(testIMUThread, boost::ref(IMU_port)));
sensorThreads.push_back(new boost::thread(testIMUThread, boost::ref(ARDUINO_port)));

Then the workhorse and thread functions are modified as such

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; }
}

On the other hand, if using pass-by-pointer, the thread initializations in main() look like this

sensorThreads.push_back(new boost::thread(testIMUThread, &IMU_port));
sensorThreads.push_back(new boost::thread(testIMUThread, &ARDUINO_port));

And the thread functions like this

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; }
}

But I have to make some additional changes (on top of the input parameter modification) to the function that does all of the work - namely the read/write calls.

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;
        }
    }
}

Both solutions compile and seem to work for my purposes.

Thanks!

Was it helpful?

Solution

boost::asio::serial_port objects are noncopyable

You should pass a reference

sensorThreads.push_back(new boost::thread(testIMUThread, boost::ref(IMU_port)));

or a pointer

sensorThreads.push_back(new boost::thread(testIMUThread, &IMU_port));

here's a coliru showing both methodologies.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top