2

I'm new in doing serial port communication with boost::asio.

In my program, the code can write data to the serial port correctly. But when receiving data, segmentation fault came up.

I tried std::cout to print a string to find the wrong parts, and found that problem may be caused by the deadline_timer (sp_timeout). But I have totally no idea how to deal with it...

Here is my code:

std::vector<char> SP::asyncRead(size_t rcv_size)
    {
        sp_service->reset();
        sp_is_read_timeout = false;
        sp_available = false;
        std::vector<char> sp_char(rcv_size);
        try
        {
            boost::mutex::scoped_lock sl(*sp_mutex);
            async_read( *sp_port, 
                        boost::asio::buffer(sp_char, rcv_size),
                        boost::bind(&SP::readCallback,
                                    this,
                                    boost::ref(*sp_timeout),
                                    boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred)
                        );
            std::cout << "now OK" << std::endl;
            sp_timeout->expires_from_now(boost::posix_time::millisec(1500));
            sp_timeout->async_wait(boost::bind( &SP::timeoutCallback, 
                                                this,
                                                boost::ref(*sp_port),
                                                boost::asio::placeholders::error)
                                   );
            sp_service->run(); 
        }
        catch(const std::exception& e)
            {
                std::cerr << e.what() << '\n';
            }
        if (sp_available)
        {
            return sp_char;
        }
        else
        {
            throw "Serial port timeout";
        }
    }

UPDATE:

Minimal reproducible example provided below:

main_node.cpp

#include "sp.h"
#include <iostream>
#include <thread>
#include <boost/thread/mutex.hpp>

std::shared_ptr<SP::SP> sp{ new SP::SP{"/dev/pts/6", 115200} };

int main(int argc, char **argv)
{
    sp->open();
    uint16_t _DATA = 0x0001;

    while(true){
        try
        {
            std::vector<char> rcv = sp->read_and_write(_DATA, 10);
            for(auto i=0; i<rcv.size(); i++){
                std::cout << std::hex << rcv[i] << std::endl;
            }
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        sleep(3);
    }
    sp->close();
    return 0;
}

sp.cpp

#include "sp.h"
namespace SP
{
    SP::SP(const std::string _serial_port, const int _baud_rate){
        set_param_serial(_serial_port, _baud_rate);
    }
    SP::~SP(){
        close();
    }

    void SP::set_param_serial(const std::string _serial_port, const int _baud_rate){
        sp_serial_port = _serial_port;
        sp_baud_rate = _baud_rate;
    }

    int SP::open(){
        if(sp_port != NULL){
            return -1;
        }

        if(sp_service){
            sp_port.reset();
            sp_service.reset();
        }
            
        sp_mutex = std::shared_ptr<boost::mutex>{new boost::mutex};
        sp_service = std::shared_ptr<io_service>{new io_service()};
        sp_port = std::shared_ptr<serial_port>{ new serial_port(*sp_service) };

        try 
        {
            sp_port->open(sp_serial_port);
            sp_port->set_option(serial_port_base::baud_rate(sp_baud_rate));
            sp_port->set_option(serial_port_base::character_size(8));
            sp_port->set_option(serial_port_base::parity(serial_port_base::parity::none));
            sp_port->set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
        }
        catch (std::exception &ex){
            std::cout << ex.what() << std::endl;
        }
        return 0;           
    }
    int SP::close(){
        if(sp_port && sp_port->is_open()){
             sp_port->close();
        }
        return 0;           
    }
    void SP::write(std::vector<char> _data){
        if (sp_port->is_open()){
            boost::mutex::scoped_lock lock(*sp_mutex);
            auto size = sp_port->write_some(buffer(_data));
            if(_data.size() != size){
                throw "Write Size Error!!!";
            }      
        }
        else
        {
            throw "Port not open";
        }
    }    
    void SP::write_data(uint16_t _data)
    {
        std::vector<uint8_t> sp_data;
        sp_data.clear();
        sp_data.push_back(_data >> 8);
        sp_data.push_back(_data);
        
        std::vector<char> sp_char(sp_data.begin(), sp_data.end());
        this->write(sp_char);
    }

    std::vector<char> SP::asyncRead(size_t rcv_size)
    {
        sp_service->reset();
        sp_is_read_timeout = false;
        sp_available = false;
        std::vector<char> sp_char(rcv_size);
        try
        {
            boost::mutex::scoped_lock sl(*sp_mutex);
            async_read( *sp_port, 
                        boost::asio::buffer(sp_char, rcv_size),
                        boost::bind(&SP::readCallback,
                                    this,
                                    boost::ref(*sp_timeout),
                                    boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred
                                    )
                        );
            sp_timeout->expires_from_now(boost::posix_time::millisec(1500));
            sp_timeout->async_wait(boost::bind(  &SP::timeoutCallback, 
                                                this,
                                                boost::ref(*sp_port),
                                                boost::asio::placeholders::error
                                                )
                                );
            sp_service->run(); 
            
        }
        catch(const std::exception& ex)
        {
            std::cout << ex.what() << std::endl;
        }
        if (sp_available)
        {
            return sp_char;
        }
        else
        {
            throw "Serial port reading timeout";
        }
    }

    void SP::readCallback(deadline_timer &timeout, const boost::system::error_code &error, std::size_t bytes_transferred)
    {
        if (error)
        {
            sp_available = false;
            std::cerr << "readCallback Error " << error << std::endl;
            return;
        }
        timeout.cancel();
        sp_available = true;
    }

    void SP::timeoutCallback(serial_port &s_port, const boost::system::error_code &error)
    {
        if (error)
        {
            return;
        }
        sp_is_read_timeout = true;
        sp_available = false;
        s_port.cancel(); 
        std::cerr << "Read timeout" << std::endl;
    }

    std::vector<char> SP::read_and_write(uint16_t _DATA, int num_bytes){
        const std::lock_guard<std::mutex> lock(sp_std_mutex);
        try
        {
            write_data(_DATA);
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        std::vector<char> response;
        {
            usleep(1500);
            try
            {
                response = asyncRead(num_bytes);
            }
            catch(const std::exception& e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        return response;
    }

}

sp.h

#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>

using namespace boost::asio;

namespace SP
{
    class SP
    {
        protected:
            std::shared_ptr<io_service> sp_service;
            std::shared_ptr<serial_port> sp_port;
            std::shared_ptr<boost::mutex> sp_mutex;
            std::shared_ptr<boost::mutex> sp_func_mutex;
            std::string sp_serial_port;
            int sp_baud_rate;
            
        public:
            SP(const std::string _serial_port, const int _baud_rate);
            virtual ~SP();

        public:
            void set_param_serial(const std::string _serial_port, const int _baud_rate);
            int open();
            int close();
            void write(std::vector<char> _data);
            void write_data(uint16_t _data);
            std::vector<char> asyncRead(size_t min_rcv);
            void readCallback(deadline_timer &timeout, const boost::system::error_code &error, std::size_t bytes_transferred);
            void timeoutCallback(serial_port &s_port, const boost::system::error_code &error);
            std::vector<char> read_and_write(uint16_t _DATA, int num_bytes);

        protected:
            bool sp_is_read_timeout;
            bool sp_available;
            std::shared_ptr<deadline_timer> sp_timeout;
            std::mutex sp_std_mutex;
    };
}
3
  • The reflex to run your code in a debugger, through an address sanitizer, etc, should be engrained as instinct, even before encountering a seg-fault. Once a seg-fault is happening, it should be considered absolutely mandatory. Commented Mar 3, 2022 at 7:22
  • Please provide a minimal reproducible example Commented Mar 3, 2022 at 7:31
  • @WhozCraig Sorry, I'm new in this field, after seeing your comment, i tried using the address sanitizer, and I got many messages about boost::system, and few about my functions (e.g. read_and_write, asyncRead, ...). I cannot understand these messages, but I'll keep searching for solving them. Thanks. Commented Mar 3, 2022 at 8:32

1 Answer 1

2

I think I found where the problem is...

In my program, I use deadline_timer to wait the data, but I only declare it in the sp.h file...

After finding this issue, I add

sp_timeout = std::shared_ptr<deadline_timer>{new deadline_timer(*sp_service)};

into the SP::open(), and it works.

It's a stupid problem. Sorry for wasting your time...

Sign up to request clarification or add additional context in comments.

4 Comments

Wasn't a waste of time for you, and you learned something out of the deal. Proper tools and some self-reviewing of your code can get you the answers you need. And think about this: we didn't actually find anything ; we just suggested some basic steps you should do to help narrow down your problem. you did the real work. That's important. Don't sell yourself short.
I'd very much simplify the whole thing. It feels like there's a bit of misleading naming (asyncRead doesn't async read, it blocks) and a lot of shared/dynamic superstition. Why not something like godbolt.org/z/r8srb81v9
@sehe Thanks for your comments. I wrote the code by referring to others, and maybe I misunderstood the logic.
@sehe For another question, I read the code and found that lambda expression is used in some functions like async_read. I'm curious about the difference between this and "readCallback" in my code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.