written below is a crude code i wrote for accessing a serial port. attached to that port is a microcontroller that sends me data and having a baud rate of 57600. i ran the code once and got results but after closing the window, my computer hanged. i pressed ctrl+alt+del and saw that under processes my program was still running. i cant close it too.
int n = 20;
char szBuff[20 + 1] = {0};
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts={0};
DWORD dwBytesRead = 20;
dcbSerialParams.DCBlength=sizeof(DCB);
hSerial = CreateFile("COM5",
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
{
puts ("cannot open port!");
}
puts ("invalid handle value!");
}
if (!GetCommState(hSerial, &dcbSerialParams))
{
puts ("error getting state");
}
dcbSerialParams.BaudRate=CBR_57600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
{
puts ("error setting port state");
}
while (1)
{
if(!ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL)){
puts ("serial read error fail!");
}
else
{
printf ("%s\n" , szBuff);
}
}
That sounds like a buggy serial port driver. See Why do some process stay in Task Manager after they've been killed?. When you kill your program, it doesn't go away until all of its pending I/O operations have been canceled.
You MUST to define some type of stop to the app if it will loop in a while(1).
Option 1: The microcontroller must send a terminate command or character that you use to break the while.
Option 2: Some type of timeout instead while(1).
Option 3: Catch some signal and then break the while().
And there are a lot of other options.
Related
I am a noob at serial programming. I am trying to code a passive monitor in C that displays to screen whatever is written to or read from a COM port. Most of the code, that I have seen actually reads from or writes to the COM Port.
I have tried to read from a COM port that is transmitting and receive Modbus traffic but I get no readings. I am using a com0com serial port emulator. Only time the code works is if I actually read from the other port that the COM port is paired with.
I am trying to mimic the Serial Port Monitor application. So far it is not working. Kindly assist.
Thanks.
Below is the code for the COM read:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void setupPort(HANDLE * handle, char * portName);
void readFromPort(HANDLE * handle);
int main()
{
HANDLE first_port;
char * first_port_name = "COM3";
setupPort(&first_port, first_port_name);
readFromPort(&first_port);
return 0;
}
void setupPort(HANDLE * handle, char * portName)
{
BOOL status;
*handle = CreateFile(portName, //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (handle == INVALID_HANDLE_VALUE)
{
printf("\n%s could not be opened\n", portName);
}
else
{
printf("\n%s successfully opened.\n", portName);
}
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
status = GetCommState(*handle, &dcbSerialParams); //retreives the current settings
if (status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
status = SetCommState(*handle, &dcbSerialParams); //Configuring the port according to settings in DCB
if (status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else //If Successful display the contents of the DCB Structure
{
printf("\n\n Setting DCB Structure Successful\n");
printf("\n Baudrate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(*handle, &timeouts) == FALSE)
printf("\n\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successful");
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
status = SetCommMask(*handle, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (status == FALSE)
printf("\n\n Error! in Setting CommMask");
else
printf("\n\n Setting CommMask successful");
}
void readFromPort(HANDLE * handle)
{
BOOL status;
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temporary Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(TRUE)
{
printf("\n\n Waiting for Data Reception");
status = TRUE; //Wait for the character to be received
/*-------------------------- Program will Wait here till a Character is received ------------------------*/
if (status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
}
else //If WaitCommEvent()==True Read the RXed data using ReadFile();
{
printf("\n\n Characters Received\n");
do
{
status = ReadFile(*handle, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
SerialBuffer[i] = TempChar;
i++;
}
while (NoBytesRead > 0);
/*------------Printing the RXed String to Console----------------------*/
printf("\n\n ");
int j =0;
for (j = 0; j < i-1; j++) // j < i-1 to remove the dupliated last character
{
printf("%02X", (unsigned int)(unsigned char)SerialBuffer[j]);
}
i=0;
}
//CloseHandle(*handle);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}
Your code should work fine (EDIT: as long as you intend to use it together with com0com). As the busybee suggested in the comment above I think you are mixing up your ports or misunderstanding how com0com is supposed to work.
You might have two different scenarios:
1)You are using your Windows PC as a sniffer to monitor the Modbus transactions in between two other parties. For instance a PLC and remote Modbus sensor. In this scenario, you need two real hardware serial ports and a couple of virtual ports provided by com0com.
2)If something in your computer is acting as one of the parties in the Modbus transaction then you only need a hardware serial port and a couple of virtual ports.
Since you mention passive I guess you are on scenario number 1. If so you just need to choose your ports correctly. I wrote a complete example on how to do this very same, coincidentally for Modbus too using Termite and com0com, take a look here. You might also want to take a look to SerialPCAP, which in combination with Wireshark can even decode your Modbus messages.
If you prefer to reinvent the wheel, I guess you can just drop com0com and share the port as somebody else suggested in the comments. There are some interesting questions you might want to read if you decide to follow on this road, see here.
EDIT: You say you do want to reinvent the wheel. That's fine but I think you need to consider some things before you jump into writing code. I'm no expert serial port developer; much less on Windows, and even much less on recent Windows versions. But I did some research on this topic way back so I can give you my view:
-Most of us non-wheelreinventors would be more than happy to monitor our serial ports with the virtual serial port techniques explained above (I will repeat myself once more: for Modbus RTU traffic monitoring, look at Wireshark/SerialPCAP and you'll forget about anything else). My first impression was you wanted to do that (that's why you were talking about com0com). Reading your comments, I guess that's not good enough for you (I can understand that, I tend to prefer clean solutions to dirty tricks).
-Now, having that clear, is there something you can do? From userspace, I don't think you can share a serial port nowadays. The trick on the comment to your question that mentions dwShareMode might have worked back in the 90s, but I'm afraid it won't work anymore. For more details see here.
-If you go to driverland, you might have some chances. Read here. Other useful links: 1,2.
My conclusion is: there is no fix for your code, what you want to do is more involved than what you have.
windows 7
I have built a project in C to make a dll, whcih could be used in a big program written by python.
static HANDLE hCom = INVALID_HANDLE_VALUE;
unsigned int g_dldtool_exit_code = 0;//can not be written by other threads.
hCom = CreateFile( commPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // overlapped I/O
NULL // hTemplate must be NULL for comm devices
);
if (hCom == INVALID_HANDLE_VALUE) {
// Handle the error.
TRACE ("CreateFile failed with error %d", GetLastError());
g_dldtool_exit_code = 1;
goto _exit;
}
if (hCom != INVALID_HANDLE_VALUE) {
unconfig_serial_port();
fSuccess = CloseHandle(hCom);
I have used
if (hCom != INVALID_HANDLE_VALUE) {
unconfig_serial_port();
fSuccess = CloseHandle(hCom);
to close the serial port, but the if condition can not be meet every time program goes here, so the serial port can not be closed.
When I annotate the if condition, error occurs: ERROR showed when building dll
How could I close the
serial port?
The full code could be download here:
https://www.dropbox.com/s/kk2sc3r6pmmh7tt/download_main.c?dl=0
The error you are reporting seems to be due to a problem in the syntax.
Maybe when you comment out the "if" statement you remove a open bracket "{" but do not remove the close bracket "}".
Concerning why the code does not meet the if statement condition, it is difficult to say from those pieces of code.
im trying to communicate with a device via a virtual comport (comport to usb adapter,PL2303) on Win10. The device is an Eltek RC250 datalogger.
I have already installed an older PL2303 driver. The devicemanager recognized the device without any errors. Sending and receiving data between the device and the official software is working properly.
My problem is that after ReadFile is executed the program is doing nothing. I think ReadFile is waiting for more input from the device and therefore stucked in this function.
Trying it on a win7 System gets to the same issue.
The message which i write to the device is a valid message.
The following code shows the communication.
hComm = CreateFile("COM3", //port name
GENERIC_READ | GENERIC_WRITE, //Read/Write
0, // No Sharing
NULL, // No Security
OPEN_EXISTING,// Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices /* establish connection to serial port */
if (hComm == INVALID_HANDLE_VALUE)
printf("Error in opening serial port");
else
printf("opening serial port successfully");
nNumberOfBytesToWrite = sizeof(message);
resW = WriteFile(
hComm,
message,
nNumberOfBytesToWrite,
&lpNumberOfBytesWritten,
NULL);
do
{
printf("\nread");
resR = ReadFile(
hComm,
&answer,
sizeof(lpNumberOfBytesRead),
&lpNumberOfBytesRead,
NULL);
SerialBuffer[i] = answer;
i++;
}
while (lpNumberOfBytesRead > 0);
return 0;
Please help me, i have no clue what the problem might be.
Thomas
In the ReadFile() call, third parameter should be sizeof(answer) (or possibly just 1 since it appears to be a single byte), but certainly not sizeof(lpNumberOfBytesRead). It is blocking waiting for 4 bytes (size of a DWORD) when presumably answer is a single byte?
Also if you have not explicitly set a Comm timeout, you have no idea how long ReadFile() will wait before returning 0 to exit the loop. If the timeout is indefinite then it will never exist the loop.
There are other potential issues in this call, but without seeing how the parameters are declared, it is not possible to say.
I am using 3.12 kernel on an ARM based linux board (imx233 CPU). My purpose is to detect pin change of a GPIO (1 to 0).
I can read the pin value constantly calling the below function (in a while(1) loop)
int GPIO_read_value(int pin){
int gpio_value = 0;
char path[35] = {'\0'};
FILE *fp;
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
if ((fp = fopen(path,"rb+")) == NULL){ //echo in > direction
//error
}
fscanf(fp, "%d", &gpio_value);
fclose(fp);
return gpio_value;
}
But it causes too much load to the CPU. I don't use usleep or nanosleep, because the pin change happens for a very short of a time that would cause me to miss the event.
As far as I find out, it is not possible to use poll(). Is there any poll() like function that I can use to detect a pin change of a GPIO?
EDIT: Just in case, if I am doing something wrong, here is my poll() usage that does not detect the pin change
struct pollfd pollfds;
int fd;
int nread, result;
pollfds.fd = open("/sys/class/gpio/gpio51/value", O_RDWR);
int timeout = 20000; /* Timeout in msec. */
char buffer[128];
if( pollfds.fd < 0 ){
printf(" failed to open gpio \n");
exit (1);
}
pollfds.events = POLLIN;
printf("fd opens..\n");
while (1)
{
result = poll (&pollfds, 0, timeout);
switch (result)
{
case 0:
printf ("timeout\n");
break;
case -1:
printf ("poll error \n");
exit (1);
default:
printf("something is happening..\n");
if (pollfds.revents & POLLIN)
{
nread = read (pollfds.fd, buffer, 8);
if (nread == 0) {
printf ("result:%d\n", nread);
exit (0);
} else {
buffer[nread] = 0;
printf ("read %d from gpio: %s", nread, buffer);
}
}
}
}
close(fd);
EDIT2: the code on https://developer.ridgerun.com/wiki/index.php/Gpio-int-test.c works fine with poll() I needed to define the rising/falling edge for the interrupt and a little bit fix on the definition. It solves my problem, however, it might be good for me and some other people to hear/know the alternative methods.
I have never seen this board before, however I guess PIC is fully implemented for this board (usually is like that) but you have to configure interrupt additionally in GPIO controller (usually is like that).
Some part should be done as a kernel module, then you have to pass information about interrupt to you application.
Example way to do this is to implement following thing as a kernel module:
setup GPIO controller to enable interrupt on particular port and level
(how to do this you can find here: http://cache.freescale.com/files/dsp/doc/ref_manual/IMX23RM.pdf 37.2.3.3 Input Interrupt Operation)
enable GPIO interrupt in PIC (how to do this: http://lwn.net/images/pdf/LDD3/ch10.pdf Chapter10)
implement interrupt handling routine (I will describe a little bit below)
implement ioctl interfaces for your module.
and a rest in your application:
a function that can coosomeoneperate with interrupt.
Simplest way of passing information about interrupt from kernel to app is by semaphore on kernel side.
in module you can implement an ioctl that will sleep until interrupt happen.
So application will call this ioctl and its thread will be blocked until interrupt happen.
Inside module, interrupt routine should check if application thread is now blocked, and if so up() semaphore.
EDIT*****
This CPU has SSP that has working mode for SPI. Why dont use it ??
I'm have a Linux application that is supposed to read from serial device /dev/ttyS0. The serial device is opened in the following manner:
// Open the serial port
if((serial_device = open("/dev/ttyS0", O_RDWR | O_NOCTTY)) < 0){
fprintf(stderr, "ERROR: Open\n");
exit(EXIT_FAILURE);
}
// Get serial device attributes
if(tcgetattr(serial_device,&options)){
fprintf(stderr, "ERROR: Terminal Get Attributes\n");
exit(EXIT_FAILURE);
}
cfsetspeed(&options,speed); // Set I/O baud rates
cfmakeraw(&options); // Set options to transceive raw data
options.c_cflag |= (CLOCAL | CREAD); // Enable the receiver and set local mode
options.c_cflag &= ~CSTOPB; // 1 stop bit
options.c_cflag &= ~CRTSCTS; // Disable hardware flow control
options.c_cc[VMIN] = 1; // Minimum number of characters to read
options.c_cc[VTIME] = 10; // One second timeout
// Set the new serial device attributes
if(tcsetattr(serial_device, TCSANOW, &options)){
fprintf(stderr, "ERROR: Terminal Set Attributes\n");
exit(EXIT_FAILURE);
}
I then use the select function to try and read from the serial device:
// Flush I/O Bffer
if(tcflush(serial_device,TCIOFLUSH)){
fprintf(stderr, "ERROR: I/O Flush\n");
exit(EXIT_FAILURE);
}
// Write message to board
if(write(serial_device,msg, strlen(msg)) != (int)strlen(msg)){
fprintf(stderr, "ERROR: Write\n");
exit(EXIT_FAILURE);
}
switch(select(serial_device+1, &set, NULL, NULL, &timeout)){
// Error
case -1:
fprintf(stderr, "ERROR: Select\n");
exit(EXIT_FAILURE);
// Timeout
case 0:
success = false;
break;
// Input ready
default:
// Try to read a character
switch(read(serial_device, &c, 1)){
// Error (miss)
case -1:
success = false;
break;
// Got a character
default:
msg[i++] = c;
break;
}
break;
}
// Set 200ms timeout
this->timeout.tv_sec = 0;
this->timeout.tv_usec = 200000;
}
I've tried reopening the port by determining if the read was not successful:
if(!success)
close(serial_device);
openPort(); // Same as above
}
However, the act of physically unplugging the serial connector will result in the application being unable to read anything further, and select will do nothing but time out. Plugging the connector back in while the application is running will not fix the issue, and select will continue to detect nothing.
The only way to successfully read from the serial port again is to restart the application. I'm wondering why this is, and how I can recover from the serial connector being unplugged at runtime.
The use of select() with just one file descriptor is unusual. It also adds a level of complexity.
Since the serial port is configured for non-canonical input, with proper selection of VMIN and VTIME, you might be able to accomplish the read of a char at a time with simpler code. E.G. try VMIN = 1 and VTIME = 10*timeout.tv_sec
However as you figured out, and if you are willing to handle (or want) a timeout rather than wait for at least one character to arrive, then VMIN = 0 will emulate your original code with the select().
VMIN = 0 and VTIME > 0
This is a pure timed read. If data are available in the input queue, it's transferred to the caller's buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more is available in the input queue, it's returned to the caller. Note that this is an overall timer, not an intercharacter one.
However (like the OP) I'm baffled as to why reconnecting the port connector should disrupt any reads or select monitoring, and have never encountered such an issue.