I have a microcontroller which I communicate with my windows pc, through FT232RL.
On the computer side, I am making a C-library to send and receive data, using windows API.
I have managed to:
Receive data (or multiple receives),
Transmit data (or multiple transmits),
First transmit (or multiple transmits) and then receive data (or multiple receives)
But I have not managed to:
Transmit Data and then receive.
If I receive anything, and then try to transmit, I get error. So, I guess when I receive data, there is a change in configuration of the HANDLE hComm, which I cannot find.
So the question is, what changes to my HANDLE hComm configuration when I receive data, which does not allow me to transmit after that?
Here is my code/functions and the main() that give me the error. If I run this, I get this "error 6" -screenshot of the error down below-:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
//Create Handler
HANDLE hComm = comPortSetup("\\\\.\\COM5");//change this to the com port of your mcu
//Setup the Receiver
rx_setup(hComm);
sleep(1);
char SerialBuffer[256];
//Receive data
rx_receive(hComm, &SerialBuffer);//<---- Works fine
char firstData[125] = ".";
tx_transmit(hComm, &firstData);//<----Wont work, since I received data first.
CloseHandle(hComm);//Closing the Serial Port
_getch();//press any key to close the window
}
HANDLE comPortSetup:
HANDLE comPortSetup(char ComPrt[])
{
int CharLoop=0;
HANDLE HandleCom; // Handle to the Serial port
/*----------------------------------- Opening the Serial Port --------------------------------------------*/
/*
There might be a case where one would need to use CreateFileA instead. (Depending on the compiler)
More can be found here: https://stackoverflow.com/questions/51462048/what-is-the-difference-between-createfile-and-createfilea
*/
HandleCom = CreateFile( ComPrt, // Name of the Port to be Opened
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O (0 does not match any of the flags of dwFlagsAndAttributes.
// This means we are setting no flags or attributes (We dont care about it) https://stackoverflow.com/questions/17997608/what-does-dwflagsandattributes-0-mean-in-the-createfile-method
NULL); // Null for Comm Devices
if (HandleCom == INVALID_HANDLE_VALUE)
printf("\n Error! - Port %s can't be opened", ComPrt);
else
printf("\n Port %s Opened\n ", ComPrt);
/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
DCB dcbSerialParams = { 0 };
// Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
BOOL StatusFun;
StatusFun = GetCommState(HandleCom, &dcbSerialParams); //retreives the current settings
if (StatusFun == 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
StatusFun = SetCommState(HandleCom, &dcbSerialParams); //Configuring the port according to settings in DCB
if (StatusFun == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else
{
printf("\n Setting DCB Structure Successfull\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 };
//miliseconds (ms) intervals
//interval between the arrival of any two bytes. Terminates the ReadFile
timeouts.ReadIntervalTimeout = 100; //Default =50
//Total = (TimeoutMultiplier*BytesToRead + TimeoutConstant)
timeouts.ReadTotalTimeoutConstant = 10; //Default = 50
timeouts.ReadTotalTimeoutMultiplier = 20; //Default = 10
//Total = (TimeoutMultiplier*BytesToRead + TimeoutConstant)
timeouts.WriteTotalTimeoutConstant = 10; //Default = 50
timeouts.WriteTotalTimeoutMultiplier = 20; //Default = 10
if (SetCommTimeouts(HandleCom, &timeouts) == FALSE)
printf("\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successfull");
return HandleCom;
}
rx_setup:
BOOL rx_setup(HANDLE HandleCom)
{
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
//https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask
BOOL Status;
Status = SetCommMask(HandleCom, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (Status == FALSE)
{
printf("\n\n Error! in Setting CommMask");
}
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
printf("\n\n Waiting for Data Reception...");
DWORD dwEventMask; // Event mask to trigger. 32-bit unsigned integer (range: 0 through 4294967295 decimal)
//Wait for the first character to be received
Status = WaitCommEvent(HandleCom, &dwEventMask, NULL); //dwEventMask Should be 1 for "A character was received and placed in the input buffer." AKA EV_RXCHAR
/*-------------------------- Program will Wait here till one Character is received ------------------------*/
if (Status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
exit(-1);
}
return Status;
}
rx_receive:
void rx_receive(HANDLE HandleCom, char SerialBufferFun[])
{
char TempChar; // Temperory Character
BOOL Status;
/*Receiver start*/
DWORD NoBytesRead; // Bytes read by ReadFile()
int loopArrayFun = 0;
do
{
Status = ReadFile(HandleCom,
&TempChar,
sizeof(TempChar), //No of bytes to be read
&NoBytesRead, //How many bytes were actually read
NULL);
SerialBufferFun[loopArrayFun] = TempChar;
printf("%c",SerialBufferFun[loopArrayFun]);
loopArrayFun = loopArrayFun+1;
}while (NoBytesRead > 0);//NoBytesRead = 0 when bytes are finished reading.
SerialBufferFun[loopArrayFun-3] = '\0'; //WHY -3
}
transmit:
void tx_transmit(HANDLE HandleCom, char DataToTransmit[])
{
BOOL Status;
/*----------------------------- Writing a Character to Serial Port----------------------------------------*/
//DataToTransmit should be char or byte array, otherwise write will fail
DWORD dNoOFBytestoWrite; // No of bytes to write into the port
DWORD dNoOfBytesWritten = 0; // No of bytes written to the port
dNoOFBytestoWrite = sizeof(DataToTransmit); // Calculating the no of bytes to write into the port
if (HandleCom == INVALID_HANDLE_VALUE)
{
printf("\n Invalid handle");
}
Status = WriteFile(HandleCom, // Handle to the Serialport
DataToTransmit, // Data to be written to the port
(dNoOFBytestoWrite), // No of bytes to write into the port
&dNoOfBytesWritten, // No of bytes written to the port
NULL);
if (Status != TRUE)
printf("\n\n Error %d in Writing to Serial Port",GetLastError());
}
The error I get after running the above code (Error 6. The 'Heater Driver' etc are from the MCU):
What I tried:
I noticed that the only thing that changes that could influence my transmission when receiving, is not inside rx_receive but inside rx_setup:
SetCommMask(HandleCom, EV_RXCHAR);
and:
WaitCommEvent(HandleCom, &dwEventMask, NULL);
So I tried to do SetCommMask(HandleCom, 0x00); after the reception but It did not work, I got the same error. I do not know if I need to disable the WaitCommEvent(HandleCom, &dwEventMask, NULL); though, but since it stops running when the reception is done, So it just runs inside the function and does not affect my HANDLE hComm
According to MSDN:Sample, Maybe you need to set a pin's signal state to indicate the data has been sent/received in your microcontroller program. More details reside in your serial communication transmission of data standard. And you should write code according to the result of WaitCommEvent(hCom, &dwEvtMask, &o); like the linked sample.
Found a solution! Solution:
I did not need to use the rx_setup function in the main at all!.
I am unsure of why that is, but I can receive/send data now in any sequence I want.
So my main will be:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
//Create Handler
HANDLE hComm = comPortSetup("\\\\.\\COM5");//change this to the com port of your mcu
//Setup the Receiver
//rx_setup(hComm);
sleep(1);
char SerialBuffer[256];
//Receive data
rx_receive(hComm, &SerialBuffer);//<---- Works fine
char firstData[125] = ".";
tx_transmit(hComm, &firstData);//<----Works as expected now
CloseHandle(hComm);//Closing the Serial Port
_getch();//press any key to close the window
}
Related
I am using the code found here to receive data from an ATmega microcontroller to my PC. The code "USB2SERIAL_Read_W32.c" handles the Windows COM ports and receives serial data, which it then prints to the console.
I tried to modify the code to run continuously, i.e. to wait for data, print them when it receives characters, and then return to waiting for new data.
Here you can see the code, with my interventions.
#include <Windows.h>
#include <stdio.h>
void main(void)
{
HANDLE hComm; // Handle to the Serial port
char ComPortName[] = "\\\\.\\COM9"; // Name of the Serial port(May Change) to be opened,
BOOL Status; // Status of the various operations
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temperory Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
int counter=0;
printf("\n\n +==========================================+");
printf("\n | Serial Port Reception (Win32 API) |");
printf("\n +==========================================+\n");
/*---------------------------------- Opening the Serial Port -------------------------------------------*/
hComm = CreateFile( ComPortName, // Name of the Port to be Opened
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (hComm == INVALID_HANDLE_VALUE)
printf("\n Error! - Port %s can't be opened\n", ComPortName);
else
printf("\n Port %s Opened\n ", ComPortName);
/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
Status = GetCommState(hComm, &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(hComm, &dcbSerialParams); //Configuring the port according to settings in DCB
if (Status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else //If Successfull display the contents of the DCB Structure
{
printf("\n\n Setting DCB Structure Successfull\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 = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
if (SetCommTimeouts(hComm, &timeouts) == FALSE)
printf("\n\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successfull");
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
dwEventMask=EV_RXCHAR;
Status = SetCommMask(hComm, dwEventMask); //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 successfull");
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(1){
int j =0;
i=0;
for(j=0;j<sizeof(SerialBuffer);j++){ //Clear SerialBuffer
SerialBuffer[j]=0;
}
printf("\n\n Waiting for Data Reception");
Status = WaitCommEvent(hComm, &dwEventMask, NULL); //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");
do{
Status = ReadFile(hComm, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
if(Status==TRUE){
if(NoBytesRead>0){
SerialBuffer[i] = TempChar;
}
i++;
}
else
printf("\n Error! in Setting ReadFile()");
}while (NoBytesRead);
/*------------Printing the RXed String to Console----------------------*/
printf("\n\n ");
for (j = 0; j < strlen(SerialBuffer); j++)
printf("%c", SerialBuffer[j]);
}
//CloseHandle(hComm);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}//End of Main()
The changes I did were to add the while(1) loop, clear the buffer at the start of each loop, commented out the closing of the port handler, changed the timeouts as mentioned in the "Timeouts" section in this microsoft tutorial and few more minor tweaks and cosmetics.
The problem is that for some reason, whenever I receive the string "1234", firstly the expected event is triggered, printing the received characters, and then another event is triggered for some reason, with no characters waiting, as you can see in the following picture:
To sum up, why is the unexpected event triggered and how can I solve it?
EDIT: I haven't managed to find the solution yet, however I am posting a workaround that I found (perhaps it can help someone to spot the initial problem). If I set the Comm Mask to zero, and reset it to EV_RXCHAR in each loop iteration, it seems to work as expected; That is, when I reach the WaitCommEvent line for the second time, it is not automatically triggered as before. The bad thing is that I cannot explain why it works now, and I believe that it should be working correctly in the first place as well. So any help would be greatly appreciated. Here is the code with the workaround, which sets the CommMask to zero and back to EV_RXCHAR:
#include <Windows.h>
#include <stdio.h>
void main(void){
printf("Hello!\n");
DWORD dwCommEvent;
DWORD dwRead;
DWORD lpEvtMask;
char chRead;
char ComPortName[] = "\\\\.\\COM9"; // Name of the Serial port(May Change) to be opened,
int i=0;
HANDLE hComm;
hComm = CreateFile( ComPortName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (hComm == INVALID_HANDLE_VALUE)
printf("Error opening port.\n");
//////////////////////////////////////////////////////
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
if (!GetCommState(hComm, &dcb)) // get current DCB
printf("Error GetCommState.\n");
// Update DCB rate.
dcb.BaudRate = CBR_9600 ;
dcb.ByteSize = 8; // Setting ByteSize = 8
dcb.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcb.Parity = NOPARITY; // Setting Parity = None
dcb.DCBlength = sizeof(dcb);
// Set new state.
if (!SetCommState(hComm, &dcb))
printf("Error SetCommState.\n");
// Error in SetCommState. Possibly a problem with the communications
// port handle or a problem with the DCB structure itself.
/////////////////////////////////////////////////////////////////////
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hComm, &timeouts))
printf("Error timeouts.\n");
if(!PurgeComm(hComm,PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT))
printf("Error PurgeComm.\n");
////////////////////////////////////////
for ( ; ; ) {
if (!SetCommMask(hComm, 0))
printf("Error CommMask.\n");
if (!SetCommMask(hComm, EV_RXCHAR))
printf("Error CommMask.\n");
printf("Waiting for characters.. \n\n");
if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {
do {
if (ReadFile(hComm, &chRead, 1, &dwRead, NULL)){
if(dwRead!=0)
printf("Character Received: %c\n",chRead);
}
else{
printf("ErrorReadFile.\n");
break;
}
}while (dwRead);
}
else{
printf("Error WaitCommEvent.\n");
break;
}
printf("=========================\n");
}
}
I am a noob in serial programming and I am trying to build a com port sniffer to sniff bytes between two ports by using pthreads. I am using com0com emulator for the com ports. The emulator creates COM ports in connected pairs. At first, I wanted to sniff using this scenario:
Application1<--->COM3<--->Sniffer<--->COM4<--->Application2
However, I am unable to read what goes on between the two ports even when I set the ports in the code to non-exclusive mode before the running the application, also, the applications are unable to access the ports (error is "Access Denied").
I tried to this scenario:
Application1<--->COM3<--->COM4<--->Sniffer<--->COM5<--->COM4<--->Application2
With this, code is able to read COM4 and COM5 while the applications take COM3 and COM4. In this scenario, I am able to see the bytes and write in one direction with one thread. But when I try to use two threads to ensure bi-directional communication, it fails at the first read.
Code for the second scenario:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <pthread.h>
void setupPort(HANDLE * handle, char * portName);
void * readFromPort(void * handles);
void writeToPort(HANDLE * handle, char data[]);
typedef struct
{
HANDLE from;
HANDLE to;
} bi_ports;
int main()
{
HANDLE first_port, second_port;
char * first_port_name = "COM4";
char * second_port_name = "COM5";
bi_ports *ports_first, *ports_second;
pthread_t firstToSecondThread, secondToFirstThread;
setupPort(&first_port, first_port_name);
setupPort(&second_port, second_port_name);
ports_first = (bi_ports *) malloc(sizeof(bi_ports));
ports_second = (bi_ports *) malloc(sizeof(bi_ports));
ports_first->from = first_port;
ports_first->to = second_port;
ports_second->from = second_port;
ports_second->to = first_port;
pthread_create(&firstToSecondThread, NULL, readFromPort, (void *) ports_first);
pthread_create(&secondToFirstThread, NULL, readFromPort, (void *) ports_second);
pthread_join(firstToSecondThread, NULL);
pthread_join(secondToFirstThread,NULL);
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(void * handles)
{
bi_ports* ports;
ports = (bi_ports*) handles;
HANDLE handleFrom;
HANDLE handleTo;
handleFrom = ports->from;
handleTo = ports->to;
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;
DWORD dNoOFBytestoWrite;
DWORD dNoOfBytesWritten = 0; // No of bytes written to the port
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
while(TRUE)
{
printf("\n\n Waiting for Data Reception");
status = TRUE;//WaitCommEvent(handleFrom, &dwEventMask, NULL); //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(handleFrom, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
SerialBuffer[i] = TempChar;
i++;
}
while (NoBytesRead > 0);
/*------------Printing the RXed String to Console----------------------*/
if(NoBytesRead > 0){
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]);
}
dNoOFBytestoWrite = sizeof(SerialBuffer);
status = WriteFile(handleTo, // Handle to the Serialport
SerialBuffer, // Data to be written to the port
dNoOFBytestoWrite, // No of bytes to write into the port
&dNoOfBytesWritten, // No of bytes written to the port
NULL);
if (status == TRUE)
{
printf("\n\n %X - Written to port", (unsigned int)(unsigned char)SerialBuffer);
}
else
{
printf("\n\n Error %d in Writing to Serial Port",GetLastError());
}
//CloseHandle(handleTo);
i=0;
}
}
//CloseHandle(handleFrom);//Closing the Serial Port
printf("\n +==========================================+\n");
}
}
I am trying to establish a serial connection with a device via RS232 and the C code. The purpose is to connect arduino to computer later and show the ip adress retrieved from the device on the LED screen.
Usually I connect the device to the computer via RS232, open PuTTY and establish the serial connection at 115200 baud rate. Then I press enter, type login, press enter, type password, press enter, type 'ip show' and then retrieve the ip adress.
The problem is I am not good at C programming (studied it only for 1 year in University). The code I come up with (copy-pasted and edited) is attached below. The questions are:
1) How do I get the information printed on the terminal screen. For example, after I type login and then press enter, there is a sentence saying type your password. How do I retrieve that to IDE's console?
2) On the final step (retrieving the ip), how do I retrieve the ip? It is in text format, after it's shown I need to copy it and paste it into another document).
As for now, my limited amount of knowledge about C prohibits me to go further.
Any kind of help (even the name of the helpful function) is appreciated!
//
// serial.c / serial.cpp
// A simple serial port writing example
// Written by Ted Burke - last updated 13-2-2013
//
// To compile with MinGW:
//
// gcc -o serial.exe serial.c
//
// To compile with cl, the Microsoft compiler:
//
// cl serial.cpp
//
// To run:
//
// serial.exe
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Define the five bytes to send ("hello")
char bytes_to_send[15];
bytes_to_send[0] = '\n';
bytes_to_send[1] = 'a';
bytes_to_send[2] = 'd';
bytes_to_send[3] = 'm';
bytes_to_send[4] = 'i';
bytes_to_send[5] = 'n';
bytes_to_send[6] = '\n';
bytes_to_send[7] = 's';
bytes_to_send[8] = 'h';
bytes_to_send[9] = 'o';
bytes_to_send[10] = 'w';
bytes_to_send[11] = ' ';
bytes_to_send[12] = 'i';
bytes_to_send[13] = 'p';
bytes_to_send[14] = '\n';
// Declare variables and structures
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
// Open the highest available serial port number
fprintf(stderr, "Opening serial port...");
hSerial = CreateFile(
"\\\\.\\COM6", GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hSerial == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "Error\n");
return 1;
}
else fprintf(stderr, "OK\n");
// Set device parameters (38400 baud, 1 start bit,
// 1 stop bit, no parity)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error getting device state\n");
CloseHandle(hSerial);
return 1;
}
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if(SetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error setting device parameters\n");
CloseHandle(hSerial);
return 1;
}
// Set COM port timeout settings
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(SetCommTimeouts(hSerial, &timeouts) == 0)
{
fprintf(stderr, "Error setting timeouts\n");
CloseHandle(hSerial);
return 1;
}
// Send specified text (remaining command line arguments)
DWORD bytes_written, total_bytes_written = 0;
fprintf(stderr, "Sending bytes...");
if(!WriteFile(hSerial, bytes_to_send, sizeof(bytes_to_send), &bytes_written, NULL))
{
fprintf(stderr, "Error\n");
CloseHandle(hSerial);
return 1;
}
fprintf(stderr, "%d bytes written\n", bytes_written);
// Close serial port
fprintf(stderr, "Closing serial port...");
if (CloseHandle(hSerial) == 0)
{
fprintf(stderr, "Error\n");
return 1;
}
fprintf(stderr, "OK\n");
fprintf(stderr, "the sent sentence is: ");
for(int i=0;i<sizeof(bytes_to_send);i++){
fprintf(stderr,"%c",bytes_to_send[i]);
}
// exit normally
return 0;
}
For setting up the port and read/write commands you can use the code from this answer.
Then you only have to worry about what you send and what you receive.
Putty actually reads the serial port in the background and then prints it on the console so you can see it. You will have to do the same thing in your app. After you read the data, you have to print it.
For example, for login, as you described it:
write (fd, "login\r\n", 7); // send 7 character login command
// (similar to writing login + enter in Putty)
usleep ((7 + 25) * 100); // allow time for sending & receiving
// (sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit)
char buf [100];
int n = read (fd, buf, sizeof(buf)); // read rx characters (Putty does this automatically)
// expect to have "the sentence saying type your password" in this buffer
// 100 characters are read, adjust it if you expect a longer answer
printf("Rx:"); // display the rx data (Putty does this automatically)
for (int i = 0; i < sizeof(buf); i++)
printf("%c",buf[i]);
printf("\n");
// continue with sending the password + reading the response
// then sending the ip command + reading the answer
I'm trying to communicate with a board a colleague made via a USB To serial cable.
It works fine when I use teraterm or putty, but I can't get it to work, when I do it with a sample code.
Since I have no idea how to do this - or at least a couple of years ago I did it last - I'm using a piece of code from github: Seerial Programming with Win32API
I know that if I transmit the command: SB50G, an LED should light up. But it doesn't. So I tried to see, if it sends an error back (It should send an 'E' char in case of any error). Though nothing is received. I then tried to transmit the command 'G', which should return a an ASCII string of 8 hex'es.
I don't get any errors upon opening the port, unless I use it in teraterm at the same time.
The code is a merge of the transmitter and receive example:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
void main(void)
{
HANDLE hComm; // Handle to the Serial port
char ComPortName[] = "\\\\.\\COM3"; // Name of the Serial port(May Change) to be opened,
BOOL Status; // Status of the various operations
DWORD dwEventMask; // Event mask to trigger
char TempChar; // Temperory Character
char SerialBuffer[256]; // Buffer Containing Rxed Data
DWORD NoBytesRead; // Bytes read by ReadFile()
int i = 0;
printf("\n\n +==========================================+");
printf("\n | Serial Transmission (Win32 API) |");
printf("\n +==========================================+\n");
/*----------------------------------- Opening the Serial Port --------------------------------------------*/
hComm = CreateFile(ComPortName, // Name of the Port to be Opened
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (hComm == INVALID_HANDLE_VALUE)
printf("\n Error! - Port %s can't be opened", ComPortName);
else
printf("\n Port %s Opened\n ", ComPortName);
/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
Status = GetCommState(hComm, &dcbSerialParams); //retreives the current settings
if (Status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_19200; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
Status = SetCommState(hComm, &dcbSerialParams); //Configuring the port according to settings in DCB
if (Status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else
{
printf("\n Setting DCB Structure Successfull\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);
}
Status = SetCommMask(hComm, 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 successfull");
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(hComm, &timeouts) == FALSE)
printf("\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successfull");
/*----------------------------- Writing a Character to Serial Port----------------------------------------*/
char lpBuffer[] = "G\r"; // lpBuffer should be char or byte array, otherwise write wil fail
DWORD dNoOFBytestoWrite; // No of bytes to write into the port
DWORD dNoOfBytesWritten = 0; // No of bytes written to the port
dNoOFBytestoWrite = sizeof(lpBuffer); // Calculating the no of bytes to write into the port
Status = WriteFile(hComm, // Handle to the Serialport
lpBuffer, // Data to be written to the port
dNoOFBytestoWrite, // No of bytes to write into the port
&dNoOfBytesWritten, // No of bytes written to the port
NULL);
if (Status == TRUE)
printf("\n\n %s - Written to %s", lpBuffer, ComPortName);
else
printf("\n\n Error %d in Writing to Serial Port", GetLastError());
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
printf("\n\n Waiting for Data Reception");
Status = WaitCommEvent(hComm, &dwEventMask, NULL); //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");
do
{
Status = ReadFile(hComm, &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("%c", SerialBuffer[j]);
}
CloseHandle(hComm);//Closing the Serial Port
printf("\n +==========================================+\n");
}
Can someone explain to me why it's not working, or any way I can find out why it's not working? The BAUD-rate and such are as they should be.
Best regards.
The problem seemed to be due to the board. After installing a sniffer, i could see the correct data being sent out, but nothin was returned.
Apparently the carriage return needed to be send separat from the data, and then it worked.
Many thanks for the comments, I'll take them in to serious consideration. :)
I have been attempting to communicate with a device of mine via an RS232 serial port(in my case it is COM6). My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response. The program seems to work relatively fine when I have it write and read to a file in my computer, but not for when I designate COM6. Here is the latest edition of my code:
using namespace std;
const char ASCII[ ]= "0123456789ABCDEF";
char *Checksum (char *buffer)
{
static char Hex[10];
static int a1, a2;
register unsigned int i;
int sum;
printf("%s \n", buffer);
sum = 256;
for ( i=0 ; i<strlen(buffer) ; i++ )
{
sum-=buffer[i];
if ( sum<0 )
sum+= 256;
}
a1 = (sum & 0xF0) >> 4;
a2 = sum & 0x0F;
Hex[0] = ASCII[a1];
Hex[1] = ASCII[a2];
Hex[2] = 0;
printf("the checksum is %s \n",Hex);
return(Hex);
}
int main()
{
char data[80], input[80], *data2;
char *response;
DCB dcb;
bool retVal;
DWORD dwBytesTransferred;
DWORD byteswritten;
printf("the variable response is initially: %d\n", response);
dcb.BaudRate = CBR_19200; //19200 Baud
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //no parity
dcb.StopBits = ONESTOPBIT; //1 stop
//New open port area
HANDLE hPort;
if ((hPort = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
printf("SUCCESS opening the port\n");// success
}
//GetCommState
DCB Dcb;
GetCommState (hPort, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
//Flushing
FlushFileBuffers( hPort );
PurgeComm (hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hPort, &dwErrorFlags, &comStat );
//NEW commtimeouts area
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = 3000; // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
} else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hPort, &CommTimeOuts);
printf("insert ASCII code string you wish to send:");
scanf("%s", input);
strcpy(data, "{0x02}");
strcat(data, input);
printf("%s \n", data);
data2=Checksum(data);
strcat(data, data2);
strcat(data, "{0x03}");
printf("the final sent message will be: %s \n",data);
retVal = WriteFile(hPort,data, strlen(data), &byteswritten, NULL);
printf("Number of bytes written: %d\n", byteswritten);
printf("Write Success? %d\n", retVal);
retVal=ReadFile (hPort, &response, 20, &dwBytesTransferred, NULL);
printf("Read Success? %d\n", retVal);
printf("Port Response: %d\n", response);
free(response);
return 0;
}
Summary of latest discoveries: Using the Free Serial Port Monitor that Habi suggested I now know for sure that WriteFile is functioning correctly and COM6 is in receiving the message. I'm still looking for a crossover cable to double check that the message is being transferred across the line. I figure while I try to figure that out if someone could look at this new edition and tell me if there is anything wrong, particularly in relation to the ReadFile function, it would be much appreciated. It bothers me that the Free Serial Port software is only showing the data passed from my computer and not a response from the device at all. =\
Instead of
"COM6"
try
"\\\\.\\COM6"
And I would recommend to use CreateFile(), ReadFile(), WriteFile().
To open the COM port try this:
HANDLE hComDev;
if ((hComDev = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
// success
}
It seems that calling GetCommState() is missing in your code. Try this to configure the COM port:
DCB Dcb;
GetCommState (hComDev, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
And to initially clear the COM port, I would do a reset like this before starting to send and receive bytes:
FlushFileBuffers( hComDev );
PurgeComm (hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hComDev, &dwErrorFlags, &comStat );
You ask for timeouts? To configure time outs try this:
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = .... // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
}
else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hComDev, &CommTimeOuts);
My code is supposed to write a string of ascii values to the device
and then read the response, however I cannot seem to get any response.
Are you sure that the bytes are really sent to the device? If you have the option, take a oscilloscope and monitor the PC's Tx line. Send some bytes and check baudrate and start/stop bits. If you have no hardware to monitor this signal, take a software based serial monitor, e.g. Free Serial Port Monitor. I have no experience with that kind of software tools but they should show you at least that the Windows drivers try to send something via the COM port of your choice.
Greetings
Habi
If you have been trying for a month then I would recommend you look at the windows function CreateFile. I have used this for serial port comms and it works fine. You might also want to look at the DCB struct and GetCommState and SetCommState functions to configure the serial port. WriteFile can be used to write data to the serial port. I cannot post all of my code as it owned by my company, but this should help you get started.
Also ClearCommError may be of help to you to query if any data is on the serial port buffer when you are waiting to read from it.
I encountered a similar problem and found the other answers helpful, but I was not receiving characters.
Ultimately I found the problem was with RTS_CONTROL_DISABLE or DTR_CONTROL_DISABLE.
By having RTS and DTR held low, the program was indicating to the modem/device that the program was not ready to receive data, and the modem was respecting this signal and dutifully not sending.
By changing these to RTS_CONTROL_ENABLE and DTR_CONTROL_ENABLE, the program indicates to the modem that data may be sent, and I began receiving characters. Not all devices respect these signals so it may work in some configurations with DTR and RTS disabled -- the modem may send data anyway.
It's possible, but you need to config com port via cmd mode command,
and there is no way to config timeout value via cmd command.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// use mode command to config port
// mode com2 dtr=off rts=off baud=9600 parity=N data=8 stop=1 to=on
FILE *f = fopen("\\\\.\\COM2", "wb+");
setvbuf(f, NULL, _IONBF, 0);
if(f == NULL) return -1;
while(1)
{
static char p[5];
int cnt = fread(&p, 1, sizeof(p), f);
printf("read[%d]:%s\n", cnt, p);
if(p[0] == '0') break;
memset(p, 0, sizeof(p));
}
fclose(f);
return 0;
}