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.
Related
currently I try to sent 720 bytes from Windows application to custom STM32 device (now for testing purposes I use Blue Pill - STM32F103xxx). Ah, I forgot to point that I am totally newbie into programming :). So on device side I have 1000 bytes buffers for receiving and sending (Thanks to STMCube for this). Testing device with terminal program ( packets < than 64 bytes) works. Then I rework one of Microsoft examples to be able to sent more data to device. Used device driver on Windows is "usbser.sys". In short my console program do following:
Calculate SINE weave (360) samples - 16 bytes size
Sent them to USB Device as 720 bytes (byte size protocol for COM port)
My problem is that no more than 64 bytes comes into device.
Somewhere I read that reason for this can be into built in Rx,Tx Windows buffers (64 bytes long by mention somewhere on internet) and for this into code below I insert:
SetupComm(hCom,1000,1000)
in hope that this will solve my troubles but nope. Below is "my" code, any ideas how I can fix this?
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
void PrintCommState(DCB dcb)
{
// Print some of the DCB structure values
_tprintf(TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
dcb.BaudRate,
dcb.ByteSize,
dcb.Parity,
dcb.StopBits);
}
int _tmain(int argc, TCHAR* argv[])
{
DCB dcb;
HANDLE hCom;
BOOL fSuccess;
const TCHAR* pcCommPort = TEXT("COM3"); // Most systems have a COM1 port
unsigned __int8 aOutputBuffer[720];// Data that will sent to device
unsigned __int16 aCalculatedWave[360];// Data that will sent to device
int iCnt; // temp counter to use everywhere
for (iCnt = 0; iCnt < 360; iCnt = iCnt + 1)
{
aCalculatedWave[iCnt] = (unsigned short)(0xFFFF * sin(iCnt * PI / 180));
if (iCnt > 180) aCalculatedWave[iCnt] = 0 - aCalculatedWave[iCnt];
}
// 16 bit aCalculatedWaveto to 8 bit aOutputBuffer
for (int i = 0, j = 0; i < 720; i += 2, ++j)
{
aOutputBuffer[i] = aCalculatedWave[j] >> 8; // Hi byte
aOutputBuffer[i + 1] = aCalculatedWave[j] & 0xFF; // Lo byte
}
// Open a handle to the specified com port.
hCom = CreateFile(pcCommPort,
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // not overlapped I/O
NULL); // hTemplate must be NULL for comm devices
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return (1);
}
if (SetupComm(hCom,1000,1000) !=0)
printf("Windows In/Out serial buffers changed to 1000 bytes\n");
else
printf("Buffers not changed with error %d.\n", GetLastError());
// Initialize the DCB structure.
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
// Build on the current configuration by first retrieving all current
// settings.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
// Fill in some DCB values and set the com state:
// 57,600 bps, 8 data bits, no parity, and 1 stop bit.
dcb.BaudRate = CBR_9600; // baud rate
dcb.ByteSize = 8; // data size, xmit and rcv
dcb.Parity = NOPARITY; // parity bit
dcb.StopBits = ONESTOPBIT; // stop bit
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("SetCommState failed with error %d.\n", GetLastError());
return (3);
}
// Get the comm config again.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess)
{
// Handle the error.
printf("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
PrintCommState(dcb); // Output to console
_tprintf(TEXT("Serial port %s successfully reconfigured.\n"), pcCommPort);
if (WriteFile(hCom, aOutputBuffer, 720, NULL, 0) != 0)
_tprintf(TEXT("720 bytes successfully writed to Serial port %s \n"), pcCommPort);
else
_tprintf(TEXT("Fail on write 720 bytes to Serial port %s \n"), pcCommPort);
return (0);
}
USB bulk endpoints implement a stream-based protocol, i.e. an endless stream of bytes. This is in contrast to a message-based protocol. So USB bulk endpoints have no concept of messages, message start or end. This also applies to USB CDC as it is based on bulk endpoints.
At the lower USB level, the stream of bytes is split into packets of at most 64 bytes. As per USB full-speed standard, packets cannot be larger than 64 bytes.
If the host sends small chunks of data that are more than 1ms apart, they will be sent and received in separate packets and it looks as if USB is a message-based protocol. However, for chunks of more than 64 bytes, they are split into smaller packets. And if small chunks are sent with less than 1ms in-between, the host will merge them into bigger packets.
Your design seems to require that data is grouped, e.g. the group of 720 bytes mentioned in the question. If this is a requirement, the grouping must be implemented, e.g. by first sending the size of the group and then the data.
Since larger groups are split into chunks of 64 bytes and the receive callback is called for every packet, the packets must be joined until the full group is available.
Also note a few problems in your current code (see usbd_cdc_if.c, line 264):
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
NewDataFromUsb = *Len;
USBD_CDC_SetRxBuffer sets the buffer for the next packet to be received. If you always use the same buffer – as in this case – it's not needed. The initial setup is sufficient. However, it could be used to set a new buffer if the current packet does not contain a full group.
Despite its name, USBD_CDC_ReceivePacket does not receive a packet. Instead, it gives the OK to receive the next package. It should only be called if the data in the buffer has been processed and the buffer is ready to receive the next packet. Your current implementation runs the risk that the buffer is overwritten before it is processed, in particular if you send a group of more than 64 bytes, which will likely result in a quick succession of packets.
Note that Windows hasn't been mentioned here. The Windows code seems to be okay. And changing to Winusb.sys will just make your life harder but not get you packets bigger than 64 bytes.
I'm programming an ESP32 to accept Bluetooth commands and send Bluetooth Data back to my phone using the Serial profile. For this I'm using the Arduino Espressif Bluetooth Serial Library. Whenever I send something to the ESP32 it processes it and then suddenly closes the Bluetooth Connection.
Up to know I have already tried various delays because I thought that maybe the processor was not keeping up with other stuff due to which it crashed.
However when monitoring using the Serial Connection via USB it still keeps sending status updates.
Other than that I could not really find a solution (also on the Internet).
As I'm pretty much a beginner I did not want to try and build my own Serial Bluetooth Library.
The ESP does not crash when sending it the data. It also keeps processing the data which was sent. I can see that as it sends the chars I sent it via Bluetooth using the Serial interface after having collected them.
The Connection cannot be rebuild after this incident, no matter how long I wait.
My Main function, containing the function call as well as a buffer to write the result to as I thought that maybe I was misusing that.
void loop() {
if (ESP_BT.available() > 0)
{
char *buffer = (char*) malloc(InputSize);
getCurrentMessage(ESP_BT, buffer, InputSize);
Serial.println(buffer);
strncpy(currentMessage, buffer, InputSize);
free(buffer);
}
if (millis() %2000 == 0){
Serial.println("Debug");
delay(1);
}
}
The function which gets called which should read the inputBuffer of the BluetoothSerial into my Buffer.
void getCurrentMessage(BluetoothSerial ESP_BT, char* receivedChars, int InputSize)
{
Serial.println("DEBUG: getCurrentMessageInit");
static byte ndx = 0;
char rc;
while (ESP_BT.available() > 0){
ESP_BT.println("DEBUG: Message Available");
Serial.println("DEBUG: Message Available");
rc = ESP_BT.read();
receivedChars[ndx] = rc;
ndx++;
delay(100);
if (ndx >= InputSize){
while(ESP_BT.available() > 0){
ESP_BT.read();
}
}
}
}
I would expect the Bluetooth Connection to keep working. This it does not do.
I also got the error Code "queue.c:1442 (xQueueGenericReceive)- assert failed!" When not using the delays and the ESP then rebooted.
This it does not do after I included the delays.
The problem was that I did not call my Bluetooth Object by reference.
Instead of giving my function the Bluetooth Object I should point to it:
void getCurrentMessage(BluetoothSerial* ESP_BT, char* receivedChars, int InputSize)
{
Serial.println("DEBUG: getCurrentMessageInit");
static byte ndx = 0;
char rc;
while (ESP_BT->available() > 0){
ESP_BT->println("DEBUG: Message Available");
Serial.println("DEBUG: Message Available");
rc = ESP_BT.read();
receivedChars[ndx] = rc;
ndx++;
delay(100);
if (ndx >= InputSize){
while(ESP_BT->available() > 0){
ESP_BT->read();
}
}
}
}
I seem to be having a bit of trouble in waiting for the completion of serial data transmissions.
My interpretation of the relevant MSDN article is the EV_TXEMPTY event is the correct signal and which indicates that:
EV_TXEMPTY - The last character in the output buffer was sent.
However in my tests the event always fires immediately as soon as the data has been submitted to the buffer and long before the final has actually reached the wire. See the repro code below where the period is always zero.
Have I made an error in the implementation, am I misunderstanding the purpose of the flag, or is this feature simply not supported by modern drivers? In the latter case is there a viable workaround, say some form of synchronous line state request?
For the record the tests were conducted with FTDI USB-RS485 and TTL-232R devices in a Windows 10 system, a USB-SERIAL CH340 interface on a Windows 7 system, as well as the on-board serial interface of a 2005-vintage Windows XP machine. In the FTDI case sniffing the USB bus reveals only bulk out transactions and no obvious interrupt notification of the completion.
#include <stdio.h>
#include <windows.h>
static int fatal(void) {
fprintf(stderr, "Error: I/O error\n");
return 1;
}
int main(int argc, const char *argv[]) {
static const char payload[] = "Hello, World!";
// Use a suitably low bitrate to maximize the delay
enum { BAUDRATE = 300 };
// Ask for the port name on the command line
if(argc != 2) {
fprintf(stderr, "Syntax: %s {COMx}\n", argv[0]);
return 1;
}
char path[MAX_PATH];
snprintf(path, sizeof path, "\\\\.\\%s", argv[1]);
// Open and configure the serial device
HANDLE handle = CreateFileA(path, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if(handle == INVALID_HANDLE_VALUE)
return fatal();
DCB dcb = {
.DCBlength = sizeof dcb,
.BaudRate = BAUDRATE,
.fBinary = TRUE,
.ByteSize = DATABITS_8,
.Parity = NOPARITY,
.StopBits = ONESTOPBIT
};
if(!SetCommState(handle, &dcb))
return fatal();
if(!SetCommMask(handle, EV_TXEMPTY))
return fatal();
// Fire off a write request
DWORD written;
unsigned int elapsed = GetTickCount();
if(!WriteFile(handle, payload, sizeof payload, &written, NULL) ||
written != sizeof payload)
return fatal();
// Wait for transmit completion and measure time elapsed
DWORD event;
if(!WaitCommEvent(handle, &event, NULL))
return fatal();
if(!(event & EV_TXEMPTY))
return fatal();
elapsed = GetTickCount() - elapsed;
// Display the final result
const unsigned int expected_time =
(sizeof payload * 1000 /* ms */ * 10 /* bits/char */) / BAUDRATE;
printf("Completed in %ums, expected %ums\n", elapsed, expected_time);
return 0;
}
The background is that this is part of a Modbus RTU protocol test suite where I am attempting to inject >3.5 character idle delays between characters on the wire to validate device response.
Admittedly, an embedded realtime system would have been more far suitable for the task but for various reasons I would prefer to stick to a Windows environment while controlling the timing as best as possible.
According to the comments by #Hans Passant and #RbMm the output buffer being referred in the EV_TXEMPTY documentation is an intermediate buffer and the event indicates that data has been forwarded to the driver. No equivalent notification event is defined which encompasses the full chain down to the final device buffers.
No general workaround is presently clear to me short of a manual delay based upon the bitrate and adding a significant worst-case margin for any remaining buffer layers to be traversed, inter-character gaps, clock skew, etc.
I would therefore very much appreciate answers with better alternate solutions.
Nevertheless, for my specific application I have implemented a viable workaround.
The target hardware is a half-duplex bus with a FTDI RS485 interface. This particular device offers an optional local-echo mode in which data actively transmitted onto the bus is not actively filtered from the reception.
After each transmission I am therefore able to wait for the expected echo to appear as a round-trip confirmation. In addition, this serves to detect certain faults such as a short-circuited bus.
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 ??
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.