Any way to make USB serial monitor non-blocking?

Status
Not open for further replies.

Geomancer

Active member
I'm using a Teensy 3.6.

The serial monitor function becomes blocking if no one is listening, which is creating some problems for me. I'm using an external watchdog chip, and if I don't have the serial monitor up eventually the buffer fills and any Serial.printf (or varieties) become blocking, which means the watchdog doesn't get kicked, which means the Teensy resets.

In my application, the USB port will not be connected 99% of the time, but I would like to plug into it on occasion as a means of monitoring what's happening. Is there a way to change the behavior of this to just ignore anything that exceeds the buffer and continue to be non-blocking?
 
if I don't have the serial monitor up eventually the buffer fills and any Serial.printf (or varieties) become blocking

Which software are you using?

Teensyduino's core library has code to detect this case and avoid blocking. But NXP's code in mbed does not. Maybe you're actually using that mbed code?

The host-present-but-not-listening detection does involve a timeout, so if your watchdog has a very short interval (under 0.1 second) perhaps you're hitting that timeout? But if there's no USB connection at all, the timeout shouldn't be a factor. Teensyduino's code acts exactly the way you're requesting in that case... which is why I'm suspecting you might actually be using NXP's code.
 
I'm using PlatformIO with Visual Studio Code. It's pointing to the Teensyduino libraries, so should be using those.

I did some more debugging though and have narrowed it down.

1) No Cable Connected
Everything works as expected, no continuous resets from the watchdog. I can cause an action that sends a serial message (me pressing a button) but it doesn't cause any resets to occur.

2) Cable Connected, but Serial Monitor (or other serial terminal) not opened
After a few serial messages (triggered by me pressing a button) the watchdog fails to get kicked and the Teensy resets.

3) Cable Connected, Serial Monitor opened
Everything works as expected, I can see the serial messages as they're triggered. Watchdog does not reset the Teensy.


It appears to be blocking only if connected to a PC, but nothing on the PC is reading the messages. I can work around this because as I said, I won't be plugging into it often once I've finished coding, but does mean that if I don't open a serial terminal fast enough it's going to cause a reset.
 
It appears to be blocking only if connected to a PC, but nothing on the PC is reading the messages. I can work around this because as I said, I won't be plugging into it often once I've finished coding, but does mean that if I don't open a serial terminal fast enough it's going to cause a reset.

This simple test code prints out a running number and toggles the LED afterwards. If the print would block without the PC reading data the blinking should stop if you disconnect the SerMon.

Code:
#include "Arduino.h"

void setup()
{
   pinMode(LED_BUILTIN,OUTPUT);
}

unsigned count = 0; 

void loop()
{
    Serial.println(count++);
    digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
    delay(25);
}

Result: After disconnecting SerMon, blinking stops for a short time (~100ms) and continues then without problem. Tested with the Arduino SerMon and TyTools (which is perfect for this test because you can connect/disconnect with a button)

Used a T3.2 for the test (currently don't have a T3.6 lying around), compiled with Arduino IDE and VisualTeensy.
 
Result: After disconnecting SerMon, blinking stops for a short time (~100ms) and continues then without problem.

Yeah, that's far too long of a time. The watchdog chip times out between 4.5 and 8.1 milliseconds. I'm using some high powered solenoids that if they stay on too long melt since they're basically just an inductor and if allowed to reach DC the current is huge.

I have a jumper for the watchdog reset signal, so if needed I can just remove the jumper whenever I want to plug in for debug info to prevent a reset. So, I can work around the issue, it would just be nice to have an option or flag that can be set to prevent the Serial function from waiting. I see both sides of it, somethings you really don't want data to be lost, and others you really don't want delays.
 
It does have an issue I had not seen before! On Windows using IDE 1.8.13 and TD 1.53b1 to build.

I happen to have a T_4.0 running loop toggling a pin 5.3M times per second in loop(). It only prints once per second (eleven chars) to show the loop/sec count which is how often it toggles the pin.

Then a second T_4.1 is monitoring that pin output and showing the counts it received per second, and printing that and it's loop() count once per second. It is doing this with a (edited) PIN _isr() - so loop counts increase when the input toggle stops.

Using TyCommander I click Disable 'Serial' on the sending T_4.0 which keeps it powered but disconnects the USB port on the PC side.

What I see is this in the T_4.1 Serial monitor :
Code:
[U]l#=10473[/U]  i#=[B]5380507[/B]
[B][COLOR="#FF0000"][U]l#=2143155[/U]  i#=4734399[/COLOR][/B]
l#=10260  i#=5380520

This shows the T_4.1 stopped seeing _isr() hits for some time allowing the loop() count to go from l#=10473 to l#=2143155 in that second

And the number of missed pin toggles agrees showing it went from 5380507 to 4734399

This happens about 5-6 seconds after Serial USB on T_4.0 is disconnected - where each print on the T_4.0 per second is eleven bytes "l#=5359332\n" - so about the time 64 bytes is accumulated.

Based on missed toggles disabling Serial USB from PC represents a STALL of the T_4.0 of about 0.12008310740976640305458203102421 seconds - after which it resumes normal function without SerMon connected - or if SerMon reconnects.
 
Last edited:
Yeah, that's far too long of a time. The watchdog chip times out between 4.5 and 8.1 milliseconds

Unfortunately, PC operating system latency can be much higher than 8 ms. Especially on Windows where the default system tick is 16 ms when no special "multimedia" tasks are running, if we conclude the PC isn't listening on such a short time scale, we'll get false triggering (and data loss) under all sorts of close-to-normal situations. There's a reason the wait for the PC side to consume data is approx 100 ms before we conclude it's not running and start discarding data.

We do have a Serial.availableForWrite() function which is meant to be used in these sorts of applications, where you just can't afford to have Serial.print() or Serial.write() block the rest of your program. The idea is Serial.availableForWrite() tells you how many bytes you can write without extra latency. It's up to you to decide how to use that info. Maybe you decide to discard your outgoing message. Or send it later? Or do something else?
 
Paul attempting to preserve data during the transition to loss declaration of USB Serial makes sense, though that blocking seems a significant impact. Notes below suggest the returned count are not as expected - drop by 2048 for 11 byte wrtites?
This is on T_4.0 - Sketch noted above has DualSerial and on the second doing - this does work to prevent blocking::
Code:
	if ( lTime > 1000 ) {
		SerialUSB1.print( Serial.availableForWrite() );
		SerialUSB1.print( "\t" );
		if ( Serial.availableForWrite() >20 )
			Serial.printf("l#=%lu\n", lCnt ); [B]// print shows : "l#=8454057"[/B]
		else
			SerialUSB1.print( "NO BUFFER ROOM \t" );
		SerialUSB1.println( Serial.availableForWrite() );
		lTime = 0;
		lCnt = 0;
	}

Shows this on the T_4.0 SerialUSB1 when Serial is disconnected. The reported counts are the same before and after print, and they drop oddly with no other 'Serial' printing in the sketch:
Code:
6144	6144
4096	4096
2048	2048
0	NO BUFFER ROOM 	0
0	NO BUFFER ROOM 	0
0	NO BUFFER ROOM 	0
 
Status
Not open for further replies.
Back
Top