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?

5
  • 1
    This is weird. Sending data to a disconnected serial port shouldn't slow things down, the data just gets sent to nowhere. I'm not sure what if (Serial) exactly does on that board, but have you tried just removing that test?
    – PMF
    Aug 23, 2023 at 14:54
  • 1
    You have something weird going on with the }; before StatusOn(); and the lack of { after if (displayTime()). I'm guessing it's just from editing, but it's something you may want to fix in the question.
    – timemage
    Aug 23, 2023 at 14:55
  • @timemage I fixed the formating for better readability, but the code was correct. The syntax is if (condition) command; where command may be compound command, like { ... } or simple command, or another if (condition) command; block as it was here. You are right, that the formating was remainder from earlier changes.
    – gilhad
    Aug 23, 2023 at 19:19
  • @PMF I had it first totally without the if (Serial), in hope that the output will just 'go nowhere' eventually and all will work the same. But I had seen the ?timeouts?, which behaved the same as is described in Problem section, so I tried the if (Serial) construction recomended somewhere, but it did not work. I also tried other conditions as in Difference from other section, but the problem remains.
    – gilhad
    Aug 23, 2023 at 19:27
  • @gilhad it's mostly the semicolon that throws things. You can put a semicolon after code block like that, but it's not terminating it. it's actually it's own statement called a "null statement". you use them in places to fulfill a syntax requirement. but, when you put it there, without a requirement, it just makes it look like you intended to do something else. if you really want to throw people put a number in front of it like if (condition) {} 7; it'll still be "correct", but it will look even less so than what you have. =)
    – timemage
    Aug 24, 2023 at 15:44

2 Answers 2

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.

1
  • Thank you very much, the screen was culprit after all. But it is too convenient for me and I call it from Makefiles all the time, so I found fix for it, which close the Serial anyway :)
    – gilhad
    Aug 24, 2023 at 1:57
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,&RTS_flag);//Clear RTS pin
        ioctl(fd,TIOCMBIC,&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
4
  • 1
    Re “the problem is really in the screen program”: I suggest you ditch screen and try picocom. This is a small program (tiny compared to screen) for accessing devices that provide serial consoles. It does only one thing but does it well; it does not try to emulate or multiplex terminals. I do not know whether picocom defaults to the correct behavior, but it keeps you in control: see the options --noreset, --hangup, --lower-rts, --raise-rts, --lower-dtr and --raise-dtr. Aug 24, 2023 at 7:24
  • @EdgarBonet can I somehow "type in" file to picocom? As if I open picocom /dev/ttyACM0 communicate with Arduino over its Serial.read and then "type-in file lorem.txt" and Arduino will get impression, that I am typing really fast really long texts. (the Serial.read will just receive lots of chars - the content of the file)
    – gilhad
    Aug 24, 2023 at 18:57
  • I am using Gentoo Linux and I also use serial communication with other 8-bit computers, where I need sometimes send few kilobytes of hexa code (Intel Hex) so it will be more convenient, then simply copy-paste text to terminal window
    – gilhad
    Aug 24, 2023 at 19:12
  • 1
    picocom -b 115200 -s cat /dev/ttyUSB0 works (-s cat) and then C-a C-s ./test_serial.002.0200.s.x.hex
    – gilhad
    Aug 24, 2023 at 19:28

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.