3

I have an arduino micro and several clone "pro micro"'s both of which are based on the 32u4, i'm trying to make my project have a debug mode which is only active when the serial port is open, the documentation states that using:

if (Serial)

on these boards should only return true if the port is open (on uart based connections it always returns true), i wrote my sketch to take advantage of that but it didn't seem to be working properly, so i stripped back to basics and wrote this sketch:

void setup() {
    // put your setup code here, to run once:
    pinMode(2, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
    if (Serial){
        digitalWrite(2, HIGH);
    }
    else{
        digitalWrite(2, LOW);
    }
}

I connected an LED to pin 2 and then tested it by connecting and disconnecting the arduino from the PC, and opening and closing the com port.

after freshly connecting the arduino to the PC the LED is off, when i open a connection to the virtual com port the LED turns on. however i would expect the LED to go out once the port is closed, but it remains on.

My questions are:
1) have i got an out of date bootloader?
2) if so how do i check?
3) is this intended behavior and if so why?
4) is this a bug in the arduino bootloader or libraries?
5) short of having to change my code to look for serial data to start and stop the debug mode (serial comms are being used for other things in the background) is there any way i can work around this? reading from hidden registers etc?

Edit:
should have mentioned i'm seeing this behavior with or without a call to Serial.begin

6
  • Might need to call Serial.begin() first. Commented Feb 18, 2016 at 19:08
  • see edit to question, i get this behavior with or without a call to Serial.begin
    – James Kent
    Commented Feb 18, 2016 at 19:16
  • I'd say this is intended behavior. arduino.cc/en/Serial/IfSerial
    – Avamander
    Commented Feb 18, 2016 at 19:21
  • why would this be intended? "On 32u4 based boards (Leonardo, Yùn, ecc) , if (Serial) indicates wether or not the USB CDC serial connection is open. " that sounds to me that it should only return true while the port is open, so once i close it it should start returning false, yet it doesn't
    – James Kent
    Commented Feb 18, 2016 at 19:23
  • It would be far simpler to enter debug mode if a certain pin was jumpered to Gnd, as that is something you can quickly set up, and test easily.
    – Nick Gammon
    Commented Feb 18, 2016 at 19:51

2 Answers 2

9

1) have i got an out of date bootloader?

No. This has nothing to do with the bootloader.

2) if so how do i check?

See above.

3) is this intended behavior and if so why?

In a manner of speaking, yes - see below.

4) is this a bug in the arduino bootloader or libraries?

No. Elsewhere. See below.

5) short of having to change my code to look for serial data to start and stop the debug mode (serial comms are being used for other things in the background) is there any way i can work around this? reading from hidden registers etc?

Yes, you can, but only once you understand exactly what is going on.

The problem with USB serial (AKA CDC/ACM) communication is that there is no way of knowing if the port has been opened by the computer or not. That functionality was just never designed into the protocol.

However, there are two virtual modem signals that the computer can assert that the CDC/ACM device at the other end can examine - one is the equivalent of DTR and the other the equivalent of RTS.

The Arduino's CDC/ACM code examines those, and if either are asserted then it assumes the CDC/ACM port is open. If neither are asserted then it assumes the port is closed.

Now comes the tricky part: You have little or no control over when those signals are asserted or not. There are three separate factors that decide what happens:

  1. The software being run can manually control the signals. The Arduino IDE does this to ensure boards that use DTR to reset (like the Uno, etc) actually do reset.
  2. The drivers on the computer can decide to automatically control the signals when the port is opened or closed
  3. The operating system itself can control the signals when the port is opened or closed.

Some software will control the RTS/DTR signals properly, some won't. Some drivers will control the signals for you, some won't. Some operating systems (such as Linux) will, unless otherwise instructed, default to automatically controlling the modem lines properly. Others, like Windows, won't.

So the only way you can be sure that these signals are being controlled properly is to manually control them from your own software used for debugging, or to find a specific terminal emulator that controls the lines properly.

8
  • Thank you, this is exactly what i was trying (and failing) to find any information on
    – James Kent
    Commented Feb 18, 2016 at 19:55
  • have you got any information on how i check the RTS/DTR lines in the arduino? i can't seem to find anything about it: arduino.cc/en/Reference/Serial
    – James Kent
    Commented Feb 18, 2016 at 19:57
  • @JamesKent It took me many months (on and off) of research and experimentation, reading USB specifications, other peoples' source code, etc, to find just this out ;) It's not something that is commonly known.
    – Majenko
    Commented Feb 18, 2016 at 19:57
  • @JamesKent The if(Serial) does it. The Arduino side is fine. It's the computer's end that is at fault.
    – Majenko
    Commented Feb 18, 2016 at 19:58
  • if (_usbLineInfo.lineState > 0) result = true; is the bit of code in CDC.cpp that does it.
    – Majenko
    Commented Feb 18, 2016 at 20:00
2

I am experiencing the same problem. My environment is Windows 10 on the host side and Arduino 1.6.7 with a genuine Arduino Micro board.

In the OP's sketch, after compiling and uploading, the light does not go on until you open the serial monitor. This is as expected and shows that the Arduino side has some way of knowing that the serial monitor has started.

However if you close the serial monitor, the light stays on. It also stays on if you unplug the USB cable. It goes off when you plug the cable back in and goes back on when you restart the serial monitor.

With some further experimentation and poking around the code, I found that _usbLineInfo.lineState > 0 and thus Serial returns True if either DTR or RTS from the host is nonzero. DTR goes to 0 when the serial monitor is closed but RTS stays HIGH so Serial stays True.

The fix is to use the (undocumented) method Serial.dtr(). This is True only if the serial monitor is running.

[edit] If your system has external power so that it should keep working with the USB cable unplugged, testing Serial.dtr() works as long as you you always close the serial monitor before unplugging the USB cable. However it will remain True if the cable is unplugged before closing the serial monitor.

To detect the cable being unplugged, add the line

USBCON |= 1<<OTGPADE;

to setup(). This enables detection of an unplugged USB cable. Then in your loop

(USBSTA & (1<<VBUS))

will be True if the USB cable is plugged in and has power.

1
  • i wouldn't describe this as a fix, more of a workaround, long term i think that the CDC.cpp file needs modifying to handle different OS's ways of leaving the ports better, which may well be only checking the state of the DTR line
    – James Kent
    Commented Feb 20, 2016 at 12:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.