I'm using a native Arduino MEGA 2560 and a (Chinese) clone, and experience the same issue.
I have a simple communication between a Console based application in C# (using Visual Studio 2022 on a Windows 10 machine) and the Arduino. My C# app and my Arduino can communicate through the Serial Port with no issues..
But in my C# application I can detect if an Arduino is disconnected from or connect to the USB port (using WMI). When I detect a (re)connection, my C# app opens the SerialPort and does some communication with the Arduino based on sending some string commands on which I receive some string answers (all terminated by "\n").
But in many cases that results in a "Timeout Exception", which means that I'm not receiving anything back from the Arduino. If I connect an external "Serial Port Monitor", I see that the Arduino in such case returns some random character (sometimes 2), and then it stops. I can retry my command over and over again, but nothing comes back.
But in case my Arduino is "stuck", and I wait more than 10 seconds before I resend a command after a timeout, then it works again. It is as after disconnecting/reconnecting my USB cable, the Arduino goes in some kind of state that doesn't allow any communication. If I repeat my commands every few seconds, it stays in that state. Only if I maintain complete communication silence for >10seconds, it "unlocks", and I can start communicating again.
So basically, the below shows a bit in pseudocode what happens:
1 - I power up my Arduino with external power
2 - I connect the USB cable
3 - C# App detects the connection, and can identify the Port where Arduino is connected (example: "COM6")
4 - C# App opens the SerialPort (8N1 - 500000baud - read timeout 2000msec, write timeout 1000msec)
5 - C# App sleeps 12 seconds (needs to be at least 10 seconds, or it "might" not work)
6 - C# App sends "\n" and clears input buffer (this is to bring Arduino in start position, and gets rid of all data that could still be in the input buffer of the C# app)
7 - C# App sends "IDENT\n"
8 - Arduino replies with "TEST Module\n" and "ARDUINO\n"
9 - C# App sends "REGISTER\n"
10 - Arduino answers with 3 strings, each one terminated by "\n", and one final "\n" to terminate the registration sequence
If I now disconnect the USB cable, the C# app detects that, and closes the SerialPort. When I then reconnect the USB cable, it all starts over at step 2.
I also did some tests with the IDE (I'm using version 2.2.1). I open my Serial Monitor and can communicate with my Arduino. Then I disconnect my Arduino from the USB, and reconnect it again a few seconds later. In some cases, I get immediate replies on my IDENT command. But in some cases, it doesn't. This means, if I type "IDENT" and press (my setup is "New Line"), I don't get a response. If I repeat my command over and over again, I don't get anything back. But if I stop sending commands for 10 seconds, and then I type "IDENT" again, it suddenly answers. So the IDE kind of shows the same behavior as my C# app.
QUESTION: Can somebody explain this behavior? Is this documented somewhere? And most important, is there a way to get rid of that delay? Of is this "by design", and is the safest approach indeed to wait 10 seconds after a first failed communication attempt (which just works fine for me now).
The relevant code in my Arduino is shown below. The parts relevant for this discussion is the 'SerialEvent()' (I added a few LED toggles trying to trace some stuff) and the 'loop()' where you can see the processing of the "IDENT" and "REGISTER" commands.
#include "src/ButtonClass/ButtonClass.h"
// Command related items
#define CMD_SIZE 100
char sBuffer[CMD_SIZE];
char sCommand[CMD_SIZE];
char* sParameter;
int iRxPtr = 0;
bool bCmdReady = false;
bool bRegistered = false;
// Global variables for Serial communication
const long baudrate = 500000;
// IDENT
const String sIdent = "TEST Module\n";
const String sProcessor = "ARDUINO\n";
// variables (variables start at 001
const char *Variables[] = {
"FLOAT64_R_A:AUTOPILOT ALTITUDE LOCK VAR:3,feet", // 001
"VOID_K:A32NX.FCU_HDG_INC", // 002
"VOID_K:A32NX.FCU_HDG_DEC", // 003
};
size_t nVariables = sizeof(Variables)/sizeof(Variables[0]);
// Easy way to toggle an LED
void ToggleLed(int pin) { digitalWrite(pin, !digitalRead(pin)); }
// Acknowledge
#define ACK Serial.print("A\n")
// Buttons
#define BTN_HDG_INC 40
#define BTN_HDG_DEC 41
ButtonClass bcHDG_INC = ButtonClass(BTN_HDG_INC, 50);
ButtonClass bcHDG_DEC = ButtonClass(BTN_HDG_DEC, 50);
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(baudrate, SERIAL_8N1);
}
void loop() {
// put your main code here, to run repeatedly:
if (bCmdReady) {
// Process IDENT command
if (strcmp(sCommand, "IDENT") == 0) {
Serial.print(sIdent);
Serial.print(sProcessor);
}
// Process REGISTER command
else if (strcmp(sCommand, "REGISTER") == 0) {
for (int i = 0; i < nVariables; i++)
{
Serial.print(Variables[i]);
Serial.print("\n");
}
Serial.print("\n");
bRegistered = true;
}
else if (bRegistered) {
// Reset bRegistered flag - do not send ACK
if (strcmp(sCommand, "RESET") == 0)
{
bRegistered = false;
ToggleLed(LED_BUILTIN);
}
else if ((strlen(sCommand) > 4) && (sCommand[3] == '='))
{
// Assumed that "NNN=..." is received
ACK;
sCommand[3] = '\0';
sParameter = &sCommand[4];
int iID = atoi(sCommand);
if (iID != 0)
{
switch (iID) {
case 1: // FLOAT64_R_A:AUTOPILOT ALTITUDE LOCK VAR:3,feet
{
ToggleLed(LED_BUILTIN);
break;
}
default:
break;
}
}
}
}
bCmdReady = false;
}
if (bRegistered) {
// Process INC
if (bcHDG_INC.ButtonState() == OFF_ON) {
Serial.print("002=1\n"); // VOID_K:A32NX.FCU_HDG_INC
}
// Process DEC
if (bcHDG_DEC.ButtonState() == OFF_ON) {
Serial.print("003=1\n"); // VOID_K:A32NX.FCU_HDG_DEC
}
}
}
void serialEvent()
{
while (Serial.available())
{
char cCmd = Serial.read();
ToggleLed(LED_BUILTIN);
if (cCmd == '\n')
{
sBuffer[iRxPtr] = 0; // terminate string
strcpy(sCommand, sBuffer); // copy sBuffer in sCommand
bCmdReady = true; // indicate that command is available
ToggleLed(LED_BUILTIN);
iRxPtr = 0;
}
else if (cCmd != '\r')
sBuffer[iRxPtr++] = cCmd;
}
}