2

How to detect, if Serial is really connected to listening PC program? (on atmega32u4)

I need to connect from PC to Arduino Micro Pro over USB Serial and disconnect again and not slow down the Arduino with timeouting on all Serial.print.

Difference from other

Not same as Arduino Leonardo(Atmega32u4) - Detecting if USB is connected to a computer?

  • difference is what should happen after disconnecting PC program
    • "it is still connected to PC" (so it is OK, solved there)
    • "it is not listend to" and timeouting (so it is BAD, asked here)
  • I tried the both solutions mentioned there (UDADDR & _BV(ADDEN)) and (USBSTA & _BV(VBUS)) as well as (Serial) - it behave the same from my point of view
    • fast before screen connects, fast when screen connected, slow after screen ends, fast when screen reconnected

Details

I have Arduino Micro Pro managing a lot of things at its own, but I want sometimes connect to it via USB from my computer. I open screen /dev/ttyACM0 115200 and get a lot of debugging info and so on. Then I close the screen and let it work in peace. But there is problem with timeouts - when I stop listening, it waits for me and everything is terribly slow, until I connect there again.

Here is my loop

    void loop() {
        DoSomethingImportant(); // it works
        StatusOff(); // Green LED off, Yellow On
        if (displayTime()) {  // print debug only on new second
            if (Serial) {   // ######## If there is somebody listening ... #########
                Serial.print(F("Ctrl: "));Serial.print(W00.Used());
                Serial.print(F("); R1/W1: "));Serial.print(R01.Used());Serial.print(F("/"));Serial.print(W01.Used());Serial.print(F(" ("));Serial.print(R01.Total_Count());Serial.print(F("/"));Serial.print(W01.Total_Count());
                Serial.print(F("); R2/W2: "));Serial.print(R02.Used());Serial.print(F("/"));Serial.print(W02.Used());Serial.print(F(" ("));Serial.print(R02.Total_Count());Serial.print(F("/"));Serial.print(W02.Total_Count());
                Serial.print(F("); R3/W3: "));Serial.print(R03.Used());Serial.print(F("/"));Serial.print(W03.Used());Serial.print(F(" ("));Serial.print(R03.Total_Count());Serial.print(F("/"));Serial.print(W03.Total_Count());
                Serial.println(F("); "));
            };
        };
        StatusOn(); // Green LED on, Yellow Off
    }

Problem

When I connect the Arduino to PC (so it gets powered, I do not have much of the rest HW completed yet), it starts to do Something Important as fast as it can and every second the yellow LED blinks too fast to see that Green was off. It is OK.

Then I connect with screen /dev/ttyACM0 115200 and it gets new long line every second. Still the Green looks all time on and Yellow can be seen just blink. Still OK.

Then I end screen and it went slow - Yellow is on for like 7 second, while Green is on for 1 sec. And it is BAD.

Then I connect with screen /dev/ttyACM0 115200 and it gets new long line every second. The Green looks all time on and Yellow can be seen just blink. Again OK.

Question

How to detect, if I am connected or not?

Or maybe better, how to prevent the terrible slowdown, when I am not connected?

Rohit Gupta
  • 618
  • 2
  • 5
  • 18
gilhad
  • 1,466
  • 2
  • 11
  • 20

2 Answers2

1

I can reproduce this on Ubuntu, however it seems to be an anomaly of the screen program. If I use this small Python program to listen to the port, the Micro doesn't seem to slow down.

import serial

ser = serial.Serial('/dev/ttyACM0', 115200) # Adjust the serial port accordingly

while True: if ser.readable(): line = ser.readline().decode().strip() print (line)

You may need to import pyserial into Python, for example:

python -m pip install pyserial

My Arduino test code was:

const byte LED_PIN = 5;  // some pin for an LED

void setup() { Serial.begin (115200); pinMode (LED_PIN, OUTPUT);

}

void loop() { digitalWrite (LED_PIN, HIGH); delay (250); digitalWrite (LED_PIN, LOW); delay (250);

Serial.println(F("Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" "Ut molestie quam cursus, feugiat massa eget, lobortis ante.\n" "Nulla semper nisi a pretium finibus. Proin rutrum accumsan massa sit amet gravida.\n" "Etiam mollis, ipsum ac sagittis aliquam, sem urna euismod ipsum,\n" "eget accumsan orci sapien ornare felis. Nulla ex felis, facilisis in tincidunt at,\n" "dignissim vitae ligula. Aliquam facilisis tristique velit,\n" "a scelerisque turpis mollis quis. Nulla porta, mi vel ornare feugiat,\n" "mauris ligula commodo felis, in congue orci neque a est. "));

}

By looking at the rate of flashes of the LED I can see that it doesn't slow down when running the Python program, or if you Ctrl+C to exit the Python program.

However it does slow down if you attach to the USB using screen and then kill it.


I don't think the Arduino can per se know whether or not a program on the PC happens to be listening to the USB port or not. That is a function of what is happening in the operating system, not a function of the USB hardware.


Another approach which I haven't tested might be to get your Python program to periodically (eg. for each line) send a character, and get the Arduino to check for serial input. If no serial input (ie. not prompting character) then don't echo the debugging data.


Another approach (and possibly the simplest) is just to have a switch on the micro on a spare port, and close the switch if you want debugging information, and open it if you don't. Then you have total control over the debugging.

Nick Gammon
  • 38,901
  • 13
  • 69
  • 125
1

Thanks to Nick Gammon for his answer, the problem is really in the screen program.

There is detailed explanation from Majenko in if(Serial) not working correctly on micro or pro micro

The problem is, that some programs (like screen) or some systems, or drivers, or so do not pull down RTS and DTR signals for Micro. (The DTR down is supposed to restart Uno and other non-USB arduinos).

It could be solved by running some small program to close the USB Serial properly after the screen is done with it - here are two which works for me

C program

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>

int main(int argc, char *argv[]) { // Check if the correct number of command-line arguments is provided if (argc != 2 ) { fprintf(stderr, "Usage: %s <filename>\n", argv[0]); fprintf(stderr, " %s /dev/ttyACM0 - close USB Serial for Arduino on the port\n", argv[0]); return 1; // Return an error code } int fd; fd = open(argv[1],O_RDWR | O_NOCTTY );//Open Serial Port

    int RTS_flag;
    RTS_flag = TIOCM_RTS;

    int DTR_flag;
    DTR_flag = TIOCM_DTR;

// ioctl(fd,TIOCMBIS,&RTS_flag);//Set RTS pin // ioctl(fd,TIOCMBIS,&DTR_flag);//Set DTR pin

    ioctl(fd,TIOCMBIC,&amp;RTS_flag);//Clear RTS pin
    ioctl(fd,TIOCMBIC,&amp;DTR_flag);//Clear DTR pin
    close(fd);

}

Python

#!/usr/bin/python -u

import serial import sys

Check if the correct number of command-line arguments is provided

if not len(sys.argv) in {2,3}: print("Usage: {} <filename> [baudrate]".format(sys.argv[0])) print(" {} /dev/ttyACM0 - close USB Serial for Arduino on the port".format(sys.argv[0])) sys.exit(1) # Exit with a non-zero error code

Get the filename from the command-line argument

baudrate=115200 filename = sys.argv[1] if len(sys.argv) > 2: baudrate=sys.argv[2]

Open the serial port

ser = serial.Serial(filename, baudrate, xonxoff=False , rtscts=True , dsrdtr=True ) ser.rts=False ser.dtr=False

gilhad
  • 1,466
  • 2
  • 11
  • 20