Simple serial port demonstration with boost asio asynchronous I/O

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Simple serial port demonstration with boost asio asynchronous I/O

Jeff Gray-3
A couple of weeks ago I posted a simple asynchronous demonstration of a
telnet client using boost asio TCP network code.

I have changed this code to turn it into a simple serial terminal
application using the serial port support in the new version of boost asio.

It required no changes at all the the read or write code. I just altered
the initialization to use a serial port instead of a socket.

Note that this is not intended to show how a fully functional
application would work. There are plenty of areas that are not handled
in this code. But it shows the idea and it's easy to build on this once
you've got it working.

Hope this helps someone else get started...
Jeff

/* minicom.cpp
        A simple demonstration minicom client with Boost asio
       
        Parameters:
                baud rate
                serial port (eg /dev/ttyS0 or COM1)
               
        To end the application, send Ctrl-C on standard input
*/

#include <deque>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>

#ifdef POSIX
#include <termios.h>
#endif

using namespace std;

class minicom_client
{
public:
        minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device)
                : active_(true),
                  io_service_(io_service),
                  serialPort(io_service, device)
        {
                if (not serialPort.is_open())
                {
                        cerr << "Failed to open serial port\n";
                        return;
                }
                boost::asio::serial_port_base::baud_rate baud_option(baud);
                serialPort.set_option(baud_option); // set the baud rate after the port has been opened
                read_start();
        }
       
        void write(const char msg) // pass the write data to the do_write function via the io service in the other thread
        {
                io_service_.post(boost::bind(&minicom_client::do_write, this, msg));
        }
       
        void close() // call the do_close function via the io service in the other thread
        {
                io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code()));
        }

        bool active() // return true if the socket is still active
        {
                return active_;
        }

private:

        static const int max_read_length = 512; // maximum amount of data to read in one operation
       
        void read_start(void)
        { // Start an asynchronous read and call read_complete when it completes or fails
                serialPort.async_read_some(boost::asio::buffer(read_msg_, max_read_length),
                        boost::bind(&minicom_client::read_complete,
                                this,
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred));
        }
       
        void read_complete(const boost::system::error_code& error, size_t bytes_transferred)
        { // the asynchronous read operation has now completed or failed and returned an error
                if (!error)
                { // read completed, so process the data
                        cout.write(read_msg_, bytes_transferred); // echo to standard output
                        read_start(); // start waiting for another asynchronous read again
                }
                else
                        do_close(error);
        }
       
        void do_write(const char msg)
        { // callback to handle write call from outside this class
                bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written?
                write_msgs_.push_back(msg); // store in write buffer
                if (!write_in_progress) // if nothing is currently being written, then start
                        write_start();
        }
       
        void write_start(void)
        { // Start an asynchronous write and call write_complete when it completes or fails
                boost::asio::async_write(serialPort,
                        boost::asio::buffer(&write_msgs_.front(), 1),
                        boost::bind(&minicom_client::write_complete,
                                this,
                                boost::asio::placeholders::error));
        }
       
        void write_complete(const boost::system::error_code& error)
        { // the asynchronous read operation has now completed or failed and returned an error
                if (!error)
                { // write completed, so send next write data
                        write_msgs_.pop_front(); // remove the completed data
                        if (!write_msgs_.empty()) // if there is anthing left to be written
                                write_start(); // then start sending the next item in the buffer
                }
                else
                        do_close(error);
        }
       
        void do_close(const boost::system::error_code& error)
        { // something has gone wrong, so close the socket & make this object inactive
                if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel()
                        return; // ignore it because the connection cancelled the timer
                if (error)
                        cerr << "Error: " << error.message() << endl; // show the error message
                else
                        cout << "Error: Connection did not succeed.\n";
                cout << "Press Enter to exit\n";
                serialPort.close();
                active_ = false;
        }

private:
        bool active_; // remains true while this object is still operating
        boost::asio::io_service& io_service_; // the main IO service that runs this connection
        boost::asio::serial_port serialPort; // the serial port this instance is connected to
        char read_msg_[max_read_length]; // data read from the socket
        deque<char> write_msgs_; // buffered write data
};

int main(int argc, char* argv[])
{
// on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress
// On other systems, you'll need to look for an equivalent
#ifdef POSIX
        termios stored_settings;
        tcgetattr(0, &stored_settings);
        termios new_settings = stored_settings;
        new_settings.c_lflag &= (~ICANON);
        new_settings.c_lflag &= (~ISIG); // don't automatically handle control-C
        tcsetattr(0, TCSANOW, &new_settings);
#endif
        try
        {
                if (argc != 3)
                {
                        cerr << "Usage: minicom <baud> <device>\n";
                        return 1;
                }
                boost::asio::io_service io_service;
                // define an instance of the main class of this program
                minicom_client c(io_service, boost::lexical_cast<unsigned int>(argv[1]), argv[2]);
                // run the IO service as a separate thread, so the main thread can block on standard input
                boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
                while (c.active()) // check the internal state of the connection to make sure it's still running
                {
                        char ch;
                        cin.get(ch); // blocking wait for standard input
                        if (ch == 3) // ctrl-C to end program
                                break;
                        c.write(ch);
                }
                c.close(); // close the minicom client connection
                t.join(); // wait for the IO service thread to close
        }
        catch (exception& e)
        {
                cerr << "Exception: " << e.what() << "\n";
        }
#ifdef POSIX // restore default buffering of standard input
        tcsetattr(0, TCSANOW, &stored_settings);
#endif
        return 0;
}

_______________________________________________
Boost-users mailing list
[hidden email]
http://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

Andrew Maclean-2
Once again a nice example.
On line 34 just change:

                if (not serialPort.is_open())
to:
                if (!serialPort.is_open())

The CMakeLists.txt file I posted in your telent client thread also
works here by changing two lines, line 1 the project name and line 92
the source file.

These are good convincing demonstrations of the utulity of asio!

Thanks
   Andrew


On Tue, Oct 7, 2008 at 12:27 PM, Jeff Gray
<[hidden email]> wrote:

> A couple of weeks ago I posted a simple asynchronous demonstration of a
> telnet client using boost asio TCP network code.
>
> I have changed this code to turn it into a simple serial terminal
> application using the serial port support in the new version of boost asio.
>
> It required no changes at all the the read or write code. I just altered
> the initialization to use a serial port instead of a socket.
>
> Note that this is not intended to show how a fully functional
> application would work. There are plenty of areas that are not handled
> in this code. But it shows the idea and it's easy to build on this once
> you've got it working.
>
> Hope this helps someone else get started...
> Jeff
>
> _______________________________________________
> Boost-users mailing list
> [hidden email]
> http://lists.boost.org/mailman/listinfo.cgi/boost-users
>



--
___________________________________________
Andrew J. P. Maclean
Centre for Autonomous Systems
The Rose Street Building J04
The University of Sydney  2006  NSW
AUSTRALIA
Ph: +61 2 9351 3283
Fax: +61 2 9351 7474
URL: http://www.acfr.usyd.edu.au/
___________________________________________
_______________________________________________
Boost-users mailing list
[hidden email]
http://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

jhodapp
In reply to this post by Jeff Gray-3
Jeff,

    I am writing to get your permission to use your simple serial port example, minicom, based on boost::serial_port in my open source project. Your example was most useful and the only example that I could find that would adequately show an effective way to use serial_port and also boost::asio::io_service with it. I have based the structure of my implementation on what you have done with some adaptations. For your information, my project will be licensed under the Simplified BSD License.

Thanks,

Jim
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

drwho
This post has NOT been accepted by the mailing list yet.
In reply to this post by Andrew Maclean-2
New to boost. (Ubuntu 10.10) Downloaded boost 1.47 source.  Built it with ./bootstrap.sh and ./b2 Keep getting linker failure, undefined reference to `boost::system::get_system_category() in my QT app. Tried your serial example from bash, same thing....

drwho@drwho-desktop:~/Projects/prog$ g++ -Lboost/stage/lib -lboost_system -lboost_thread serial.cpp -o test
/tmp/ccvTv9iN.o: In function `__static_initialization_and_destruction_0(int, int)':
serial.cpp:(.text+0x3cf): undefined reference to `boost::system::get_system_category()'
serial.cpp:(.text+0x3d9): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3e3): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3ed): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3f7): undefined reference to `boost::system::get_system_category()'
/tmp/ccvTv9iN.o: In function `boost::system::error_code::error_code()':
serial.cpp:(.text._ZN5boost6system10error_codeC1Ev[boost::system::error_code::error_code()]+0x10): undefined reference to `boost::system::get_system_category()'
/tmp/ccvTv9iN.o: In function `boost::asio::error::get_system_category()':
serial.cpp:(.text._ZN5boost4asio5error19get_system_categoryEv[boost::asio::error::get_system_category()]+0x7): undefined reference to `boost::system::get_system_category()'
collect2: ld returned 1 exit status
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

jhodapp
Any reason why you can't use Ubuntu's package for Boost? Do you need the latest version of Boost? If you use the Ubuntu packages, you should have no issues as I was successfully able to run that example on my Ubuntu installation.


On Aug 27, 2011, at 9:55 AM, drwho [via Boost] wrote:

New to boost. (Ubuntu 10.10) Downloaded boost 1.47 source.  Built it with ./bootstrap.sh and ./b2 Keep getting linker failure, undefined reference to `boost::system::get_system_category() in my QT app. Tried your serial example from bash, same thing....

drwho@drwho-desktop:~/Projects/prog$ g++ -Lboost/stage/lib -lboost_system -lboost_thread serial.cpp -o test
/tmp/ccvTv9iN.o: In function `__static_initialization_and_destruction_0(int, int)':
serial.cpp:(.text+0x3cf): undefined reference to `boost::system::get_system_category()'
serial.cpp:(.text+0x3d9): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3e3): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3ed): undefined reference to `boost::system::get_generic_category()'
serial.cpp:(.text+0x3f7): undefined reference to `boost::system::get_system_category()'
/tmp/ccvTv9iN.o: In function `boost::system::error_code::error_code()':
serial.cpp:(.text._ZN5boost6system10error_codeC1Ev[boost::system::error_code::error_code()]+0x10): undefined reference to `boost::system::get_system_category()'
/tmp/ccvTv9iN.o: In function `boost::asio::error::get_system_category()':
serial.cpp:(.text._ZN5boost4asio5error19get_system_categoryEv[boost::asio::error::get_system_category()]+0x7): undefined reference to `boost::system::get_system_category()'
collect2: ld returned 1 exit status



If you reply to this email, your message will be added to the discussion below:
http://boost.2283326.n4.nabble.com/Simple-serial-port-demonstration-with-boost-asio-asynchronous-I-O-tp2582657p3773016.html
To unsubscribe from Simple serial port demonstration with boost asio asynchronous I/O, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

shunguang
This post has NOT been accepted by the mailing list yet.
In reply to this post by Jeff Gray-3
Jeff,

This is a nice demo code, but when i am playing with it i found that the call back function read_complete() is never invoked. Therefore it dose not read anything regardless of what chars inputted. what's the problem at here?

Thx,

-shunguang

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Simple serial port demonstration with boost asio asynchronous I/O

Chunya
This post has NOT been accepted by the mailing list yet.
In reply to this post by Jeff Gray-3
Hi Jeff,

Thanks for your example here, which demonstrates write and read.

I am a beginner with Serial port and want to get more ideas for it.  I would like to know, if it is possible that, the device at one side of Serial Connecting makes and send a request, for instance add(a, b),  the other device at the other side of Serial Connecting receives the request, processing it (calculate it) and later return the result to the first device. We assume that in the second device exist a program with the function add(int a, int b).

Thanks and best regards,
Chunya
Loading...