Boost ASIO Serial Communication with Arduino: Error above 9600 bps - c++

I'm developing a C++ interface to communicate with na Arduino UNO which is running some code.
To communicate with the Arduino, i'm using boost asio library. My application works well at a baud rate of 9600bps. Now, i wanted to communicate faster with the arduino, so, i tried to communicate at 115200bps, 57600bps, etc, without success.
At 115200bps, it seems that boost::write is sending two non-desirable bytes (both the same, with value ASCII 240 - this only happens for the first data transaction, so if i unplug and plug again the Arduino, this bytes will be sent during the first communication ). At this baud rate i can read the data that is being sent by Arduino (which is wrong for the first data communication, but is correct for the next ones).
At 57600bps, those 2 wrong bytes are not sent, but data is not read from arduino (it seems that write is not sending nothing).
The code to write to the serial is fairly simple, is just the boost::write and the code to read from the serial is just a loop and a boost::read of one byte (communications are synchronous just to test if everything was okay, which is not for higher baud rates than 9600bps).
The write function:
void sendMessage(char *c, unsigned int size) {
serial.write(c, size);
return;
}
The read function:
void readMessage(void) {
char c;
uint8_t count = 0;
for (;;)
{
boost::asio::read(serial, boost::asio::buffer(&c, 1));
cout << "Received char: " << static_cast<unsigned int>(c) << endl;
if (count == 3 ){
return;
}
count++;
}
return;
}
I know that the problem is not in the side of the arduino (that's why i posted the question here and not in the arduino stackexchange) because, using realterm and sending the exact same bytes that i send using boost, i get the proper reply from the Arduino for every baud rate (9600, 57600 and 115200bps).
If anyone can help, i would be appreciated, since at this moment i don't know which is the problem (and i'm a beginner to boost).
Best regards
Edit
At 74880 bps, I recieve four times the byte with value 252.

Related

Qt Serial Communication not sending all data

I am writing a Qt application for serial communication with a Qorvo MDEK-1001. All built-in serial commands I've had to use work fine except for one: aurs n k, where n and k are integers corresponding to the desired rate of data transmission (e.g. "aurs 1 1\r"). Write function is:
void MainWindow::serialWrite(const QByteArray &command)
{
if(mdek->isOpen())
{
mdek->write(command);
qDebug() << "Command: " << command;
//mdek->flush();
}
}
If I send the command "aurs 1 1\r". It doesn't actually get sent to the device until I send another command for some reason. So if I subsequently send the "quit" command to the device, the returned data from the device is: "aurs 1quit", which registers as an unknown command. Any assistance getting this command to send properly is appreciated.
I've tried a bunch of stuff (setting bytes to write as second parameter in write(), using QDataStream, appending individual hex bytes onto QByteArray and writing that), but nothing has worked. This is the first time I've had to use Qt's serial communication software so I've probably missed something obvious.
On Linux Manjaro (same thing happens on Windows 8.1)
Connection settings: 8 data bits, Baud: 115200, No Flow Control, No Parity, One Stop Bit

Switching the GPIO of a Raspberry Pi at a fixed frequency

I have a 208 - 232 Bit long binary signal that I want to send via a GPIO of the raspberry pi.
The delay between the bits needs to be constant.
How can I achieve this ?
The simplest solution that came to my mind was this (pseudocode):
send(gpio, message, delay){
for(int i = 0; i < lenght(message); i++){
if (message[i] == 1){
gpio.high()
}
else{
gpio.low()
}
sleep(delay)
}
}
But the frequency at which I want to send this message is around 40kHz so the delay between two bits is only 25us.
How can I assure it is exactly&constantly that much delay.
In userspace there is no way to garantee that anything is happening in "real time".
Because of this I decided using a seperate Microcontroller that is responsible for time critical features that communicates via I2C or UART with the RaspberryPi.
This way the RaspberryPi can do high abstraction level decisions and show animation to a user while being able to send messages over ultrasound.
Another way I came up with is to create a file similar to .wav and then use DMA or other techniques like with audio. But as I haven't tried it I don't know if the Pi is sufficient to do this at higher Samplingrates.

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.

Strange Serial MisComunication

Ok, so I have 3 devices.
an AVR Butterfly microcontroller, set up with USART
A Bifferboard, running Debian, using a custom made program for serial.
A Desktop machine running Br#y's.
So I'm trying to make the Bifferboard send serial to the AVR, But the AVR never receives the signal, (we've checked the wires). But if i connect the AVR to the desktop box, and send with Br#y's it receives just fine.
If I connect the Bifferboard to the Desktop, Br#y's receives just fine.
Heres the code for the Bifferboard.
#include "string2num.h" //a custom header
#include <cstdlib>
#include <iostream>
#include <SerialStream.h>
using namespace LibSerial;
//using namespace std;
int main(int argc, char*argv[])
{
if (argc<2)
{
std::cout<<argv[0]<<" requires the device name eg \'dev/tty0\' as a parameter\nterminating.\n";
return 1;
}
SerialStream theSerialStream(argv[1]); //open the device
if(!theSerialStream.IsOpen()) //did the device succesfuilly open
{ //open faile
std::cerr<<"Open " << argv[1] << " failed\n Terminating.\n";
return 1; //exit failure
}
theSerialStream.SetVMin(0);//no min number of characters to send
theSerialStream.SetVTime(0);// don't wait betwenn characters
theSerialStream.SetBaudRate( SerialStreamBuf::BAUD_19200);
theSerialStream.SetCharSize(SerialStreamBuf::CHAR_SIZE_8); //8
theSerialStream.SetParity(SerialStreamBuf::PARITY_NONE);// N
theSerialStream.SetNumOfStopBits(1);// 1
theSerialStream.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
std::cout<<"Ready for serial trasmission. Press Ctrl+C to quit\n";
//insert basic instructions here
while (1)
{
char input[BUFSIZ];
std::cin>>input;
char* values=getAllValues(input); //DECODE any formatting (this function is in the custom header)
std::cout<<"about to transmit: " << values << "\n";
theSerialStream << values;
free(values);
}
theSerialStream.Close();
return 0;
}
I've also tried using minicom from Bifferboard - it can talk to the desktop windows machine, but not the the AVR.
(We've checked the wires)
This still sounds like a cabling problem. If Br#y's can communicate with both, then it doesn't seem to be a configuration issue. You should throw a logic analyzer or oscilloscope on the receive pin (and probably probe other pins) of the AVR and see what's happening electrically when yo try to send data from the Bifferboard.
I'd bet that you see the data on some other pin. But I wouldn't bet a whole lot, because serial RS232 connectivity is such a touchy thing.
It's a long shot, but are all the serial ports running at the same voltage levels? I see the bifferboard has a 3.3V UART, whereas the AVR has a level convertor. The desktop port may be more flexible about voltage.
I'll throw out another "long shot" possibility. Depending on the transciever chip being used on a board it may not be able to produce its own negative supply voltage (typically via a charge pump) for RS232 levels. Some chips will "steal" their negative supply from the other side of the line, and that works great if you're talking to something like a PC. If both sides take that approach, however, it doesn't work out so great. Tranceivers like MAX232 (plus external caps and resistors) will generate their own negative supply, but chips like the DS275 don't.