I have created a wrapper library around QSerialPort. I want to communicate with my device. First, I send list command to my device and it should return list of commands supported by that device. However, while debugging my code, i observed that list command is being send to the device and device returns the proper response to it (i debugged using serial traffic sniffer Linux tools). However, i am not getting any response from my device using QSerialPort (while serial traffic sniffer tool was disable). I am unable to get it work after testing it several times.
My Serial.h:
class Serial : public Print {
public:
Serial();
explicit Serial(const char *dev_path);
~Serial();
int begin(unsigned long baudrate);
int begin(unsigned long baudrate, uint8_t cfg);
void end(void);
int available(void) const;
bool availableForWrite(void) const;
void flush(void);
bool isError(void) const;
void reset(void);
unsigned long write(uint8_t c);
unsigned long write(uint8_t *p_data, unsigned long maxSize);
int read(void);
void close();
QSerialPort &getPort()
{
return *_p_port;
}
public slots:
void readyBe(void);
private:
QSerialPort *_p_port;
unsigned long _baudrate;
};
My Serial.cpp:
Serial::Serial()
{
_p_port = new QSerialPort();
if (_p_port == nullptr)
throw std::runtime_error("Can't allocate memory");
}
Serial::Serial(const char *dev_path)
{
_p_port = new QSerialPort(QString(dev_path), QApplication::instance());
if (_p_port == nullptr)
throw std::runtime_error("Can't allocate memory");
// _p_port->setPortName(QString(dev_path));
if (_p_port->open(QIODevice::ReadWrite) == false) {
throw std::runtime_error("Can't open the serial _p_port");
delete _p_port;
}
_p_port->setBaudRate(QSerialPort::Baud9600);
_p_port->setDataBits(QSerialPort::Data8);
_p_port->setParity(QSerialPort::NoParity);
_p_port->setStopBits(QSerialPort::OneStop);
_p_port->setFlowControl(QSerialPort::NoFlowControl);
}
Serial::~Serial()
{
if (_p_port != nullptr) {
end();
delete _p_port;
}
}
int Serial::begin(unsigned long baudrate)
{
if (_p_port->setBaudRate(baudrate, QSerialPort::AllDirections) == false)
return -1;
_baudrate = baudrate;
return 0;
}
void Serial::end()
{
if (_p_port->isOpen())
_p_port->close();
}
int Serial::available(void) const
{
int num_bytes = _p_port->bytesAvailable();
return num_bytes;
}
bool Serial::availableForWrite(void) const
{
if (_p_port->isWritable())
return true;
return false;
}
void Serial::flush()
{
_p_port->flush();
}
unsigned long Serial::write(uint8_t c)
{
if (_p_port->putChar(c))
return 1;
return 0;
}
unsigned long Serial::write(uint8_t *p_data, unsigned long maxSize)
{
return _p_port->write(reinterpret_cast<const char *>(p_data), (qint64)maxSize);
}
int Serial::read(void)
{
char c;
_p_port->getChar(&c);
return c;
}
void Serial::reset(void)
{
_p_port->clear(QSerialPort::AllDirections);
_p_port->clearError();
}
bool Serial::isError(void) const
{
if (_p_port->error() == QSerialPort::NoError)
return false;
return true;
}
And my main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
// w.show();
Serial serial("ttyACM0");
if (serial.begin(115200))
std::cout << "Failed to set Baud rate" << std::endl;
std::cout << "Sending data" << std::endl;
// QObject::connect(&(serial.getPort()), SIGNAL(readyRead()), &serial, SLOT(readyBe()));
serial.print("list\r");
serial.flush();
while (true) {
while (true) {
while (serial.available() == 0) {
if (serial.isError()) {
std::cout << "Error" << std::endl;
// serial.reset();
}
}
char c = serial.read();
std::cout << c;
if (c == '\n')
break;
}
std::cout << std::endl;
}
return a.exec();
}
You've pretty much missed everything needed for this code to work: the event loop. I/O in real life is asynchronous. You can't just "read" from the port without having some means of getting informed when the data is available, and actually letting the asynchronous I/O requests get processed. Yes, there are some legacy APIs that let you do that, but they mostly lead to spaghetti code, wasted threads, and poor performance.
The while (serial.available() == 0) loop is a no-op. It does nothing to actually let the available() return any other value. All that available() does internally is read an integer member of a class. You never run any code that could update the value stored in that member. Even if you would convert this to serial.waitForReadyRead(), which does update the number of available bytes, you're still not spinning an event loop, and thus you won't be able to process timeouts, or react to any other events an application might need to react to. QIODevice::waitForReadyRead is only meant to do one thing: return when a readyRead signal would fire. It won't process any other events, and it's a crutch used to port blocking code and is not really meant for production use.
You should redesign your code to be asynchronous and driven by signals from QSerialPort. The work will then be done from within QCoreApplication::exec - you won't have a loop of your own. This inversion of control is critical for getting async I/O working.
See e.g. this answer for a very simple example of an async approach, and this answer for a more complete one.
Related
I've written a engine for the game "draughts" some time ago and now I want to write a program that communicates with two of those engines via some protocol. In the end I hope to have something similar to the UCI-protocol which is widely known among programmers of chess engines.
The engine is supposed to receive all the commands via stdin and sends it's response via stdout.
I've created some dummy engine to test this with some testing before the if-statement to see if the engine receives anything at all.
int main(){
std::cerr<<"MainEngine"<<std::endl;
while (!std::cin.eof()) {
std::string current;
std::getline(std::cin, current);
std::cerr<<"FromMain:"<<current<<std::endl;
if (current == "init") {
initialize();
std::cout << "ready" << "\n";
} else if (current == "hashSize") {
std::string hash;
std::getline(std::cin, hash);
setHashSize(1u << std::stoi(hash));
} else if (current == "position") {
std::string position;
std::getline(std::cin, position);
} else if (current == "move") {
std::string move;
std::getline(std::cin, move);
}
}
return 0
}
and here is my attempt at the communication-part using pipes
struct Interface {
enum State {
Idle, Ready, Searching
};
const int &engineRead1;
const int &engineRead2;
const int &engineWrite1;
const int &engineWrite2;
State oneState;
State twoState;
void initEngines();
void writeMessage(const int &pipe, const std::string &message);
void processInput(const int &readPipe);
Interface &operator<<(const std::string message);
};
void Interface::processInput(const int &readPipe) {
std::string message;
char c;
while ((read(readPipe, &c, sizeof(char))) != -1) {
if (c == '\n') {
break;
} else {
message += c;
}
}
if (message == "ready") {
std::cout << "ReadyEngine" << std::endl;
}
}
void Interface::writeMessage(const int &pipe, const std::string &message) {
write(pipe, (char *) &message.front(), sizeof(char) * message.size());
}
int main(int argl, const char **argc) {
int numEngines = 2;
int mainPipe[numEngines][2];
int enginePipe[numEngines][2];
for (auto i = 0; i < numEngines; ++i) {
pipe(mainPipe[i]);
pipe(enginePipe[i]);
auto pid = fork();
if (pid < 0) {
std::cerr << "Error" << std::endl;
exit(EXIT_FAILURE);
} else if (pid == 0) {
dup2(mainPipe[i][0], STDIN_FILENO);
close(enginePipe[i][1]);
dup2(enginePipe[i][1], STDOUT_FILENO);
close(mainPipe[i][0]);
execlp("./engine", "engine", NULL);
}
close(enginePipe[i][0]);
close(mainPipe[i][1]);
std::string message = "init\n";
Interface inter{enginePipe[0][0], enginePipe[1][0], mainPipe[0][1], mainPipe[1][1]};
inter.writeMessage(inter.engineWrite1, message);
inter.writeMessage(inter.engineWrite2, message);
int status;
for (int k = 0; k < numEngines; ++k) {
wait(&status);
}
}
}
I am creating two child-process one for each engine. In this test I simply send "init\n" to each of the engine and would expect the child processes to print "FromMain: init". However, I am only getting the output "MainEngine" from one of the child-processes.
This is my first attempt at using pipes and I dont know where I messed up. I would appreciate some tips/help on how to properly setup the communication part.
close(enginePipe[i][1]);
dup2(enginePipe[i][1], STDOUT_FILENO);
You're closing a pipe and then trying to dup it. This doesn't work.
close(enginePipe[i][0]);
close(mainPipe[i][1]);
std::string message = "init\n";
Interface inter{enginePipe[0][0], enginePipe[1][0], mainPipe[0][1], mainPipe[1][1]};
And you're closing these pipes then trying to use them too. And making inter with all of the pipes each iteration through, instead of each only once.
I'd advise you to do something simple with two processes and one pipe, before trying complicated things like this with three processes and four pipes.
I'm trying do some serial communication between my pc and an arduino ATmega2560
First the microntroller's program :
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.write('A');
}
The arduino program is very basic, his aim is to check the next program which is on the pc.
The main.cpp :
#include <iostream>
#include "SerialPort.h"
using namespace std;
int main()
{
SerialPort port("com3", 9600);
while (1)
{
//Receive
unsigned char dataR;
port.receive(dataR, 1);
cout << dataR << endl;
}
return 0;
}
The SerialPort.h:
#include <windows.h>
#include <iostream>
class SerialPort
{
public:
//Constructors
SerialPort();
SerialPort(const char* port, unsigned long BaudRate);
//Initialization
void Initialize(const char* port, unsigned long BaudRate);
//Serial I/O
void receive(unsigned char &data, unsigned int byteSize);
void transmit(unsigned char *data, unsigned int byteSize);
//State
void connect();
void disconnect();
bool isConnected();
//Destructor
~SerialPort();
private:
HANDLE handler;
bool isConnect;
};
And the SerialPort.cpp :
#include "SerialPort.h"
/*Constructors*/
SerialPort::SerialPort()
: isConnect(false) {}
SerialPort::SerialPort(const char* port, unsigned long BaudRate)
: isConnect(false)
{
Initialize(port, BaudRate);
}
/*Initialization*/
void SerialPort::Initialize(const char* port, unsigned long BaudRate)
{
handler = CreateFile(port, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handler == INVALID_HANDLE_VALUE)
{
std::cout << "ERROR!::Error during opening port" << port << std::endl;
return;
}
DCB serialParameters;
if (!GetCommState(handler, &serialParameters)) /*Get com parameters*/
{
std::cout << "ERROR!::failed to get current serial parameters" << std::endl;
return;
}
serialParameters.DCBlength = sizeof(DCB);
serialParameters.BaudRate = BaudRate;
serialParameters.ByteSize = 1; /*8 bit data format*/
serialParameters.StopBits = TWOSTOPBITS;
serialParameters.Parity = PARITY_NONE;
if (!SetCommState(handler, &serialParameters)) /*Send modified com parameters*/
{
std::cout << "ALERT!:Failed to set THE Serial port parameters" << std::endl;
return;
}
isConnect = true;
PurgeComm(handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
}
/*Serial I/O*/
void SerialPort::receive(unsigned char &data, unsigned int byteSize)
{
ReadFile(handler, &data, byteSize, NULL, NULL);
}
void SerialPort::transmit(unsigned char *data, unsigned int byteSize)
{
WriteFile(handler, data, byteSize, NULL, NULL);
}
/*State*/
void SerialPort::connect()
{
isConnect = true;
}
void SerialPort::disconnect()
{
isConnect = false;
}
bool SerialPort::isConnected()
{
return isConnect;
}
/*Destructors*/
SerialPort::~SerialPort()
{
if (isConnect)
{
isConnect = false;
CloseHandle(handler);
}
}
I've an issue with this program : I don't receive the right data. Where I should get on the terminal
A
A
A
...
I get weird characters made of ? in a square
I hope you understood my problem
Thanks
The DCB ByteSize parameter is in bits. You have specified a UART frame with one data bit - which is not supported by the hardware at either end.
For a conventional N,8,1 data frame, use
serialParameters.ByteSize = 8 ;
serialParameters.StopBits = ONESTOPBIT ;
serialParameters.Parity = NOPARITY ;
ByteSize is perhaps a misleading name. It defines the number of bits between the start and stop bit in an UART frame. Most commonly this is 8, but for pure ASCII data transfer 7 might be used - historically at least.
The Atmel AVR UART supports frames with 5 to 9 data bits. The PC's UART may be virtual, but will typically be compatible with the 16550 UART, which supported 5 to 8 bit data frames, however these days you are more likely to be using USB-Serial adapter, and the UART on the USB/Serial bridge may not support all 16550 modes - the common FTDI232R for example only supports 7 or 8 bit frames, while Prolific PL2303 supportts 5 to 8. It probably pays to avoid unconventional frames and stick to N,8,1 if you want to be sure it will work on a range of hardware.
From the Arduino documentation on Serial.begin() (emphasis added):
An optional second argument configures the data, parity, and stop
bits. The default is 8 data bits, no parity, one stop bit.
I see this in your code (with no second parameter):
Serial.begin(9600);
and this
serialParameters.StopBits = TWOSTOPBITS;
I think that may be your problem.
In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()),
this, SLOT(processMessage()));
Where lcmhandler.h is:
class LCMHandler : public QObject
{
Q_OBJECT
public:
static LCMHandler* getInstance();
LCMHandler();
~LCMHandler() {}
void handleMessage(const lcm::ReceiveBuffer* rbuf,
const std::string &chan,
const example::example_t *msg);
signals:
void messageReceived();
private:
static LCMReceiver* _instance;
};
And lcmhandler.cpp is:
LCMHandler* LCMHandler::_instance = 0;
LCMHandler::LCMHandler()
{
lcm::LCM lcm;
if(lcm.good())
{
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
while(0 == lcm.handle());
} else {
std::cerr << "LCM Error" << std::endl;
}
}
LCMHandler* LCMHandler::getInstance() {
if (!_instance) {
_instance = new LCMHandler();
}
return _instance;
}
void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf,
const std::string &chan,
const hlelcm::transponder_t *msg)
{
std::cout << "Received message on channel " << chan.c_str() << std::endl;
emit messageReceived();
}
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's processMessage(), presumably because the application gets stuck looping on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() is:
void Consumer::processMessage() {
std::cout << "Message received" << std::endl;
}
It never executes, while handleMessage(...) loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.
What is the best way to handle the incoming messages? Should I refrain from using a singleton for LCMHandler? What do I need to change to make this implementation work?
Move the contents of your LCM constructor to another function:
LCMHandler::beginCommunication()
{
lcm::LCM lcm;
if(lcm.good())
{
//QObject base class call.
moveToThread( &_myLocalQThread );
_myLocalThread.start();
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
_isActive = true;
// This is blocking, we don't want it to interfere with
// the QApplication loop
while(0 == lcm.handle());
}
else
{
std::cerr << "LCM Error" << std::endl;
}
_isActive = false;
}
Then something along these lines to allow your LCM loop to happen in another thread.
auto lcmHandler = LCMHandler::getInstance();
// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
this, &Consumer::processMessage, Qt::QueuedConnection );
// Add a std::atomic< bool > _isActive to LCMHandler
if( not lcmHandler.isActive() )
{
lcmHandler.beginCommunication();
}
And then make sure to properly close your QThread in the destructor.
LCMHandler::~LCMHandler()
{
_myLocalQThread.quit();
_myLocalQThread.wait();
}
I have a mini2440 board with Linux 2.6 on which I have to program to control a mounted solar panel. The algorithm is provided and I need to code it for the ARM board. The GUI is done in Qt and I need a to write the code for the actual control. I saw the method for GPIO access from user space and it is tedious. I question the accuracy I can get for PWM.
What other methods can I use to program the GPIOs for on/off and PWM applications?
A few friends suggested programming the control code as a kernel module but I am not quite sure I wish to jump into that.
A few friends suggested programming the control code as a kernel module but I am not quite sure I wish to jump into that.
I am almost certain if it is not closely hardware related it would be rejected in the Linux kernel. The purpose of the userspace syscall and sysfs access to put your custom logic into a Linux on top of the hardware abstraction (OSI model).
What you would need to do is first check if the Linux kernel provides all the hardware support for your devices. Then you could take my middleware class for controling the GPIO in a C++ manner. Finally, you could write a small main application to test the kernel and C++ class. That application would do just something simple like instantiating the GPIO class, export a GPIO, and then write values.
(How this is related to your PWM question is unclear, but you seem to mix up two different kernel driver areas)
You could do something like the code below through sysfs as per the Linux kernel gpio documentation. You will of course need to make sure that your hardware gpio is supported by the Linux kernel.
gpio.h
#ifndef FOOBAR_GENERALPURPOSEIO_H
#define FOOBAR_GENERALPURPOSEIO_H
namespace Foobar
{
class FOOBAR_EXPORT GeneralPurposeIO
{
public:
enum Direction {
Input,
Output
};
explicit GeneralPurposeIO(quint32 gpioNumber = 0);
~GeneralPurposeIO();
int gpioExport();
int gpioUnexport();
bool isGpioExported();
quint32 gpioNumber() const;
void setGpioNumber(quint32 gpioNumber);
Direction direction() const;
int setDirection(Direction direction);
qint32 value() const;
int setValue(qint32 value);
private:
class Private;
Private *const d;
};
}
#endif // FOOBAR_GENERALPURPOSEIO_H
gpio.cpp
#include "generalpurposeio.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
using namespace Foobar;
class GeneralPurposeIO::Private
{
public:
Private()
{
}
~Private()
{
}
static const QString gpioExportFilePath;
static const QString gpioUnexportFilePath;
static const QString gpioDirectionFilePath;
static const QString gpioValueFilePath;
static const QString gpioFilePath;
quint32 gpioNumber;
};
const QString GeneralPurposeIO::Private::gpioExportFilePath = "/sys/class/gpio/export";
const QString GeneralPurposeIO::Private::gpioUnexportFilePath = "/sys/class/gpio/unexport";
const QString GeneralPurposeIO::Private::gpioDirectionFilePath = "/sys/class/gpio/gpio%1/direction";
const QString GeneralPurposeIO::Private::gpioValueFilePath = "/sys/class/gpio/gpio%1/value";
const QString GeneralPurposeIO::Private::gpioFilePath = "/sys/class/gpio/gpio%1";
GeneralPurposeIO::GeneralPurposeIO(quint32 gpioNumber)
: d(new Private)
{
d->gpioNumber = gpioNumber;
}
GeneralPurposeIO::~GeneralPurposeIO()
{
}
/*
* Exports the desired gpio number.
*
* Note: Unfortunately, it is not possible to just call this method "export"
* since that is a reserved keyword in C++. Systematically the unexport method
* cannot be called "unexport" either for consistency.
*/
int GeneralPurposeIO::gpioExport()
{
if (isGpioExported()) {
// TODO: Proper error mutator mechanism for storing different error
// enumeration values internally that can be requested by the API user
qDebug() << "Cannot export the gpio pin since it is already exported:" << d->gpioNumber;
return -1;
}
QFile gpioExportFile(d->gpioExportFilePath);
if (!gpioExportFile.open(QIODevice::Append)) {
qDebug() << "Cannot open the gpio export file:" << d->gpioExportFilePath;
return -1;
}
/*
* Seek to begining of the file
*/
gpioExportFile.seek(0);
/*
* Write our value of "gpioPinNumber" to the file
*/
if (gpioExportFile.write(QByteArray::number(d->gpioNumber)) == -1) {
qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioExportFilePath;
gpioExportFile.close();
return -1;
}
gpioExportFile.close();
return 0;
}
int GeneralPurposeIO::gpioUnexport()
{
if (!isGpioExported()) {
// TODO: Proper error mutator mechanism for storing different error
// enumeration values internally that can be requested by the API user
qDebug() << "Cannot unexport the gpio pin since it is not exported yet:" << d->gpioNumber;
return -1;
}
QFile gpioUnexportFile(d->gpioUnexportFilePath);
if (!gpioUnexportFile.open(QIODevice::Append)) {
qDebug() << "Cannot open the gpio export file:" << d->gpioUnexportFilePath;
return -1;
}
/*
* Seek to begining of the file
*/
gpioUnexportFile.seek(0);
/*
* Write our value of "gpioPinNumber" to the file
*/
if (gpioUnexportFile.write(QByteArray::number(d->gpioNumber)) == -1) {
qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioUnexportFilePath;
gpioUnexportFile.close();
return -1;
}
gpioUnexportFile.close();
return 0;
}
bool GeneralPurposeIO::isGpioExported()
{
if (!QFile(d->gpioFilePath.arg(d->gpioNumber)).exists()) {
return false;
}
return true;
}
quint32 GeneralPurposeIO::gpioNumber() const
{
return d->gpioNumber;
}
void GeneralPurposeIO::setGpioNumber(quint32 gpioNumber)
{
d->gpioNumber = gpioNumber;
}
GeneralPurposeIO::Direction GeneralPurposeIO::direction() const
{
// TODO: Implement me
return GeneralPurposeIO::Output;
}
int GeneralPurposeIO::setDirection(Direction direction)
{
if (!isGpioExported()) {
if (gpioExport() == -1) {
return -1;
}
}
/*
* Set the direction
*/
QFile gpioDirectionFile(d->gpioDirectionFilePath.arg(d->gpioNumber));
if (!gpioDirectionFile.open(QIODevice::ReadWrite)) {
qDebug() << "Cannot open the relevant gpio direction file:" << d->gpioDirectionFilePath;
return -1;
}
int retval = 0;
/*
* Seek to begining of the file
*/
gpioDirectionFile.seek(0);
switch (direction) {
case Output:
if (gpioDirectionFile.write("high") == -1) {
qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioDirectionFilePath;
retval = -1;
}
break;
case Input:
if (gpioDirectionFile.write("low") == -1) {
qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioDirectionFilePath;
retval = -1;
}
break;
default:
break;
}
gpioDirectionFile.close();
return retval;
}
qint32 GeneralPurposeIO::value() const
{
// TODO: Implement me
return 0;
}
int GeneralPurposeIO::setValue(qint32 value)
{
if (direction() != GeneralPurposeIO::Output) {
qDebug() << "Cannot set the value for an input gpio pin:" << d->gpioNumber;
return -1;
}
/*
* Set the value
*/
QFile gpioValueFile(d->gpioValueFilePath.arg(d->gpioNumber));
if (!gpioValueFile.open(QIODevice::ReadWrite)) {
qDebug() << "Cannot open the relevant gpio value file:" << d->gpioValueFilePath.arg(d->gpioNumber);
gpioValueFile.close();
return -1;
}
/*
* Seek to begining of the file
*/
gpioValueFile.seek(0);
if (gpioValueFile.write(QByteArray::number(value)) == -1) {
qDebug() << Q_FUNC_INFO << "Error while writing the file:" << d->gpioValueFilePath.arg(d->gpioNumber);
gpioValueFile.close();
return -1;
}
gpioValueFile.close();
return 0;
}
I am using boost library to develop a asynchronous udp communication. A data received at the receiver side is being precessed by another thread. Then my problem is when I read the received data in another thread rather than the receiver thread it self it gives a modified data or updated data which is not the data that is supposed to be.
My code is working on unsigned character buffer array at sender side and receiver side. The reason is I need consider unsigned character buffer as a packet of data
e.g buffer[2] = Engine_start_ID
/* global buffer to store the incomming data
unsigned char received_buffer[200];
/*
global buffer accessed by another thread
which contains copy the received_buffer
*/
unsigned char read_hmi_buffer[200];
boost::mutex hmi_buffer_copy_mutex;
void udpComm::start_async_receive() {
udp_socket.async_receive_from(
boost::asio::buffer(received_buffer, max_length), remote_endpoint,
boost::bind(&udpComm::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
/* the data received is stored in the unsigned char received_buffer data buffer*/
void udpComm::handle_receive_from(const boost::system::error_code& error,
size_t bytes_recvd) {
if (!error && bytes_recvd > 0) {
received_bytes = bytes_recvd;
hmi_buffer_copy_mutex.lock();
memcpy(&read_hmi_buffer[0], &received_buffer[0], received_bytes);
hmi_buffer_copy_mutex.unlock();
/*data received here is correct 'cus i printed in the console
checked it
*/
cout<<(int)read_hmi_buffer[2]<<endl;
}
start_async_receive();
}
/* io_service is running in a thread
*/
void udpComm::run_io_service() {
udp_io_service.run();
usleep(1000000);
}
The above code is the asynchronous udp communication running a thread
/* My second thread function is */
void thread_write_to_datalink()
{ hmi_buffer_copy_mutex.lock();
/* here is my problem begins*/
cout<<(int)read_hmi_buffer[2]<<endl;
hmi_buffer_copy_mutex.unlock();
/* all data are already changed */
serial.write_to_serial(read_hmi_buffer, 6);
}
/* threads from my main function
are as below */
int main() {
receive_from_hmi.start_async_receive();
boost::thread thread_receive_from_hmi(&udpComm::run_io_service,
&receive_from_hmi);
boost::thread thread_serial(&thread_write_to_datalink);
thread_serial.join();
thread_receive_from_hmi.join();
return 0;
}
/* The Serial_manager class contains functions for writting and reading from serial port*/
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
class Serial_manager {
public:
Serial_manager(boost::asio::io_service &serial_io_service,char *dev_name);
void open_serial_port();
void write_to_serial(void *data, int size);
size_t read_from_serial(void *data, int size);
void handle_serial_exception(std::exception &ex);
virtual ~Serial_manager();
void setDeviceName(char* deviceName);
protected:
io_service &port_io_service;
serial_port datalink_serial_port;
bool serial_port_open;
char *device_name;
};
void Serial_manager::setDeviceName(char* deviceName) {
device_name = deviceName;
}
Serial_manager::Serial_manager(boost::asio::io_service &serial_io_service,char *dev_name):
port_io_service(serial_io_service),
datalink_serial_port(serial_io_service) {
device_name = dev_name;
serial_port_open = false;
open_serial_port();
}
void Serial_manager::open_serial_port() {
bool temp_port_status = false;
bool serial_port_msg_printed = false;
do {
try {
datalink_serial_port.open(device_name);
temp_port_status = true;
} catch (std::exception &ex) {
if (!serial_port_msg_printed) {
std::cout << "Exception-check the serial port device "
<< ex.what() << std::endl;
serial_port_msg_printed = true;
}
datalink_serial_port.close();
temp_port_status = false;
}
} while (!temp_port_status);
serial_port_open = temp_port_status;
std::cout <<std::endl <<"serial port device opened successfully"<<std::endl;
datalink_serial_port.set_option(serial_port_base::baud_rate(115200));
datalink_serial_port.set_option(
serial_port_base::flow_control(
serial_port_base::flow_control::none));
datalink_serial_port.set_option(
serial_port_base::parity(serial_port_base::parity::none));
datalink_serial_port.set_option(
serial_port_base::stop_bits(serial_port_base::stop_bits::one));
datalink_serial_port.set_option(serial_port_base::character_size(8));
}
void Serial_manager::write_to_serial(void *data, int size) {
boost::asio::write(datalink_serial_port, boost::asio::buffer(data, size));
}
size_t Serial_manager::read_from_serial(void *data, int size) {
return boost::asio::read(datalink_serial_port, boost::asio::buffer(data, size));
}
void Serial_manager::handle_serial_exception(std::exception& ex) {
std::cout << "Exception-- " << ex.what() << std::endl;
std::cout << "Cannot access data-link, check the serial connection"
<< std::endl;
datalink_serial_port.close();
open_serial_port();
}
Serial_manager::~Serial_manager() {
// TODO Auto-generated destructor stub
}
I think my area of problem is about thread synchronization and notification and I will be happy if you help me. You should not worry about the sender it is works perfectly as I already checked it the data is received at the receiver thread. I hope you understand my question.
Edit: Here is the modification.My whole idea here is to develop a simulation for the Manual flight control so according my design i have client application that sends commands through
udp communication. At the receiver side intended to use 3 threads. one thread receives input from sticks i.e void start_hotas() the second thread is a thread that receives commands from sender(client): void udpComm::run_io_service() and 3rd is the void thread_write_to_datalink().
/* a thread that listens for input from sticks*/
void start_hotas() {
Hotas_manager hotasobj;
__s16 event_value; /* value */
__u8 event_number; /* axis/button number */
while (1) {
hotasobj.readData_from_hotas();
event_number = hotasobj.getJoystickEvent().number;
event_value = hotasobj.getJoystickEvent().value;
if (hotasobj.isAxisPressed()) {
if (event_number == 0) {
aileron = (float) event_value / 32767;
} else if (event_number == 1) {
elevator = -(float) event_value / 32767;
} else if (event_number == 2) {
rudder = (float) event_value / 32767;
} else if (event_number == 3) {
brake_left = (float) (32767 - event_value) / 65534;
} else if (event_number == 4) {
} else if (event_number == 6) {
} else if (event_number == 10) {
} else if (event_number == 11) {
} else if (event_number == 12) {
}
} else if (hotasobj.isButtonPressed()) {
}
usleep(1000);
}
}
/*
* Hotas.h
*
* Created on: Jan 31, 2013
* Author: metec
*/
#define JOY_DEV "/dev/input/js0"
#include <iostream>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <linux/joystick.h>
bool message_printed = false;
bool message2_printed = false;
class Hotas_manager {
public:
Hotas_manager();
virtual ~Hotas_manager();
void open_hotas_device();
/*
*
* read from hotas input
* used to the updated event data and status of the joystick from the
* the file.
*
*/
void readData_from_hotas();
js_event getJoystickEvent() {
return joystick_event;
}
int getNumOfAxis() {
return num_of_axis;
}
int getNumOfButtons() {
return num_of_buttons;
}
bool isAxisPressed() {
return axis_pressed;
}
bool isButtonPressed() {
return button_pressed;
}
int* getAxis() {
return axis;
}
char* getButton() {
return button;
}
private:
int fd;
js_event joystick_event;
bool hotas_connected;
int num_of_axis;
int num_of_buttons;
int version;
char devName[80];
/*
* the the variables below indicates
* the state of the joystick.
*/
int axis[30];
char button[30];
bool button_pressed;
bool axis_pressed;
};
Hotas_manager::Hotas_manager() {
// TODO Auto-generated constructor stub
hotas_connected = false;
open_hotas_device();
std::cout << "joystick device detected" << std::endl;
}
Hotas_manager::~Hotas_manager() {
// TODO Auto-generated destructor stub
}
void Hotas_manager::open_hotas_device() {
bool file_open_error_printed = false;
while (!hotas_connected) {
if ((fd = open(JOY_DEV, O_RDONLY)) > 0) {
ioctl(fd, JSIOCGAXES, num_of_axis);
ioctl(fd, JSIOCGBUTTONS, num_of_buttons);
ioctl(fd, JSIOCGVERSION, version);
ioctl(fd, JSIOCGNAME(80), devName);
/*
* NON BLOCKING MODE
*/
ioctl(fd, F_SETFL, O_NONBLOCK);
hotas_connected = true;
} else {
if (!file_open_error_printed) {
std::cout << "hotas device not detected. check "
"whether it is "
"plugged" << std::endl;
file_open_error_printed = true;
}
close(fd);
hotas_connected = false;
}
}
}
void Hotas_manager::readData_from_hotas() {
int result;
result = read(fd, &joystick_event, sizeof(struct js_event));
if (result > 0) {
switch (joystick_event.type & ~JS_EVENT_INIT) {
case JS_EVENT_AXIS:
axis[joystick_event.number] = joystick_event.value;
axis_pressed = true;
button_pressed = false;
break;
case JS_EVENT_BUTTON:
button[joystick_event.number] = joystick_event.value;
button_pressed = true;
axis_pressed = false;
break;
}
message2_printed = false;
message_printed = false;
} else {
if (!message_printed) {
std::cout << "problem in reading the stick file" << std::endl;
message_printed = true;
}
hotas_connected = false;
open_hotas_device();
if (!message2_printed) {
std::cout << "stick re-connected" << std::endl;
message2_printed = true;
}
}
}
I updated the main function to run 3 threads .
int main() {
boost::asio::io_service receive_from_hmi_io;
udpComm receive_from_hmi(receive_from_hmi_io, 6012);
receive_from_hmi.setRemoteEndpoint("127.0.0.1", 6011);
receive_from_hmi.start_async_receive();
boost::thread thread_receive_from_hmi(&udpComm::run_io_service,
&receive_from_hmi);
boost::thread thread_serial(&thread_write_to_datalink);
boost::thread thread_hotas(&start_hotas);
thread_hotas.join();
thread_serial.join();
thread_receive_from_hmi.join();
return 0;
}
The void thread_write_to_datalink() also writes the data come from the hotas_manager(joysticks).
void thread_write_to_datalink() {
/*
* boost serial communication
*/
boost::asio::io_service serial_port_io;
Serial_manager serial(serial_port_io, (char*) "/dev/ttyUSB0");
cout << "aileron " << "throttle " << "elevator " << endl;
while (1) {
// commands from udp communication
serial.write_to_serial(read_hmi_buffer, 6);
// data come from joystick inputs
//cout << aileron<<" "<<throttle<<" "<<elevator<< endl;
memcpy(&buffer_manual_flightcontrol[4], &aileron, 4);
memcpy(&buffer_manual_flightcontrol[8], &throttle, 4);
memcpy(&buffer_manual_flightcontrol[12], &elevator, 4);
unsigned char temp;
try {
serial.write_to_serial(buffer_manual_flightcontrol, 32);
//serial.write_to_serial(buffer_manual_flightcontrol, 32);
} catch (std::exception& exp) {
serial.handle_serial_exception(exp);
}
try {
serial.write_to_serial(buffer_payloadcontrol, 20);
} catch (std::exception& exp) {
serial.handle_serial_exception(exp);
}
usleep(100000);
}
}
My question is how better can I design to synchronize these 3 threads. If your answer says you do not need to use 3 threads I need you to tell me how.
Let's back up a little bit from multi-threading, your program mixes synchronous and asynchronous operations. You don't need to do this, as it will only cause confusion. You can asynchronously write the buffer read from the UDP socket to the serial port. This can all be achieved with a single thread running the io_service, eliminating any concurrency concerns.
You will need to add buffer management to keep the data read from the socket in scope for the lifetime of the async_write for the serial port, study the async UDP server as an example. Also study the documentation, specifically the requirements for buffer lifetime in async_write
buffers
One or more buffers containing the data to be written.
Although the buffers object may be copied as necessary, ownership of
the underlying memory blocks is retained by the caller, which must
guarantee that they remain valid until the handler is called.
Once you have completed that design, then you can move to more advanced techniques such as a thread pool or multiple io_services.
You need to make your access to read_hmi_buffer synchronized.
Therefore you need a mutex (std::mutex, pthread_mutex_t, or the windows equivalent), to lock onto whenever a piece of code read or write in that buffer.
See this question for a few explanations on the concept and links to other tutorials.