Boost serial port 100% cpu usage - c++

My problem seems to be related to https://svn.boost.org/trac10/ticket/10496.
Simply opening a boost serial port and waiting for data cause one of the cpu
core of the embedded cpu to have 100% usage.
My hardware is the redpitaya STEMLAB 125-14 running Ubuntu 16.04. Some relevant
code snippets below:
// In the header
namespace ba = boost::asio;
typedef std::shared_ptr < boost::asio::serial_port serial_port_ptr;
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
class SerialPort {
boost::asio::io_service m_io_service;
serial_port_ptr m_port;
work_ptr m_work;
bool open(const std::string &portname);
};
// in the source file,
bool SerialPort::open(const std::string &portname) {
m_port = serial_port_ptr(new ba::serial_port(m_io_service));
m_work = work_ptr(new ba::io_service::work(m_io_service));
m_port->open(portname, ec);
if (ec) {
std::cout << "open failed" << std::endl;
return false;
}
m_io_service.reset();
std::thread t(boost::bind(&ba::io_service::run, &m_io_service));
t.detach();
}
If I comment out the last two lines, I get 0% cpu utilization. The code works
fine, I can read and send from the serial port. The serial port is a usb to
serial device (can this have any effect?).
Has anyone else experience this before and is there a workaround before it is
‘fixed’ by the boost developers or am I doing something wrong?
EDIT: I initially thought maybe the hardware is not ‘powerful enough’, so I installed Ubuntu on VMWare on my i7 laptop. And run only the serial code on it with the USB serial device and I obtained the same result - 100% CPU usage on one core.
I understand it is difficult to help without seeing the full code, so I created a simple code that demonstrate the problem: this can be downloaded from goo.gl/FD5RNE . For simplicity, the code is only looking for USB serial device and will try to open the first device it found.
If you remove the comment from sp.open(), you will see the cpu usage go close to full utilisation. Thanks.
Edit: Still no solution to this but since I am not expecting the serial device to send unsolicited messages to the client, I have changed the program to always close the port i.e. the write/read are executed together with port opened before write and closed after read.
This has worked well so far and I am getting an average CPU of around 0-0.8%. Maybe a future updates to boost will solve the issue.

Related

C++ Boost::asio serial communcation with Arduino can't write

I tried to communicate with my Arduino Uno using the Boost Asio library. Somehow I can't send data to my Arduino, and I have no idea what i'm doing wrong. Reading works fine, but writing only works when i open a terminal and say:
cat /dev/ttyACM0
When this terminal window is open, and I run my C++ application it works otherwise it doesn't work.
Code of the test application (C++):
#include <iostream>
#include <boost/asio.hpp>
char* message;
int main()
{
boost::asio::io_service ioservice;
boost::asio::serial_port serial(ioservice, "/dev/ttyACM0");
serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
serial.set_option(boost::asio::serial_port::flow_control(boost::asio::serial_port::flow_control::none));
serial.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));
serial.set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));
serial.set_option(boost::asio::serial_port::character_size(boost::asio::serial_port::character_size(8)));
std::string s = "u";
boost::asio::streambuf b;
std::ostream os(&b);
os << s;
boost::asio::write(serial, b.data());
if (serial.is_open()) {
serial.close();
}
return 0;
}
Code of my Arduino application:
#include "Servo.h"
Servo servo;
void setup() {
Serial.begin(115200);
servo.attach(9);
servo.write(0);
}
void loop() {
if(Serial.available()) {
char c = Serial.read();
if(c == 'u') {
servo.write(180);
} else if (c == 'v') {
servo.write(0);
}
}
}
I tried this both on my Ubuntu 18.04 and Debian 10 installation to rule out a permission issue, so I think there is something wrong with my code.
Update:
I found the issue, the Arduino is restarting when making a serial connection. When I add a thread sleep for for example 5 seconds and after that resent the data it works (because then it keeps the serial connection alive). I'm still looking for a permanent solution, so that I don't have to do a write before I really want to write something.
Update 2:
Apparently I don't even have to do a write, but where must be a small delay before I can start writing, because when after opening the port the Arduino is still restarting.
I fixed it with adding a small delay before writing to the serial port. As I also wrote in my comment above, the Arduino is restarting when you start a serial communication.
This can be disabled on several ways: https://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection/
Another option is to send a "ready" signal from the Arduino to know in your application that the Arduino is rebooted. So then start in your application with reading, and when you received that message, you can start writing.

How to properly write a Win32 application using boost::asio to communicate over serial connection

I'm developing a Windows application that has to communicate (both input and output) with an Arduino through its serial port. I'm using boost::asio for portability reasons and I want to keep using it. What happens is that the first time I run my application it works perfectly, but if I run it a second time, no data comes from the Arduino anymore and the application stucks on the read operation. The only way to recover is to unplug and replug the Arduino USB cable from the computer.
This behavior is Windows-specific. The same code works perfectly on Linux.
The compiler is Visual Studio 2017 Community Edition.
Here is an example code to reproduce the issue:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <vector>
int main() {
boost::asio::serial_port port(ioctx, "COM3"); // "/dev/ttyACM0" on Linux
port.set_option(boost::asio::serial_port::baud_rate(9600));
port.set_option(boost::asio::serial_port::character_size(8));
port.set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));
port.set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));
port.set_option(boost::asio::serial_port::flow_control(boost::asio::serial_port::flow_control::none));
char c = 'e';
auto const s = boost::asio::write(port, boost::asio::buffer(&c, 1));
std::cout << "sent " << s << " bytes" << std::endl;
boost::asio::streambuf response;
boost::asio::read_until(port, response, "\r\n");
std::istream response_stream(&response);
std::string line;
std::getline(response_stream, line);
std::cout << line << std::endl;
port.close(); // last-ditch effort to get it working
}
Here is an Arduino sketch (got from the Arduino website):
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
Is there a way to restore the correct state of the connection? Am I missing something?
After having learned a couple of things, here is the solution:
Arduino uses its USB communication for both burning the sketch and performing data transmission back and forth the PC. The boot sequence foresees that for 2 seconds (for new Arduino versions and standard boot loader) the communication towards the boot loader is active. After that time, the sketch is executed.
Windows API allows to set all connection parameters at once via the SetCommState function and to retrieve them in a similar fashion with the GetCommState one. That is the method the set_option function uses to set the parameters, but it happens that calling GetCommState-SetCommState multiple times in a row slows down the process a lot (maybe by resetting the Arduino multiple times).
I ended writing the following function:
#include <Windows.h>
#include <chrono>
void init_arduino(boost::asio::serial_port& port, std::chrono::milliseconds const& sleep = 2000)
{
DCB dcbSerialParams = { 0 };
GetCommState(port.native_handle(), &dcbSerialParams);
// this is the optimal way to set the whole serial port configuration
// just in one shot.
dcbSerialParams.BaudRate = CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
//Setting the DTR to Control_Enable ensures that the Arduino is properly
//reset upon establishing a connection
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
SetCommState(port.native_handle(), &dcbSerialParams);
PurgeComm(port.native_handle(), PURGE_RXCLEAR | PURGE_TXCLEAR);
// Wait for Arduino to boot the sketch
Sleep(sleep.count());
}
and using it to replace the port.set_option( lines in the question example.
I also set the flow control to DTR_CONTROL_ENABLE instead of the original none in order to reset the Arduino upon connection.
USB serial adaptors can have device driver bugs and hardware problems. The fact that you have to unplug and plug the device to get it working again is indicative of a device driver bug.
Look for an updated driver. It will probably a Prolific or FTDI chipset, make sure you get the driver from the chip maker. See Prolific or FTDI
If it is a flow control related hardware problem you can wire together the DTR, DSR and CD pins, and wire together the RTS and CTS pins on the RS-232 connector on the USB adaptor. I have seen USB adaptors where this is necessary, despite setting no flow control in software.

Can't read from serial device

I'm trying to write a library to read data from a serial device, a Mipex-02 gas sensor. Unfortunately, my code doesn't seem to open the serial connection properly, and I can't figure out why.
The full source code is available on github, specifically, here's the configuration of the serial connection:
MipexSensor::MipexSensor(string devpath) {
if (!check_dev_path(devpath))
throw "Invalid devpath";
this->path = devpath;
this->debugout_ = false;
this->sensor.SetBaudRate(SerialStreamBuf::BAUD_9600);
this->sensor.SetCharSize(SerialStreamBuf::CHAR_SIZE_8);
this->sensor.SetNumOfStopBits(1);
this->sensor.SetParity(SerialStreamBuf::PARITY_NONE);
this->sensor.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
this->last_concentration = this->last_um = this->last_ur = this->last_status = 0;
cout << "Connecting to "<< devpath << endl;
this->sensor.Open(devpath);
}
I think the meaning of the enums here are obvious enough. The values are from the instruction manual:
UART characteristics:
exchange rate – 9600 baud,
8-bit message,
1 stop bit,
without check for parity
So at first I was using interceptty to test it, and it worked perfectly fine. But when I tried to connect to the device directly, I couldn't read anything. the RX LED flashes on the devices so clearly the program manages to send something, but -unlike with interceptty- the TX LED never flash.
So I don't know if it's sending data incorrectly, if it's not sending all of it, and I can't even sniff the connection since it only happens when interceptty isn't in the middle. Interceptty's command-line is interceptty -s 'ispeed 9600 ospeed 9600 -parenb -cstopb -ixon cs8' -l /dev/ttyUSB0 (-s options are passed to stty) which is in theory the same options set in the code.
Thanks in advance.

Optimization for multicast receiver program

I am writing a program using boost asio to receive multicase messages from around 30 multicase ip in linux with c++. I am here to seek advances on how to minimize packet drop from my client side during runtime. I have already maximized the NIC receive buffer. I am using a 8 core cpu. I am also wondering will the NIC card create same number of buffer queue to equal to number of sockets in the program? Beside configure the NIC card, could I do something on the linux kernel? Since I believe kernel will do buffer copy from the NIC first before our program copy data from it, right?
template<typename msg, int id>
void data_service<msg, id>::on_rt_recv( char* p_raw_packet, int p_length, const boost::system::error_code& error )
{
if (!error)
{
//post to strand and wait to proceed
processing_strand_.post(boost::bind(&data_service::on_rt_recv_handler, this,
p_raw_packet,
p_length));
//continue to listen as soon as possible
auto new_buffer = get_new_buffer();
rt_socket_[p_line]->async_receive_from(boost::asio::buffer(new_buffer, BUFFER_SIZE_), rt_endpoint_,
boost::bind(&data_service::on_rt_recv, this,
new_buffer,
boost::asio::placeholders::bytes_transferred,
boost::asio::placeholders::error));
}
else if (error != boost::asio::error::operation_aborted)
{
memory_pool_.free((void*)p_raw_packet);
}
}
Packet loss issue was caused by hardware, including switch, and NIC cards. the packet rate is actually 2500 * 70 /sec because there were 70 udp sockets. Highly recommend a network monitoring tool call Wireshark which provide load of information regarding to your current network traffic.
Regarding to Demon solution, Boost asio under the hook use iocp in window, and epoll in unix.
No buffer size need to adjust as well.

QextSerialPort connection problem to Arduino

I'm trying to make a serial connection to an Arduino Diecimila board with QextSerialPort. My application hangs though everytime I call port->open(). The reason I think this is happening is because the Arduino board resets itself everytime a serial connection to it is made. There's a way of not making the board reset described here, but I can't figure out how to get QextSerialPort to do that. I can only set the DTR to false after the port has been opened that's not much help since the board has already reset itself by that time.
The code for the connection looks like this:
port = new QextSerialPort("/dev/tty.usbserial-A4001uwj");
port->open(QIODevice::ReadWrite);
port->setBaudRate(BAUD9600);
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);
port->setDtr(false);
port->setRts(false);
Any ideas on how to get this done. I don't necessarily need to use QextSerialPort should someone know of another library that does the trick.
I'm new to C++ and Qt.
UPDATE:
I noticed that if I run a python script that connects to the same port (using pySerial) before running the above code, everything works just fine.
I had a similar problem.
In my case QExtSerial would open the port, I'd see the RX/TX lights on the board flash, but no data would be received. If I opened the port with another terminal program first QExtSerial would work as expected.
What solved it for me was opening the port, configuring the port settings, and then making DTR and RTS high for a short period of time.
This was on Windows 7 w/ an ATMega32u4 (SFE Pro Micro).
bool serialController::openPort(QString portName) {
QString selectPort = QString("\\\\.\\%1").arg(portName);
this->port = new QextSerialPort(selectPort,QextSerialPort::EventDriven);
if (port->open(QIODevice::ReadWrite | QIODevice::Unbuffered) == true) {
port->setBaudRate(BAUD38400);
port->setFlowControl(FLOW_OFF);
port->setParity(PAR_NONE);
port->setDataBits(DATA_8);
port->setStopBits(STOP_1);
port->setTimeout(500);
port->setDtr(true);
port->setRts(true);
Sleep(100);
port->setDtr(false);
port->setRts(false);
connect(port,SIGNAL(readyRead()), this, SLOT(onReadyRead()));
return true;
} else {
// Device failed to open: port->errorString();
}
return false;
}
libserial is an incredible library I use for stand-alone serial applications for my Arduino Duemilanove.
qserialdevice use!
Example:
http://robocraft.ru/blog/544.html
Can you just use a 3wire serial cable (tx/rx/gnd) with no DTR,RTS lines?