Okay, it was much simpler than I thought. We just use Serial.begin() when we first notice an userspace application has connected (by noticing that (Serial) is now true), and when (Serial) turns false, we stop outputting and do Serial.end(). Very logical!
Here is the example serial-counter.ino I crafted:
Code:
// LED pin to use for indicating an application connection
#define LED 13
// Duration between counter increments, in milliseconds
#define INTERVAL_MS 200
// Initial counter value to use with a new userspace application
#define COUNTER_START 1
// Flag to indicate whether we believe we are connected to an userspace application
static bool counting;
// The counter whose value will be provided via the USB serial connection
static uint32_t counter;
void setup() {
// We use the LED to indicate when we are connected to an userspace application on the host computer.
pinMode(LED, OUTPUT);
// When first powered on, we start disconnected and with a zero counter.
counting = false;
counter = 0;
// but, we also want the USB serial device to be enabled/running, initially.
Serial.begin(9600);
}
void loop() {
// Do we have an userspace application connected to us via USB serial?
if (Serial) {
// We have a connection to an userspace application.
if (!counting) {
// but we must initialize our USB Serial object first.
Serial.begin(9600);
counting = true;
counter = COUNTER_START;
// Let the LED show we are connected.
digitalWriteFast(LED, HIGH);
}
// Because we do not read any input, we need to clear the input buffer
// or we will get stuck if the user inputs something.
Serial.clear();
// Output the counter and increment it.
Serial.println(counter);
counter++;
} else {
// No connection to an userspace application.
if (counting) {
// Stop counting, and clean up the Serial object.
Serial.end();
counting = false;
// Let the LED show we are connected.
digitalWriteFast(LED, LOW);
}
}
delay(INTERVAL_MS);
}
You can try this trivially with the Arduino Serial Monitor. Whenever you open the window, it starts counting from 1 upwards, five numbers per second (200ms interval is 1000/200ms = 5 Hz).
Note the Serial.clear(); and the accompanying comment. I did notice that without this, in certain circumstances I could get the Teensy to get stuck (stop outputting, even though it knew it was connected to an userspace application since the LED was lit). So, the read and write sides on the Serial object on the Teensy are not truly independent, and if one gets stopped (because of buffering, and either Teensy or the userspace application is not consuming the data), the other gets stopped too. (Meaning, you cannot just ignore input and do output. You need to either discard the input like I do with Serial.clear() above, or consume it, to be sure you can write too. As I understand it, this is because both sending and receiving via USB uses the same buffer space on the Teensy.)
When using the Linux tty device, we must either discard all input in the Teensy example, or stop the tty device from echoing everything by default. You see, serial ports in POSIXy and UNIXy systems like Linux and Macs, for historical reasons, default to a state where the input is echoed. If we do not change that, then Teensy will receive an echo of everything it outputs! (If you find yourself staring at "infinite output" from Teensy, this is the reason: your serial port is told to echo everything Teensy writes back to Teensy, and if Teensy echoes everything it sees, you get an infinite loop of data between the kernel (more properly, the tty device in the kernel) and the Teensy. Ouch!)
This state is controlled on the host system via an interface called termios; this interface is built in to the C libraries on these systems. There is also a command-line tool, stty, that can be used to change that state.)
So, the proper test to read output from Teensy in Linux and Macs is not cat /dev/ttyACM0; that causes Teensy to get into an echo chamber loop. You need to do something like
Code:
bash -c 'exec 0<>/dev/ttyACM0 ; stty raw ; cat'
instead, to disable that echo. This runs three commands under the Bash shell. The exec part redirects standard input (as readable and writable) to the USB serial device. The second sets standard input to "termios raw mode"; basically the mode that disables all shenanigans, and lets you send and receive serial data as-is without echo and other magic. The third then outputs everything from input (the USB serial device, since we redirected it) to output. We run it in a Bash subshell, because that way we can return back to the original shell by pressing Ctrl+C, and have control back.
(The reason I didn't do this in the first example, is that I knew there was no data coming out of the Teensy, so infinite echo chamber was not possible. And I wanted to keep everything as concise as I could and omit the tty/termios stuff, because I've very recently been told in another forum that I am basically a troll ("reported to moderators") because I write too long and too complicated posts.)
If you find this interesting, you might consider adding a second counter, to count the number of userspace applications that have connected, and print a line describing that before printing the initial counter value. This is an easy change, just a three lines added at the proper spots, I think; should be interesting!