Как определить, действительно ли последовательный порт подключен к прослушивающей программе ПК? (на atmega32u4)

Как определить, действительно ли Serial подключен к прослушивающей программе ПК? (на atmega32u4)

Мне нужно подключиться от ПК к Arduino Micro Pro через USB Serial и снова отключиться, а не замедлять работу Arduino с тайм-аутом на всех Serial.print.

Отличие от других

Не то же самое, что Arduino Leonardo (Atmega32u4) — определяет, подключен ли USB к компьютеру?

  • разница в том, что должно произойти после отключения программы на ПК
    • "он все еще подключен к ПК" (так что ОК, там решено)
    • "его не слушают" и таймаут (так что это ПЛОХО, спросил здесь)
  • Я попробовал оба упомянутых там решения (UDADDR & _BV(ADDEN)) и (USBSTA & _BV(VBUS)) а также (Serial) - с моей точки зрения, они ведут себя одинаково
    • быстро до подключения screen, быстро, когда screen подключен, медленно после завершения screen, быстро, когда >экран повторно подключен

Подробности

У меня есть Arduino Micro Pro, который самостоятельно управляет многими вещами, но иногда мне хочется подключиться к нему через USB со своего компьютера. Я открываю screen /dev/ttyACM0 115200 и получаю много отладочной информации и так далее. Затем я закрываю экран и позволяю ему спокойно работать. Но есть проблема с таймаутами - когда я перестаю слушать, он меня ждет и все ужасно тормозит, пока я туда снова не подключусь.

Вот мой цикл

    void loop() {
        DoSomethingImportant(); // оно работает
        StatusOff(); // Зеленый светодиод выключен, желтый включен
        if (displayTime()) {  // печатаем отладку только с новой секунды
            if (Serial) {   // ######## Если кто-то слушает ... #########
                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(); // Зеленый светодиод включен, желтый выключен
    }

Проблема

Когда я подключаю Arduino к ПК (чтобы он получил питание, у меня еще не завершена большая часть остального оборудования), он начинает делать что-то важное так быстро, как только может, и каждую секунду желтый светодиод мигает слишком быстро, чтобы увидеть, что Грин был выключен. Это нормально.

Затем я подключаюсь к screen /dev/ttyACM0 115200, и каждую секунду он получает новую длинную строку. Тем не менее, зеленый все время светится, а желтый просто мигает. Все еще в порядке.

Затем я закрываю экран, и он стал медленным — желтый горит примерно 7 секунд, а зеленый — 1 секунду. И это ПЛОХО.

Затем я подключаюсь к screen /dev/ttyACM0 115200, и каждую секунду он получает новую длинную строку. Зеленый светится все время, а желтый можно увидеть только мигающим. Опять ок.

Вопрос

Как определить, подключен я или нет?

А может лучше, как предотвратить ужасное замедление, когда я не на связи?

, 👍2

Обсуждение

Это странно. Отправка данных в отключенный последовательный порт не должна замедлять работу, данные просто отправляются в никуда. Я не уверен, что именно делает if (Serial) на этой плате, но пробовали ли вы просто удалить этот тест?, @PMF

У вас происходит что-то странное с }; перед StatusOn(); и отсутствием { после if (displayTime()). Я предполагаю, что это просто редактирование, но вы, возможно, захотите исправить это в вопросе., @timemage

@timemage Я исправил форматирование для лучшей читаемости, но код был правильным. Синтаксис: if (условие) команда;, где команда может быть составной командой, например { ... } или простой командой, или другим блоком if (условие ) команда;, как это было здесь. Вы правы, что форматирование осталось от более ранних изменений., @gilhad

@PMF Сначала я сделал это полностью без if (Serial), в надежде, что вывод в конечном итоге просто «уйдет в никуда», и все будет работать так же. Но я видел «таймауты», которые вели себя так же, как описано в разделе «Проблемы», поэтому я попробовал рекомендованную где-то конструкцию «if (Serial)», но она не сработала. Я также пробовал другие условия, как в разделе «Отличие от других», но проблема осталась., @gilhad

@gilhad, в основном это точка с запятой, которая выбрасывает вещи. Вы можете поставить точку с запятой после такого блока кода, но это не завершает его. на самом деле это собственный оператор, называемый «нулевым оператором». вы используете их местами для выполнения требований синтаксиса. но когда вы помещаете его туда без каких-либо требований, это просто создает впечатление, будто вы намеревались сделать что-то другое. если вы действительно хотите выбросить людей, поставьте перед ним число, например if (condition) {} 7; оно все равно будет «правильным», но будет выглядеть еще хуже, чем то, что у вас есть. "=", @timemage


2 ответа


1

Я могу воспроизвести это в Ubuntu, однако, похоже, это аномалия экранной программы. Если я использую эту небольшую программу на Python для прослушивания порта, Micro не замедляется.

import serial

ser = serial.Serial('/dev/ttyACM0', 115200)  # Настройте последовательный порт соответствующим образом

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

Возможно, вам понадобится импортировать pyserial в Python, например:

python -m pip install pyserial

Мой тестовый код Arduino был:

const byte LED_PIN = 5;  // какой-то вывод для светодиода

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. "));
  
  
}

Глядя на частоту миганий светодиода, я вижу, что он не замедляется при запуске программы Python или при нажатии Ctrl+C для выхода из программы Python.

Однако он замедляется, если вы подключаете USB-накопитель с помощью screen, а затем убиваете его.


Я не думаю, что Arduino сама по себе может знать, прослушивает ли программа на ПК порт USB или нет. Это зависит от того, что происходит в операционной системе, а не от аппаратного обеспечения USB.


Другой подход, который я не тестировал, может заключаться в том, чтобы ваша программа Python периодически (например, для каждой строки) отправляла символ и заставляла Arduino проверять последовательный ввод. Если нет последовательного ввода (т. е. нет символа запроса), не отображайте данные отладки.


Другой подход (и, возможно, самый простой) состоит в том, чтобы просто включить микропереключатель на запасном порту и закрыть переключатель, если вам нужна отладочная информация, и открыть его, если вам этого не нужно. Тогда вы получите полный контроль над отладкой.

,

Спасибо большое, все-таки виноват "экран". Но для меня это слишком удобно, и я все время вызываю его из Makefiles, поэтому нашел исправление, которое все равно закрывает Serial :), @gilhad


1

Спасибо Нику Гаммону за ответ, проблема действительно в программе screen.

Подробное объяснение от Маженко см. если (последовательный порт) работает неправильно на микро или профессиональном микро

Проблема в том, что некоторые программы (например, screen), или некоторые системы, или драйверы, или что-то подобное не сбрасывают RTS и DTR сигналы для Микро. (Предполагается, что нажатие DTR перезапустит Uno и другие Arduino, не поддерживающие USB).

Это можно решить, запустив какую-нибудь небольшую программу, которая правильно закроет USB-последовательный порт после того, как экран будет готов. Вот две из них, которые мне подходят

>

Программа C

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

int main(int argc, char *argv[]) {
        // Проверяем, указано ли правильное количество аргументов командной строки
        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; // Возвращаем код ошибки
        }
        int fd;
        fd = open(argv[1],O_RDWR | O_NOCTTY );//Открыть последовательный порт

        int RTS_flag;
        RTS_flag = TIOCM_RTS;

        int DTR_flag;
        DTR_flag = TIOCM_DTR;

// ioctl(fd,TIOCMBIS,&RTS_flag);//Установить вывод RTS
// ioctl(fd,TIOCMBIS,&DTR_flag);//Установить контакт DTR

        ioctl(fd,TIOCMBIC,&RTS_flag);//Очистить вывод RTS
        ioctl(fd,TIOCMBIC,&DTR_flag);//Очистить контакт DTR
        close(fd);
}

Питон

#!/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
,

По поводу «_проблема на самом деле в экранной программе_»: я предлагаю вам отказаться от screen и попробовать [picocom](https://github.com/npat-efault/picocom). Это небольшая программа (_tiny_ по сравнению с screen) для доступа к устройствам, предоставляющим последовательные консоли. Он делает только одно, но делает это хорошо; он не пытается эмулировать или мультиплексировать терминалы. Я не знаю, правильно ли поведение picocom по умолчанию, но оно позволяет вам контролировать ситуацию: см. параметры --noreset, --hangup, --lower-rts, --raise-rts , --lower-dtr и --raise-dtr., @Edgar Bonet

@EdgarBonet могу ли я как-нибудь «ввести» файл в picocom? Как будто я открываю picocom /dev/ttyACM0 и общаюсь с Arduino через Serial.read, а затем «ввожу файл lorem.txt», и у Arduino создается впечатление, что я печатаю очень быстро и очень длинные тексты. (Serial.read просто получит много символов - содержимое файла), @gilhad

Я использую Gentoo Linux, а также использую последовательную связь с другими 8-битными компьютерами, где мне иногда нужно отправить несколько килобайт шестнадцатеричного кода (Intel Hex), чтобы было удобнее, а затем просто скопировать и вставить текст в окно терминала., @gilhad

picocom -b 115200 -s cat /dev/ttyUSB0 работает (-s cat), а затем Ca Cs ./test_serial.002.0200.sxhex, @gilhad