New to linux: C++ opening and closing usb port issues - c++

New developer, Linux, C++, USB - Serial Adapter.
I've completed a program where I am able to write to the USB port. However, if I change my code, make, log back in as root, and try to write to the port again, it doesn't go through. It'll only work if I remove the USB cable from the computer and reseat it before attempting sending data again. If you need more info let me know.
I'm on two different computers and have no way of copying and pasting but here is the gist of what I'm doing.
int fd = 0;
int iOut = 0;
char *ComPort = "/dev/ttyUSB0";
fd=open(ComPort, O_CREAT | O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1)
cout << "unable to open" << endl;
// blah blah getting data ready to be sent
// create a block of 50 hex characters to be sent : DB
iOut = write(fd, $DB, sizeof(DB));
// blah blah error checking
close(fd);
return(0);

#Surt #alexfarber I had a talk with a coworker on this and we concluded that this is most likely a hardware issue with my display or usb to serial adapter. I believe the only way this can work with this particular adapter is by turning off the power to it and turning it back on in order to reflect what it would see when being removed and reseated manually. I dont believe this is possible but I'll start another thread with anything I may run into. I appreciate you all taking the time to help with this, I did learn a number of other things I didn't know before hand so this was still very helpful. Thank you once again.

Take at look at chapter 3.2 here http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html
add some of the error checking first so you can see where if fails. The perror line will help there.
if (fd <0) {perror(ComPort ); exit(-1); } // note the exit which your code doesn't have.
This should now tell you some more info and add
if (errno) {perror(ComPort ); exit(-1); }
after all operations, read, write and set things on the fd.
now add the newtio part of 3.2 to your program in case some handshake failed. You must change it so it conforms with the display.
The final version of your program might be more like 3.3.

Related

How can i mock a serial port UART linux

Preface
So basically im doing a project for a extracurricular activity and it involves having a microcontroller read some data from a CAN bus and then send that data over through a UART serial connection to a bananaPi Zero M2 thats currently running arch linux.
The microcontroller is probably an arduino of some kind(most likely a modified version of it), the problem resides with the constant change of the project, and since i want my code to survive longer than a year a part of that is creating tests, ive been looking for a way to emulate the serial connection that is made from the bananaPi (on file/dev /dev/ttyS0) to the microcontroller so that i dont have to constantly compile the code for the bananaPi set everything up just to check if "hello" is being correctly sent over the serial line. The thing is i havent found a way to sucessfully virtualize a serial port
Attempts
So i've looked a bit on options and i found socat, apparently it can redirect sockets and all kinds of connections and especially baud rates(although personally its not really that relevant for giving credence to the tests to my colleagues is of the most importance) So i spent a evening trying to learn three things at once and after a lot of problems and a lot of learning i came to this
void Tst_serialport::sanityCheck(){
socat.startDetached("socat -d -d pty,rawer,b115200,link=/tmp/banana, pty,rawer,b115200,link=/tmp/tango");
sleep(1);
_store = new store("/tmp/banana");
QCOMPARE(_store->dev,"/tmp/banana");
}
void Tst_serialport::checkSendMessage(){
QSerialPort tango;
tango.setPortName("/tmp/tango");
tango.setBaudRate(QSerialPort::Baud115200);
tango.setDataBits(QSerialPort::Data8);
tango.setParity(QSerialPort::NoParity);
tango.setStopBits(QSerialPort::OneStop);
tango.setFlowControl(QSerialPort::NoFlowControl);
tango.open(QIODevice::ReadWrite);
tango.write("Hello");
tango.waitForBytesWritten();
tango.close();
QCOMPARE(_store->lastMessage,"Hello");
}
void Tst_serialport::closeHandle(){
socat.close();
}
QTEST_MAIN(Tst_serialport)
The intent here being that in sanityCheck a fake serial device would be created on /tmp/banana and /tmp/tango that would redirect io between each other so that when _store started listening to banana and i sent a message to tango i would receive that same message inside the store object
The thing is the function that is waiting for messages, etc... isnt triggering even tough ive managed to work with it when i had an arduino plugged directly to my computer
before continuing im sorry that the code is kinda all messed up, im kinda new to both qt and c++, although i have some experience with C which made me use a lot of C stuff when i shouldve sticked with qt. Unfortunately i havent had much time to refactor everything to a more clean version of the code
Here's the other side
int store::setupSerial() {
QSerialPort* serial= new QSerialPort();
serial->setPortName(this->dev);
serial->setBaudRate(QSerialPort::Baud115200);
serial->setDataBits(QSerialPort::Data8);
serial->setStopBits(QSerialPort::OneStop);
serial->setParity(QSerialPort::NoParity);
serial->setFlowControl(QSerialPort::NoFlowControl);
if (!serial->open(QIODevice::ReadOnly)) {
qDebug() << "Can't open " << this->dev << ", error code" << serial->error();
return 1;
}
this->port = serial;
connect(this->port, &QSerialPort::readyRead, this, &store::handleReadyRead);
connect(this->port, &QSerialPort::errorOccurred, this, &store::handleError);
return 0;
}
store::store( char * dev, QObject *parent ): QObject(parent){
if (dev == nullptr){
// TODO: fix this(use a better function preferably one handled by QT)
int len = sizeof(char)*strlen(DEFAULT_DEVICE)+1;
this->dev = (char*)malloc(len);
strcpy(this->dev,DEFAULT_DEVICE);
}
//copy dev to this->dev
else{
int len = sizeof(char)*strlen(dev)+1;
this->dev = (char*)malloc(len);
strcpy(this->dev,dev);
}
setupSerial();
}
void store::handleReadyRead(){
bufferMessage=port->readAll();
serialLog.append(bufferMessage);
//can be optimized using pointers or even a variable as a "bookmark" wether a int or pointer
lastMessage.append(bufferMessage);
uint32_t size = (int)lastMessage[0] | (int)lastMessage[1] << 8 | (int)lastMessage[2] << 16 | (int)lastMessage[3] << 24;
int8_t eof = 0x00;
if((bool)((long unsigned int)lastMessage.size() == size+sizeof(size)+sizeof(eof))&& ((bool) lastMessage[lastMessage.size()-1] == eof)){
parseJson();
//clear lastMessage()
lastMessage.clear();
}
}
//... some other code here
If you're wondering whats the output or the result well
11:23:40: Starting /home/micron/sav/Trabalhos/2022-2023/FormulaStudent/VolanteAlphaQT/build-VolanteAlphaQT-Desktop-Testing/bin/VolanteAlphaQT_testes...
********* Start testing of Tst_serialport *********
Config: Using QtTest library 5.15.8, Qt 5.15.8 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 12.2.1 20230201), arch unknown
PASS : Tst_serialport::initTestCase()
2023/02/15 11:23:40 socat[6248] N PTY is /dev/pts/2
2023/02/15 11:23:40 socat[6248] N PTY is /dev/pts/3
2023/02/15 11:23:40 socat[6248] N starting data transfer loop with FDs [5,5] and [7,7]
PASS : Tst_serialport::sanityCheck()
FAIL! : Tst_serialport::checkSendMessage() Compared values are not the same
Actual (_store->lastMessage): ""
Expected ("Hello") : Hello
Loc: [../VolanteAlphaQT_1/test/tst_serialport.cpp(35)]
PASS : Tst_serialport::closeHandle()
PASS : Tst_serialport::cleanupTestCase()
Totals: 4 passed, 1 failed, 0 skipped, 0 blacklisted, 1005ms
********* Finished testing of Tst_serialport *********
11:23:41: /home/micron/sav/Trabalhos/2022-2023/FormulaStudent/VolanteAlphaQT/build-VolanteAlphaQT-Desktop-Testing/bin/VolanteAlphaQT_testes exited with code 1
As usual all per most of my questions its not very descriptive it basically just never triggers the signal ReadyRead which in turn causes last message to be blank
Conclusion / TL;DR
So what am i doing wrong? why is the ready read signal not being triggered? Is there a better way to simulate/mock a serial connection?
Well, I found the solution.
Apparently it wasn't a socat problem, the ready signal is way slower than I had in mind and when I slept it actually froze the process. Due to the ready signal taking some time even after the buffer itself being ready, the QCOMPARE came right after the "unfreeze" making the stall useless.
The actual solution was rather simple I placed a _store->waitForReadyRead(); so I could wait for the signal to be sent without freezing the process.

FFmpeg - RTCP BYE packets

I’m working on some C++ project which depends on Wi-Fi RAK5206 electronic board. I’m using ffmpeg library to obtain video and audio stream and I have issue where I can start and stop stream for four times, but when I want to start for the fifth time I get error. Error description is Invalid data found when processing input and it happens when I call avformat_open_input function and I need to restart the electronic board, reconnect to Wi-Fi etc.
I figured out with Wireshark application that VLC is working and it is sending some BYE packets when TEARDOWN is called. I wonder if error depends to them, because from my application I’m not sending. How I can make setup to force ffmpeg to send BYE packets?
I found some declarations in rtpenc.h file which options to set and tried when I want to connect, but obviously without success.
The code that I used for setting options and opening input:
AVDictionary* stream_opts = 0;
av_dict_set(&stream_opts, "rtpflags", "send_bye", 0);
avformat_open_input(&format_ctx, url.c_str(), NULL, &stream_opts);
Make sure you are calling this av_write_trailer function, from your application.
if not please debug and check it.
/* Write the trailer, if any. The trailer must be written before you
* close the CodecContexts open when you wrote the header; otherwise
* av_write_trailer() may try to use memory that was freed on
* av_codec_close(). */
av_write_trailer(oc);
function Call flow code snippet from ffmpeg source:
av_write_trailer ->
....
ret = s->oformat->write_trailer(s);
} else {
s->oformat->write_trailer(s);
}
...
.write_trailer = rtp_write_trailer ->
...
if (s1->pb && (s->flags & FF_RTP_FLAG_SEND_BYE))
rtcp_send_sr(s1, ff_ntp_time(), 1)
Resolved issue with adding flag 16 (binary: 10000) to AVFormatContext object's flag.
formatCtx->flags = formatCtx->flags | 16;
According to rtpenc.h:
#define FF_RTP_FLAG_SEND_BYE 16

How to read from serial device in C++

I'm trying to figure out how I should read/write to my Arduino using serial communication in Linux C++. Currently I'm trying to read a response from my Arudino that I "trigger" with
echo "g" > /dev/ttyACM0"
I've tried looking at the response from my Arduino in my terminal, by using the following command:
tail -f /dev/ttyACM0
This is working as it should. Now I want to do the same thing in a C++ application. So I made this test
void testSerialComm()
{
std::string port = "/dev/ttyACM0";
int device = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
std::string response;
char buffer[64];
do
{
int n = read(device, buffer, sizeof buffer);
if (n > 0) {
response += std::string(buffer);
std::cout << buffer;
}
} while (buffer[0] != 'X'); // 'X' means end of transmission
std::cout << "Response is: " << std::endl;
std::cout << response << std::endl;
close(device);
}
After a few "messages", the transmission gets a little messed up. My test application writes the response characters in random order and something's not right. I tried configuring the /dev/ttyACM0 device by this command:
stty -F /dev/ttyUSB0 cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts
No dice. Can someone help me understand how to communicate with my Arduino in C++?
The shown code opens /dev/ttyACM0, attempts to seek to the end of this "file", and based on the resulting file position allocates an old-fashioned, C-style memory buffer.
The problem with this approach is that you can only seek through regular, plain, garden-variety files. /dev/ttyACM0 is not a regular file. It's a device. Although some devices are seekable, this one isn't. Which, according to the comments, you've discovered independently.
Serial port devices are readable and writable. They are not seekable. There's no such thing as "seek"ing on a serial port. That makes no sense.
To read from the serial port you just read it, that's all. The operating system does maintain an internal buffer of some size, so if some characters were already received over the serial port, the initial read will return them all (provided that the read() buffer size is sufficiently large). If you pass a 1024 character buffer, for example, and five characters were already read from the serial port read() will return 5, to indicate that accordingly.
If no characters have been read, and you opened the serial port as a blocking device, read() will block at least until one character has been read from the serial port, and then return.
So, in order to read from the serial port all you have to do is read from it, until you've decided that you've read all there is to read from it. How do you decide that? That's up to you. You may decide that you want to read only until reading a newline character. Or you may decide that you want to read only until a fixed #n number of characters have been read. That's entirely up to you.
And, of course, if the hardware is suitably arranged, and you make the necessary arrangements with the serial port device to respect the serial port control pins, and, depending on your configuration, the DCD and/or DSR pins are signaled to indicate that the serial port device is no longer available, your read() will immediately return 0, to indicate a pseudo-end of file condition on the serial port device. That's also something that you will need to implement the necessary logic to handle.
P.S. neither C-style stdio, nor C++-style iostreams will work quite well with character devices, due to their own internal buffering algorithms. When working with serial ports, using open(2), read(2), write(2), and close(2) is better. But all of the above still applies.

Linux system does not allow creation of sockets because of "Too many open files" *even after a reboot*

There is a line in my code where I am doing this-
int sockDesc = socket(AF_INET, SOCK_DGRAM, 0);
Earlier it was working like a charm, but then suddenly one day the function returned something less than zero. So I examined standard error and found Too many open files.
I realised that what I have done is, exited the program using ctrl + c many times. so may be the sockets are somehow still open and I need to do something about it (apart from writing a signal handler of course), like - increasing some limits in the /etc/sysctl.conf file and all.
But that is wrong right? When I exited the program, won't linux automatically clean up after me ?
Just to confirm this is not some issue caused by stuff let opened by me all over the RAM, I rebooted-
Still the same error !
What is going on here ?
But wait a minute. I had already closed my program and restarted it. Why must I get such an error in my system? What is a way to correctly diagnose this? Is this really about sockets or any other type of open file descriptors? What should be my next step to solve this issue?
EDIT 1:
I ran another small program:
int main()
{
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
return 1;
}
else
{
printf("Opening datagram socket....OK.\n");
}
close(sd);
return true;
}
Which ran with no issues.
Now I really do not understand what could be the issue. The exact same code is not working when it is running from my actual code base.
Also, the output of lsof has only 236 lines in it for my user, which means that I a well below the soft limit of 1024.
EDIT 2
Here is some of the code explanation to explain how I am doing things
There is a main file that creates multiple threads, each one being a network connection to a multicast stream. The beginning of each such thread is the line int sockDesc = socket(AF_INET, SOCK_DGRAM, 0);. This is where it fails. For some reason it says "too many open files" and the socket is not created.
I used lsof command as suggested in the comments. Was able to look at what was going wrong that way - I had written a faulty constructor for a class that is instantiated a gazillion times. A rogue file descriptor was getting created and hence the error.

RS-232 confusion under C++

What's the problem in given code? Why it is not showing the output for rs232 when we connect it by the d-9 connector with the short of pin number 2 & 3 in that?
#include <bios.h>
#include <conio.h>
#define COM1 0
#define DATA_READY 0x100
#define SETTINGS ( 0x80 | 0x02 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
bioscom(0, SETTINGS, COM1); /*initialize the port*/
cprintf("Data sent to you: ");
while (1)
{
status = bioscom(3, 0, COM1); /*wait until get a data*/
if (status & DATA_READY)
if ((out = bioscom(2, 0, COM1) & 0x7F) != 0) /*input a data*/
putch(out);
if (kbhit())
{
if ((in = getch()) == 27) /* ASCII of Esc*/
break;
bioscom(1, in, COM1); /*output a data*/
}
}
return 0;
}
Well, the code looks alright. Have you really connected the remaining pins correctly in the plug, see serial and pin connections.
Nothing obvious stands out from your code as the cause. Check all your bases as you are dealing with hardware/software. The following Microsoft article has a different implementation using _bios_serialcom (from bios.h) which might be a good reference point for you.
http://support.microsoft.com/kb/39501
Suggestions for where to go from here:
I would also suggest replacing the literals (eg 0x08) using the constants predefined for Baud rate, Parity (eg _COM_NOPARITY) to make the code more readable in your question.
Check that the Com port is actually open, as its a assumption which is unchecked in your code example above.
Also check up on the pin connections for the DB9. To connect two computers/devices you will need to null modem it, eg pin 2 to pin 3 at the other end, plus the Signal Ground. Make sure you are disabling/not looking for DTR.
If the other computer/device is setup then I would suggest first running HyperTerminal (Programs->Accessories->Communication) and connect to your COM 1 and check you can see characters from the other device. If not its most likely related to your cable.
Hope that helps.
Before checking with your code always check your serial communication with a terminal program. I don't have much experience with Windows environment but in Linux you have programs like cutecom or gtkterm where you can send/receive data from serial port. We extensively used these programs for serial communication in Linux, they are great for debugging potential problems with serial port interface (both h/w & s/w as well). So, before suspecting your code check with a terminal emulator program.
Hey, I am not an expert on Win32, but it seems to be easier to use another article as a source (the one mentioned here before looks outdated):
http://msdn.microsoft.com/en-us/library/ms810467
This is old too, dated around 1995, but it looks still effective. The NT architecture is very restrictive when it comes to grant access to hardware, for example, to send bytes to a PC paralell port one needs to rely on workarounds dll written by open source developers.
I'm assuming from your comment about "pin 2 & 3" that you've connected a loopback cable, so you're expecting to see anything you type come up on the screen (and stop doing so when you disconnect the cable).
I think there's a bug in the code: the if (kbhit()) is inside the if (status & DATA_READY).
That means you only check the keyboard if there is some input ready to receive from the serial port - which there won't be, because you haven't sent it anything yet! Try moving the test and see if it improves matters.
(Here is some similar serial port code that puts the if (kbhit()) test outside the DATA_READY check. It doesn't claim to work, but provides some evidence that this might be the source of the problem...)