dimanche 19 mars 2017

Boost Asio. Can not close serial port (Communicating with Arduino)

i have a problem closing the serial port using boost (1.58) asio on a linux (xubuntu 16.04) system. The problem is, the close operation never returns. The code below is my test program on linux host. Everything works fine as long as the arduino (micro) calls its read function. But if i have a blank ino file with just the serial port opened (it doesnt send nor receive) i can not close the port on linux host. Furthermore if sending to arduino the write call will block as the writebuffer(?) is full. ( sometimes after 200 bytes sent, sometimes after 300 bytes sent).

I set off the flow control on linux side and i read arduino has no flow control at all.

So i can not explain why this is happening.

#include <boost/asio/serial_port.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
#include <deque>
#include <thread>
#include <mutex>

using namespace boost;

class SerialDeviceAsyncExample
{
public:
    SerialDeviceAsyncExample();
    ~SerialDeviceAsyncExample();

    bool                    open(std::string device, unsigned long baudrate);
    void                    close();

    void                    start_reading();
    void                    stop_reading();

    int                     write(const char* buffer, int length);
    bool                    read(uint8_t& c);

private:
    std::thread             _thread;
    void                    _thread_func();
    bool                    _running;
    std::deque<uint8_t>     _buffer;
    std::mutex              _buffer_mutex;

private:
    asio::io_service        _io;
    asio::serial_port       _port;
    uint8_t                 _read();
};

SerialDeviceAsyncExample::SerialDeviceAsyncExample() : _running(true), _port(_io){}

SerialDeviceAsyncExample::~SerialDeviceAsyncExample(){}

bool SerialDeviceAsyncExample::open(std::string device, unsigned long baudrate)
{
    boost::asio::serial_port_base::baud_rate
    BAUD( baudrate) ;
    boost::asio::serial_port_base::character_size
    C_SIZE( 8 );
    boost::asio::serial_port_base::flow_control
    FLOW( boost::asio::serial_port_base::flow_control::none );
    boost::asio::serial_port_base::parity
    PARITY( boost::asio::serial_port_base::parity::even );
    boost::asio::serial_port_base::stop_bits
    STOP( boost::asio::serial_port_base::stop_bits::one );
    try{
        _port.open(device);
        _port.set_option(C_SIZE);
        _port.set_option(FLOW);
        _port.set_option(PARITY);
        _port.set_option(STOP);
        _port.set_option(BAUD);

    }catch(boost::exception&  ex){
        return false;
    }

     return true;
}

void SerialDeviceAsyncExample::close()
{
    _port.close();
}

void SerialDeviceAsyncExample::start_reading()
{
    _running = true;
    _thread = std::thread(&SerialDeviceAsyncExample::_thread_func, this);
}

void SerialDeviceAsyncExample::stop_reading()
{
    _running = false;
    _thread.join();
    _buffer.clear();
}

int SerialDeviceAsyncExample::write(const char* buffer, int length)
{
    int written = _port.write_some(boost::asio::buffer(buffer, length));

    return written;
}

bool SerialDeviceAsyncExample::read(uint8_t& c)
{
    _buffer_mutex.lock();

    if(_buffer.size() > 0){
        c = _buffer.front();
        _buffer.pop_front();
    }else{
        _buffer_mutex.unlock();
        return false;
    }

    _buffer_mutex.unlock();

    return true;
}

void SerialDeviceAsyncExample::_thread_func()
{
    while(_running)
    {
        uint8_t c = _read();
        _buffer_mutex.lock();
        _buffer.push_back(c);
        _buffer_mutex.unlock();
    }
}

uint8_t SerialDeviceAsyncExample::_read()
{
    uint8_t c = 0;

    // loops as long until a char arrives at serial device or threadfunc is canceled

    for(;_running == true;){
        try{
            asio::read(_port, asio::buffer(&c,1));
            break;
        }catch(boost::exception& e){
        }
    }
    return c;
}


int main(int argc, char** argv)
{
    SerialDeviceAsyncExample com;
    std::string device = "/dev/ttyACM0";

    if(!com.open(device, 115200)){
        std::cout << "No Device at port " << device << std::endl;
        return -1;
    }

    std::cout << "Press enter to start reading." << std::endl;
    std::cin.ignore();
    com.start_reading();
    std::cout << "Now reading into a software buffer." << std::endl;

    std::string input;

    std::cout << "Enter a string you want to send via serial port." << std::endl;
    std::cout << "Enter 'quit' to quit the loop" << std::endl;
    std::cout << "Enter 'fetch' to fetch all read bytes in the software buffer" << std::endl;

    while(true){
        std::cout << "Command: " ; std::cin >> input;

        if(input.compare(std::string("fetch")) == 0){
            uint8_t c;
            std::cout << "Buffer: ";
            while (com.read(c)){
                // print each char in hex format
                //std::cout << "0x" << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(c) << " " << std::dec;
                std::cout << c;
            }

            std::cout << std::endl;
            continue;
        }

        if(input.compare(std::string("quit")) == 0){
            break;
        }

        com.write(input.c_str(), input.size());
    }


    std::cout << "Stop reading." << std::endl;
    com.stop_reading();


    return 0;
}

The blank ino file

void setup() {
  Serial.begin(115200);

}

void loop() {    
  //while(Serial.available()){char c = Serial.read();Serial.write(c);};
}

Aucun commentaire:

Enregistrer un commentaire