Here's my entire program:
#include <iostream>
#include <windows.h>
int main() {
//? create the serial port file with read and write perms
HANDLE hPort = CreateFileW(L"COM3",
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (hPort == INVALID_HANDLE_VALUE) {
std::cout << "INVALID_HANDLE_VALUE/6\n";
if (GetLastError() == 2) {
std::cout << "serial port doesn't exist. error code: 2/ERROR_FILE_NOT_FOUND\n";
} else {
std::cout << "error occured with serial port file creation (CreateFileW). error code: " << GetLastError() << std::endl;
}
CloseHandle(hPort);
} else {
std::cout << "serial port created successfully (probably)\n";
}
DCB port_conf;
int err = GetCommState(hPort, &port_conf);
if (err == 0) {
std::cout << "GetCommState failed. error code: " << GetLastError() << "\n";
CloseHandle(hPort);
}
port_conf.BaudRate = 9600;
port_conf.Parity = NOPARITY;
port_conf.ByteSize = 8;
port_conf.StopBits = ONESTOPBIT;
port_conf.DCBlength = sizeof(port_conf);
err = SetCommState(hPort, &port_conf);
COMMTIMEOUTS timeouts_conf;
timeouts_conf.ReadIntervalTimeout = 1;
timeouts_conf.ReadTotalTimeoutConstant = 1;
timeouts_conf.ReadTotalTimeoutMultiplier = 1;
timeouts_conf.WriteTotalTimeoutConstant = 1;
timeouts_conf.WriteTotalTimeoutMultiplier = 1;
err = SetCommTimeouts(hPort, &timeouts_conf);
DWORD buffer_size_read;
char buffer_read[512]{};
int buffer_read_size;
char buffer_read_last[512]{};
while (1){
ReadFile(hPort,
buffer_read,
512,
&buffer_size_read,
0);
std::cout << buffer_read;
// if (buffer_read_last != buffer_read) {
// std::cout << buffer_read;
// }
// buffer_read_size = strlen(buffer_read);
// for (int i = 0; i <= buffer_read_size; i++) {
// buffer_read_last[i] = buffer_read[i];
// }
if (GetKeyState(VK_SPACE) != 0) {
break;
}
}
CloseHandle(hPort);
}
The problem with it is that everything is spit out too fast into cout. I made a miserable attempt at limiting this (it is commented out), but the program just doesn't do anything then. Another attempt was using the timeouts_conf.ReadIntervalTimeout, which Microsoft describes this way:
The maximum time allowed to elapse before the arrival of the next byte on the communications line, in milliseconds. If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.
but it didn't change anything. The serial port is continuously receiving data from a microcontroller in which I will not do the limiting for a pretty specific reason.
I need some sort of reliable way of not spitting everything out to cout at the speed of light. Thanks in advance
Related
In a Linux environment, I have an application that gets a file descriptor from an API call to a driver. The following function is what I use to read the data read in by a card on my system. About 1 out of 10 reads fail. I am puzzled as to why, after a successful select, and checking to see if the read_fd is set, no data is returned.
int MyClass::Read(int file_descriptor)
{
unsigned short read_buffer[READ_BUFFER_SIZE];
fd_set read_set;
time_val timeout;
int return_value = 0;
int count = 0;
int status = -1;
// Initialize read file descriptor
FD_ZERO(&read_set)
// Add driver file descriptor
FD_SET(file_descriptor, &read_set)
// Set timeout
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
while(!count)
{
// Wait for data to be available to read
return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);
// Make sure an error or a timeout didn't occur
if (-1 == return_value)
{
cout << "an error occurred" << endl;
}
else if (0 == return_value)
{
cout << "a timeout occurred" << endl;
}
else
{
// If the read file descriptor is set, read in the data
if (FD_ISSET(file_descriptor, &read_set))
{
count = read(file_descriptor, read_buffer, sizeof(read_buffer));
// Double check that data was read in
if (!count)
{
cout << "read failed" << endl;
}
else
{
// Set status to success
status = 0;
}
}
}
}
return status;
}
A return value of 0 from read (your if (!count) check) does not mean that the read failed -- it means that the read succeeded and got an EOF.
In any case, select returning with the file descriptor set does not mean that a read of that fd will not fail -- it means that a read of that fd will not block, and will return something immediately, either failure or success.
You are not using select() correctly. It modifies the fd_set, and possibly the time_val, so you have to reset them on each loop iteration.
Also, you are not handling errors correctly. read() returns -1 on error, 0 on disconnect, and > 0 on bytes read. You are not handling error and disconnect conditions correctly.
Try something more like this instead:
int MyClass::Read(int file_descriptor)
{
unsigned short read_buffer[READ_BUFFER_SIZE];
fd_set read_set;
time_val timeout;
int return_value, count;
do
{
// Initialize read file descriptor
FD_ZERO(&read_set);
// Add driver file descriptor
FD_SET(file_descriptor, &read_set);
// Set timeout
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
// Wait for data to be available to read
return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);
// Make sure an error or a timeout didn't occur
if (-1 == return_value)
{
cout << "select failed" << endl;
return -1;
}
if (0 == return_value)
{
cout << "select timed out" << endl;
continue; // or return, your choice...
}
// select() returned > 0, so the fd_set MUST be set,
// so no need to check it with FD_ISSET()...
// read in the data
count = read(file_descriptor, read_buffer, sizeof(read_buffer));
// Double check that data was actually read in
if (-1 == count)
{
cout << "read failed" << endl;
return -1;
}
if (0 == count)
{
cout << "peer disconnected" << endl;
return 0; // or -1, or whatever you want...
}
// success
break;
}
while (true);
return 0;
}
I created 2 functions to read and write across a serial port, I am coding in c++ with visual studios 2012, windows 7, 64 bit operating system, and using RS-232 serial cord. The board I'm connecting to is supposed to send 5 characters, TRG 1, upon pressing a button, the code works, however the output isn't always the correct values.
char serialRead()
{
char input[5];
DCB dcBus;
HANDLE hSerial;
DWORD bytesRead, eventMask;
COMMTIMEOUTS timeouts;
hSerial = CreateFile (L"\\\\.\\COM13", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hSerial == INVALID_HANDLE_VALUE)
{
cout << "error opening handle\n";
}
else
{
cout << "port opened\n";
}
dcBus.DCBlength = sizeof(dcBus);
if ((GetCommState(hSerial, &dcBus) == 0))
{
cout << "error getting comm state\n";
}
dcBus.BaudRate = CBR_9600;
dcBus.ByteSize = DATABITS_8;
dcBus.Parity = NOPARITY;
dcBus.StopBits = ONESTOPBIT;
if ((GetCommState(hSerial, &dcBus) == 0))
{
cout << "error setting comm state\n";
}
if ((GetCommTimeouts(hSerial, &timeouts) == 0))
{
cout << "error getting timeouts\n";
}
timeouts.ReadIntervalTimeout = 10;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 500;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 500;
if (SetCommTimeouts(hSerial, &timeouts) == 0)
{
cout << "error setting timeouts\n";
}
if (SetCommMask(hSerial, EV_RXCHAR) == 0)
{
cout << "error setting comm mask\n";
}
if (WaitCommEvent(hSerial, &eventMask, NULL))
{
if (ReadFile(hSerial, &input, 5, &bytesRead, NULL) !=0)
{
for (int i = 0; i < sizeof(input); i++)
{
cout << input[i];
}
cout << endl;
}
else
{
cout << "error reading file\n";
}
}
else
{
cout << "error waiting for comm event\n";
}
switch (input[4])
{
case '1' :
CloseHandle(hSerial);
return '1';
break;
case '2' :
CloseHandle(hSerial);
return '2';
break;
case '3' :
CloseHandle(hSerial);
return '3';
break;
case '4' :
CloseHandle(hSerial);
return '4';
break;
case '5':
CloseHandle(hSerial);
return '5';
break;
default :
CloseHandle(hSerial);
return '9';
break;
}
}
The code runs successfully in the sense that the port is configured correctly and data is being transmitted. The output varies, most of the time the output will print the whole "TRG 1", but randomly (it seems), the output will be "TRG|}|}" or "T|}|}|}|}", i.e. it will be part of the string and every character missing will be replaced with a "|}" instead of the correct characters. This is a problem because I want to be able to send it different values for trigger and run the switch of that variable.
I'm relatively new to serial communication and not an expert programmer so I'm wondering what's going on?
Serial communication is not packet-based. The information doesn't come to you in packages where the entire message can necessarily be read in one go; instead, it's a stream, so you could read half a message, a whole message, more than one message, etc.
As zdan said in the comments, you need to check the number bytes read from ReadFile and use that to compose 5-character packages which are your messages.
Specifically, only the first couple of characters up to the returned number of bytes read are valid; the rest are garbage.
At the time we are trying to create an interface for serial communication, to be able to communicate with a microprocessor.
Actually - everything works fine. Almost!
To be able to communicate with our controller, we need to sync up with it. To do this, we write a string: "?0{SY}13!", and the controller should then reply with "!0{SY}F5?" to accept the request for sync.
To do this, we use a writeData function (that works - we know that by using echo), and after that we use a readData to read the answer.
The problem is that, for some reason, it will not read anything. Though it returns 1 for success, the chars it reads is constanly " " (nothing).
Now comes the weird part - if we use an external terminal program to initialize the port (like putty), and then close the program, then everything works fine. It accepts the sync request, answers (and we can read it), and then we can do all that we want. But unless we use an external program to initialize the port, it doesn't work.
The constructor for the initializing the interface looks like this:
SerialIF::SerialIF(int baud, int byteSize, int stopBits, char* parity, int debug)
{
string coutport = getPort();
wstring wideport;
debug_ = debug; //Debuglevel
sync = false; //sync starts with false
error = false; //Error false as beginnging
//this is just for converting to the right type
for (int i = 0; i < coutport.length(); i++)
{
wideport += wchar_t(coutport[i]);
}
const wchar_t* port = wideport.c_str();
SerialIF::hserial = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hserial == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
if (debug_ != LOW)
{
cout << "[-] Port " << coutport << "doesn't exist." << endl;
}
}
if (debug_ != LOW)
{
cout << "[-] Handle error - is there another terminal active?" << endl;
}
error = true;
}
DCB dcbParms = { 0 };
dcbParms.DCBlength = sizeof(dcbParms);
if (!GetCommState(hserial, &dcbParms))
{
if (debug_ != LOW)
{
cout << "[-] Couldn't get status from port " << coutport << endl;
}
error = true;
}
if (!error)
{
setBaud(dcbParms, baud);
setParity(dcbParms, parity);
setByteSize(dcbParms, byteSize);
setStopbits(dcbParms, stopBits);
if (debug_ == HIGH)
{
cout << "[+] Serial port " << coutport << " has been activated. \nBaud-rate: " << baud << "\nParity: "
<< parity << "\nStop bits: " << stopBits << endl;
}
}
else if (debug_ != LOW)
{
cout << "[-] Port not initialized" << endl;
}
}
This should work - I really don't know why it shouldn't. It returns no errors, I've tried A LOT of error searching the last couple of days, I tried timeouts, I tried other ways of building it, but it all boils down to the same problem.
Why wont this initialize the port?
EDIT:
The output when trying to sync:
Can't post pictures due to lack of reputation. though it outputs as follows:
[+] Serial port COM1 has been activated.
Baud-rate: 9600
Parity: NONE
Stop bits: 1
[+] -> ?0{SY}13! is written to the port.
((And this is where it goes in to the infinite loop reading " "))
EDIT: code for read:
const int bytesToRead = 1; //I byte pr læsning
char buffer[bytesToRead + 1] = { 0 }; //Bufferen til data
DWORD dwBytesRead = 0; //Antal bytes læst
string store; //Store - den vi gemmer den samlede streng i
bool end = false; //Kontrolvariabel til whileloop.
while (end == false)
{
if (ReadFile(hserial, buffer, bytesToRead, &dwBytesRead, NULL))
/*Readfile læser fra interfacet vha. hserial som vi oprettede i constructoren*/
{
if (buffer[0] == '?') //Da protokollen slutter en modtaget streng med "?", sætter vi end til true
{ //Hvis denne læses.
end = true;
}
store += buffer[0];
}
else
{
if (debug_ != LOW)
{
cout << "[-] Read fail" << endl; //Hvis readfile returnerer false, så er der sket en fejl.
}
end = true;
}
}
if (debug_ == HIGH)
{
cout << "[+] Recieved: " << store << endl; //I forbindelse med debug, er det muligt at få udsrkevet det man fik ind.
}
recentIn = store; //RecentIN brugES i andre funktioner
if (verify()) //Som f.eks. her, hvor vi verificerer dataen
{
if (debug_ == HIGH)
{
cout << "[+] Verification success!" << endl;
}
return convertRecData(store);
}
else
{
if (debug_ != LOW)
{
cout << "[-] Verification failed." << endl;
}
vector <string> null; //Returnerer en string uden data i, hvis der er sket en fejl.
return null;
}
You never call SetCommState.
I'm not sure where your functions setBaud,setParity etc. come from, but I can't see how they can actually modify the serial port, as they don't have access to the comm device's handle.
ReadFile() can return success even when zero bytes are read. Use dwBytesRead to find the actual number of received characters.
while (ReadFile(hserial, buffer, 1, &dwBytesRead, NULL))
{
if (dwBytesRead != 0)
{
store += buffer[0];
if (buffer[0] == '?')
{
end = true;
break;
}
}
}
Had a similar problem between a PC and an arduino nano clone including a CH340. This post was the only one which discribes my problem very good.
I solved it by switching off DTR (data-terminal-ready) and RTS (request-to-send) flow control, which is normaly activated after (re)start the PC or plugging in the arduino. I found a descrition of this parameters in the documentation of DCB
I know that shis post is very old but maybe i can help somebody else with this idea/solution.
I am trying to make RS-232 communication between the PC and the PC MCU PIC. So I started making the PC program first in C++, and it was errorless, and according to the cout I made to output the status it should be working, but I wanted to be sure. So I downloaded Hyperterminal and connected Tx to Rx pin in serial com port, but whenever I try to connect the hyperterminal it gives an error, saying access denied (error 5) when I try to run my C++ program. I don't understand where the problem really is. Here's the full code, if the problem was the code's, just to make sure:
main.c:
#include <windows.h>
#include <winbase.h>
#include <iostream>
PDWORD sent;
char buf;
int main(){
DCB serial;
HANDLE hserial = CreateFile("\\\\.\\COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
serial.DCBlength = sizeof(DCB);
serial.BaudRate = CBR_9600;
serial.fBinary = true;
serial.fParity = false;
serial.ByteSize = 8;
serial.Parity = NOPARITY;
serial.StopBits = ONESTOPBIT;
char result = BuildCommDCB("baud=9600 parity=N data=8 stop=1", &serial);
if(result != 0){
std::cout << "DCB Structure Successfully Created!" << std::endl;
}else{
std::cout << "DCB Structure Creation Failed!" << std::endl;
}
if(hserial != INVALID_HANDLE_VALUE){
std::cout << "COM Port Handle Successfully Created!" << std::endl;
}else{
std::cout << "COM Port Handle Creation Failed!" << std::endl;
std::cout << GetLastError() << std::endl;
}
char res = WriteFile(hserial, "0xFF", 1, sent, NULL);
if(res != 0){
std::cout << "Writing to COM Port Successfull!" << std::endl;
}else{
std::cout << "Writing to COM Port Failed!" << std::endl;
std::cout << GetLastError() << std::endl;
}
CloseHandle(&hserial);
return 0;
}
Only one program at a time can open a particular COM port. You can do your test if you have two COM ports available.
I've had my socket class working for a while now, but I wanted to add a timeout using select(). Seems pretty straight forward but I always have 0 returned from select(). I've even removed the select() check so it reads data regardless of select() and the data gets read, but select() still reports that data is not present. Any clue on how to get select() to stop lying to me? I've also set the socket to non-blocking. Thanks.
Code:
char buf [ MAXRECV + 1 ];
s = "";
memset ( buf, 0, MAXRECV + 1 );
struct timeval tv;
int retval;
fd_set Sockets;
FD_ZERO(&Sockets);
FD_SET(m_sock,&Sockets);
// Print sock int for sainity
std::cout << "\nm_sock:" << m_sock << "\n";
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(1, &Sockets, NULL, NULL, &tv);
std::cout << "\nretval is :[" << retval << "]\n\n";
// Check
if (FD_ISSET(m_sock,&Sockets))
std::cout << "\nFD_ISSET(m_sock,&Sockets) is true\n\n";
else
std::cout << "\nFD_ISSET(m_sock,&Sockets) is false\n\n";
// If error occurs
if (retval == -1)
{
perror("select()");
std::cout << "\nERROR IN SELECT()\n";
}
// If data present
else if (retval)
{
std::cout << "\nDATA IS READY TO BE READ\n";
std::cout << "recv ( m_sock, buf, MAXRECV, 0)... m_sock is " << m_sock << "\n";
int status = recv ( m_sock, buf, MAXRECV, 0 );
if ( status == -1 )
{
std::cout << "status == -1 errno == " << errno << " in Socket::recv\n";
return 0;
}
else if ( status == 0 )
{
return 0;
}
else
{
s = buf;
return status;
}
}
// If data not present
else
{
std::cout << "\nDATA WAS NOT READY, TIMEOUT\n";
return 0;
}
Your call to select is incorrect, as you have already discovered. Even though the first parameter is named nfds in many forms of documentation, it is actually one more than the largest file descriptor number held by any of the fd_sets passed to select. In this case, since you are only passing in one file descriptor, the call should be:
retval = select(m_sock + 1, &Sockets, NULL, NULL, &tv);
If you have an arbitrary number of sockets you are handling each in a different thread, you might find my answer to this question a better approach.
Whoops. Looks like I forgot to set select()'s int nfds:
working good now.