I have to read many(8) serial devices on my project. They are Pantilt, Camera, GPS, Compass etc. they all are RS232 devices but they have different command structure and behavior. e.g GPS starts sending data as
soon as I open the port. where as PanTilt and Camera only responds when I send specific commands to them.
I use following environment
OS: Ubuntu 11.10
Language: C++
Framework: Qt 4.7
For PanTilt and Camera I want to develop function like this.
int SendCommand(string& command, string& response)
{
port.write(command, strlen(command));
while(1)
{
if(response contains '\n') break;
port.read(response) // Blocking Read
}
return num_of_bytes_read;
}
I want to implement this way as this function will be used as building block for more complex algorithm
like this...
SendCoammd("GET PAN ANGLE", &angle);
if(angle > 60)
SendCommand("STOP PAN", &stopped?);
if(stopped? == true)
SendCommand("REVERSE PAN DIRECTION", &revesed?);
if(reversed? == true)
SendCommand("START PAN", &ok);
To do something like this I need strict synchronous behavior. Anybody has any idea how to approach this?
I found this tutorial very intresting and helpful.
http://www.webalice.it/fede.tft/serial_port/serial_port.html
It shows how boost::asio can be used to perform both sync and async read/write.
Thank You for the help!
what is the problem of using standard file API?
fd = open("/dev/ttyX");
write(fd, command.cstr(), command.size());
vector v(MAX_SIZE);
read(fd,&v[0], MAX_SIZE);
Low-level communication with serial port in Qt can be established with QExtSerialPort library that partially implements QIODevice interface.
The high level protocols you have to implement yourself.
Related
I have a USB device attached. I decided to try to write some code where I open the USB device as a FileDescriptor like on Linux where I can use ioctl function on the fd to send commands.
I ended up with the following:
CFMutableDictionaryRef matches = IOServiceMatching(kIOUSBDeviceClassName);
if (!matches)
{
std::cerr<<human_error_string(kIOReturnError)<<"\n";
return;
}
io_iterator_t deviceIterator;
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matches, &deviceIterator);
if (kr != kIOReturnSuccess)
{
std::cerr<<human_error_string(kr)<<"\n";
return;
}
io_service_t service = IO_OBJECT_NULL;
while((service = IOIteratorNext(deviceIterator)))
{
int32_t fd = IOServiceOpenAsFileDescriptor(service, O_RDWR) //doesn't work! Returns: -1
IOObjectRelease(service);
}
IOObjectRelease(deviceIterator);
However, the FD is always -1.. Any ideas why?
As far as I'm aware, there isn't any kind of file descriptor based API for USB on macOS. You need to go through the IOUSB based APIs. There's some example code here.
If you want to share as much code as possible between Linux an macOS, you can use libusb which wraps both Apple's IOKit API and Linux's ioctl based USB API.
I have to admit I'm not exactly sure what IOServiceOpenAsFileDescriptor is intended for. The source code in IOKitLib is not terribly enlightening, as it simply connects to an XPC service and the file descriptor is expected in the XPC reply. I assume the service is implemented in a system daemon that isn't open source or documented.
Perhaps IOServiceOpenAsFileDescriptor is for accessing block devices or serial ports, which have both an IOKit object and a device node file in /dev/. General USB devices do not have a node in the file system.
I was wondering, if it is possible to let one client communicate with multiple server at the same time. As far as I know, common browsers like for example firefox are doing exactly this.
The problem I have now is, that the client has to listen and wait for data from the server, rather then requesting it itself. It has to listen to multiple server at once. Is this even possible? What happens if the client is listening to server 1 and server 2 sends something? Is the package lost or will it be resend until the client communicates a successful receival? The protocol used is TCP.
edit: platform is Windows. Thanks for pointing this out Arunmu.
This is nothing different from regular socket programming using select/poll/epoll OR using thread-pool OR using process-per-connection OR whatever model that you know.
I can show you a rough pseudo-code on how to do it with epoll.
NOTE: None of my functions exist in C++, its just for explanation purpose. ANd I am ALSO assuming that you are on Linux, since you have mentioned nothing about the platform.
socket sd = connect("server.com", 8080);
sd.set_nonblocking(1);
epoll_event event;
event.data.fd = sd
epoll_ctl(ADD, event);
...
...
while (True) {
auto n = epoll_wait(events, 1);
for (int i : 1...n) {
if (events[i].data.fd == sd) // The socket added in epoll_ctl
{
std::thread(&Session::read_handler, rd_hndler_, sd); // Call the read in another thread or same thread
}
}
}
I hope you got the gist. In essence, think of server like a client and client like a server and you have your problem solved (kind of). Check out below link to know more about epoll
https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
To see an fully functional server design using epoll, checkout:
https://github.com/arun11299/cpp-reactor-server/blob/master/epoll/reactor.cc
I'm trying to read from ardupilot that using mavlink protocol for sending data. if you visit this site:
http://qgroundcontrol.org/mavlink/start
you can find how mavlink protocol works and what I'm trying to do. at the bottom of the site you can see the header's bytes arrangement.
I wrote some code and it's works correctly but after running for a while runtime error appears. as far as I know this errors are about memory allocations but I can't find where I allocate memory illegally.
Here is my reader thread code:
QByteArray header;
quint8 ID;
QByteArray payload;
QSerialPort *serial;
void mThread::run()
{//1
while(serial->isOpen())
{//2
if(serial->bytesAvailable() >= 200)
{//3
this->msleep(1);
header = serial->read(1);
if(header.contains(254))
{//4
this->msleep(5);
header.append(serial->read(5));
if((header[3] == 1) && (header[4] == 1))
{//5
ID = header[5];
msleep(12);
payload = serial->read(header[1]);
emit updated(payload , ID);
payload.clear();
header.clear();
}//5
else
{//6
header.clear();
}//6
}//4
else
{//7
header.clear();
}//7
}//3
}//2
}//1
I'm using qt5 whit ubuntu 14.04
and I should mention that I'm reading data with 115200baud and it should manage with thread. If not reading from it will lock my ui.
Do you really need all those msleeps... Reading at that baud rate I would think your serial port input buffer would overflow pretty quickly. Might be worth the time to have a look at it.
As a complete different approach why not use the MAVlink generator to generate a library that you could use. This way it saves you from the trouble of parsing the messages and error checking them and all that...
Currently, I'm reading the CTS and DSR signals of a serial port in the following way:
bool get_cts(int fd) {
int s;
ioctl(fd, TIOCMGET, &s);
return (s & TIOCM_CTS) != 0;
}
Now I'd like to wait until get_cts() returns true. A simple loop isn't the best solution I think (as it's extremely resource-intensive).
void wait_cts(int fd) {
while(1) {
if(get_cts(fd)) {
return;
}
}
}
Is there any better solution using C or C++ on Linux? (I cannot use any hardware flow control as I don't need the serial data lines at all.)
There is the ioctl TIOCMIWAIT which blocks until a given set of signals change.
Sadly this ioctl is not documented in the tty_ioctl(4) page nor in ioctl_list(4).
I have learned about this ioctl in this question:
Python monitor serial port (RS-232) handshake signals
The select system call is meant for applications like that. You can do other work, or sleep, then periodically check the status of the FD_SET. It might even be overkill for what you are doing, if your program does nothing else but grab data.
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?