@@ -46,7 +46,8 @@ bool CommsController::enqueueRx(const char* msg, const IpAddress& ip, uint16_t p
4646 int next_head = (m_rxQueueHead + 1 ) % RX_QUEUE_SIZE;
4747 if (next_head == m_rxQueueTail) {
4848 if (m_guiDiscovered && EthernetMgr.PhyLinkActive ()) {
49- char errorMsg[] = " PRESSBOI_ERROR: RX QUEUE OVERFLOW - COMMAND DROPPED" ;
49+ char errorMsg[128 ];
50+ snprintf (errorMsg, sizeof (errorMsg), " %s_ERROR: RX QUEUE OVERFLOW - COMMAND DROPPED" , DEVICE_NAME_UPPER);
5051 m_udp.Connect (m_guiIp, m_guiPort);
5152 m_udp.PacketWrite (errorMsg);
5253 m_udp.PacketSend ();
@@ -74,7 +75,8 @@ bool CommsController::enqueueTx(const char* msg, const IpAddress& ip, uint16_t p
7475 int next_head = (m_txQueueHead + 1 ) % TX_QUEUE_SIZE;
7576 if (next_head == m_txQueueTail) {
7677 if (m_guiDiscovered && EthernetMgr.PhyLinkActive ()) {
77- char errorMsg[] = " PRESSBOI_ERROR: TX QUEUE OVERFLOW - MESSAGE DROPPED" ;
78+ char errorMsg[128 ];
79+ snprintf (errorMsg, sizeof (errorMsg), " %s_ERROR: TX QUEUE OVERFLOW - MESSAGE DROPPED" , DEVICE_NAME_UPPER);
7880 m_udp.Connect (m_guiIp, m_guiPort);
7981 m_udp.PacketWrite (errorMsg);
8082 m_udp.PacketSend ();
@@ -136,7 +138,9 @@ void CommsController::processUsbSerial() {
136138 // Buffer overflow protection - discard message
137139 else {
138140 usbBufferIndex = 0 ;
139- ConnectorUsb.Send (" PRESSBOI_ERROR: USB command too long\n " );
141+ char errorMsg[128 ];
142+ snprintf (errorMsg, sizeof (errorMsg), " %s_ERROR: USB command too long\n " , DEVICE_NAME_UPPER);
143+ ConnectorUsb.Send (errorMsg);
140144 }
141145 }
142146}
@@ -168,35 +172,50 @@ void CommsController::processTxQueue() {
168172 // Note: If ethernet link is down or no valid network IP, UDP send is skipped
169173 // This prevents watchdog timeouts from blocking UDP sends
170174
171- // Mirror to USB serial with chunking for large messages
172- // This happens ALWAYS, regardless of network state
173- // USB CDC buffer is 64 bytes, so we chunk messages larger than 50 bytes
175+ // Mirror to USB serial (if USB host is connected and reading)
176+ // USB CDC buffer is 64 bytes, so we chunk messages larger than 50 bytes
177+ //
178+ // USB Connection Detection:
179+ // - If USB buffer has space, a host is connected and reading data
180+ // - If USB buffer stays full for >3 seconds, no host is reading (disconnected or app closed)
181+ // - Stop sending to prevent buffer deadlock, resume automatically when host reconnects
182+
183+ static uint32_t lastUsbHealthy = 0 ;
184+ static bool usbHostConnected = true ; // Assume connected at boot
185+ uint32_t now = Milliseconds ();
186+ int usbAvail = ConnectorUsb.AvailableForWrite ();
187+
188+ // Check if USB buffer has space - if yes, a host is actively reading
189+ if (usbAvail > 5 ) {
190+ lastUsbHealthy = now;
191+ if (!usbHostConnected) {
192+ // USB host reconnected! Resume sending
193+ usbHostConnected = true ;
194+ char recoveryMsg[64 ];
195+ snprintf (recoveryMsg, sizeof (recoveryMsg), " %s_INFO: USB host reconnected\n " , DEVICE_NAME_UPPER);
196+ ConnectorUsb.Send (recoveryMsg);
197+ }
198+ } else {
199+ // Buffer is nearly full - either host is slow or disconnected
200+ if (usbHostConnected && (now - lastUsbHealthy) > 3000 ) {
201+ // Buffer full for 3+ seconds - host disconnected or stopped reading
202+ usbHostConnected = false ;
203+ // Stop sending to prevent buffer deadlock
204+ }
205+ }
206+
207+ // Only send to USB if a host is connected and reading
208+ if (usbHostConnected) {
174209 const int CHUNK_SIZE = 50 ;
175210 int msgLen = strlen (msg.buffer );
176211
177212 if (msgLen <= CHUNK_SIZE) {
178213 // Small message - send directly if buffer available
179- int usbAvail = ConnectorUsb.AvailableForWrite ();
180214 if (usbAvail >= msgLen + 1 ) {
181215 ConnectorUsb.Send (msg.buffer );
182216 ConnectorUsb.Send (" \n " );
183- } else {
184- // Debug: Report dropped messages
185- static uint32_t dropCount = 0 ;
186- static uint32_t lastReport = 0 ;
187- dropCount++;
188- uint32_t now = Milliseconds ();
189- if (now - lastReport > 2000 ) {
190- char dbg[64 ];
191- snprintf (dbg, sizeof (dbg), " USB_DROP: %lu msgs, avail=%d need=%d\n " , dropCount, usbAvail, msgLen + 1 );
192- // Try to send error if space available
193- if (ConnectorUsb.AvailableForWrite () > 60 ) {
194- ConnectorUsb.Send (dbg);
195- }
196- lastReport = now;
197- dropCount = 0 ;
198- }
199217 }
218+ // If buffer full, just drop the message silently
200219 } else {
201220 // Large message - send in chunks with continuation markers
202221 // Format: "MSG_PART_1/3: first_50_chars"
@@ -228,8 +247,9 @@ void CommsController::processTxQueue() {
228247 }
229248 }
230249 }
231- // If buffer is full, message is silently dropped to prevent blocking
232- // This prevents watchdog timeouts from blocking USB sends
250+ }
251+ // If USB unhealthy or buffer full, message is silently dropped to prevent blocking
252+ // USB will auto-recover when buffer has space again (host reconnects)
233253 }
234254}
235255
@@ -251,6 +271,9 @@ void CommsController::setupUsbSerial(void) {
251271 ConnectorUsb.PortOpen ();
252272 // USB setup is non-blocking - the connector will become available when ready
253273 // No need to wait here, as the main loop will handle USB when available
274+
275+ // Send a startup message after a delay to confirm USB is working
276+ // (Will be sent in the main loop once USB is ready)
254277}
255278
256279void CommsController::setupEthernet () {
@@ -277,7 +300,9 @@ void CommsController::setupEthernet() {
277300 m_udp.Begin (LOCAL_PORT);
278301
279302 // Send status message over USB to confirm network is ready
280- ConnectorUsb.Send (" PRESSBOI_INFO: Network ready, listening on port 8888\n " );
303+ char infoMsg[128 ];
304+ snprintf (infoMsg, sizeof (infoMsg), " %s_INFO: Network ready, listening on port %d\n " , DEVICE_NAME_UPPER, LOCAL_PORT);
305+ ConnectorUsb.Send (infoMsg);
281306}
282307
283308// parseCommand is now in commands.cpp as a global function
0 commit comments