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.
Related
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
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;
}
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.
cout<<"getting in issue read operation"<<endl;
ReadFile(hSerial, readbuff, dwBytesRead, &dwBytesRead, NULL);
cout<<"error: "<<GetLastError()<<endl;
if (!ReadFile(hSerial, readbuff, dwBytesRead, &dwBytesRead, NULL))
{
if (GetLastError() != ERROR_IO_PENDING)
cout << "Error in communications; report it.";
else
fWaitingOnRead = TRUE;
}
else
{
cout << "no waiting\n";
cout << "no. of bytes read: " <<dwBytesRead << endl;
cout<<"read buff: ";
for (DWORD i = 0; i < sizeof(writebuff); i++)
{
cout<< readbuff[i];`enter code here`
}
cout<<endl;
}
i cant understand whats going wrong coz each tym i have 0 bytes read.....
help me plzzz.....
You must check the value returned by ReadFile. Call GetLastError only if ReadFile returns zero.
ReadFile parameter 3 should be the size of your buffer. Parameter 4 should be a separate variable that will receive the number of bytes actually read.
Since you are passing NULL for Parameter 5 you will not get overlapped operation, so ReadFile will never return ERROR_IO_PENDING.
You may have more success if you use a proven library for serial port operations, such as this one:
http://www.naughter.com/serialport.html
UPDATE:
Looking through the protocol here, I can't figure out what goes into the Unsized Envelope Record. I can't find any examples online.
ORIGINAL:
I have the following WCF service
static void Main(string[] args)
{
var inst = new PlusFiver();
using (ServiceHost host = new ServiceHost(inst,
new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IPlusFive), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipePlusFive");
host.Open();
Console.WriteLine("Service is Available. Press enter to exit.");
Console.ReadLine();
host.Close();
}
}
[ServiceContract]
public interface IPlusFive
{
[OperationContract]
int PlusFive(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PlusFiver : IPlusFive
{
public int PlusFive(int value)
{
Console.WriteLine("Adding 5 to " + value);
return value + 5;
}
}
I output the adding 5 line so I know if the server processed the
request or not.
I have a .NET client that I used to test this and everything works as
expected.
Now I want to make an unmanaged C++ client for this.
I figured out how to get the name of the pipe, and write to it.
I've downloaded the protocol from here
I can write to the pipe but I can't read to it. Whenever I try to read from it I get a ERROR_BROKEN_PIPE 109 (0x6D) The pipe has been ended. error. If I replace the read with a write, the write is successful, so I don't think that the pipe is closed, at least not until I try to do a read.
Here is how I'm connecting to the pipe.
HANDLE OpenPipe(OLECHAR* bstrGuid)
{
wstring pipeName = L"\\\\.\\pipe\\";
wstring strGuid = bstrGuid;
pipeName.append(strGuid.substr(1,36));
wcout << "Pipe Name " << endl;
wcout << pipeName.c_str() << endl;
HANDLE hPipe = CreateFile(pipeName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
{
wcout << "failed to create pipe" << endl;
system("pause");
return NULL;
}
return hPipe;
}
this is how i'm creating the first message that I'm sending
std::list<wchar_t> GetFirstMessage()
{
std::list<wchar_t> message;
message.push_back(0x00);// version record
message.push_back(0x01);// major version
message.push_back(0x00);// minor version
message.push_back(0x01);// mode record
message.push_back(0x01);// singleton-unsized mode
message.push_back(0x02);// via record
wstring url = L"net.pipe://localhost/PipePlusFive";
message.push_back(url.length());// via length
for(int x= 0;x<url.length();x++)
{
message.push_back(url[x]); // via
}
message.push_back(0x03);
message.push_back(0x08);
return message;
}
This is how I'm writing it to the file.
int WriteMessage(HANDLE hPipe, LPVOID message, int size)
{
DWORD bytesWritten;
BOOL bWrite = WriteFile(hPipe, &message, size, &bytesWritten, NULL);
wcout << "Bytes Written: " << bytesWritten << endl;
if(bWrite == false)
{
wcout << "fail"<<endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
return 0;
}
list<wchar_t> full_message = GetFirstMessage();
int result = WriteMessage(hPipe, &full_message, full_message.size());
if (result == 1)
{ return 1;}
Here is how I'm writing the end message
wchar_t message = 12;
result = WriteMessage(hPipe, &message, 1);
if (result == 1)
{ return 1;}
here is how I'm trying to read the response
char buffer[10];
DWORD bytesRead;
BOOL bRead = ReadFile(hPipe, buffer, 1, &bytesRead, NULL);
if(bRead == false)
{
wcout << "fail read"<<endl;
wcout << "error: " << GetLastError() << endl;
CloseHandle(hPipe);
system("pause");
return 1;
}
I'm new to c++, so I don't know if I'm not following the protocol correctly or making a stupid mistake in the way I'm trying to do this?
UPDATE:
The problem was that I was writing the pointer address to the named pipe instead of the contents of the list. I've fixed that And I'm now able to read the Preamble Ack Record. Now I have to figure out what needs to be sent for the next part of the protocol.
Check if this works for you
Try to open a named pipe. (CreateFile)
Set the read mode and the blocking mode of the specified named pipe. (SetNamedPipeHandleState)
Send a message to the pipe server and receive its response. (WriteFile, ReadFile)
Close the pipe. (CloseHandle)