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 whenscreen
connected, slow afterscreen
ends, fast whenscreen
reconnected
- fast before
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?
2 Answers 2
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.
-
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– gilhad2023年08月24日 01:57:32 +00:00Commented Aug 24, 2023 at 1:57
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
-
1Re "the problem is really in the screen program": I suggest you ditch
screen
and try picocom. This is a small program (tiny compared toscreen
) 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 whetherpicocom
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
.Edgar Bonet– Edgar Bonet2023年08月24日 07:24:50 +00:00Commented 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 itsSerial.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– gilhad2023年08月24日 18:57:08 +00:00Commented 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 windowgilhad– gilhad2023年08月24日 19:12:47 +00:00Commented 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– gilhad2023年08月24日 19:28:22 +00:00Commented Aug 24, 2023 at 19:28
if (Serial)
exactly does on that board, but have you tried just removing that test?};
beforeStatusOn();
and the lack of{
afterif (displayTime())
. I'm guessing it's just from editing, but it's something you may want to fix in the question.if (condition) command;
where command may be compound command, like{ ... }
or simple command, or anotherif (condition) command;
block as it was here. You are right, that the formating was remainder from earlier changes.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 inProblem
section, so I tried theif (Serial)
construction recomended somewhere, but it did not work. I also tried other conditions as inDifference from other
section, but the problem remains.if (condition) {} 7;
it'll still be "correct", but it will look even less so than what you have. =)