Что происходит с последовательным выводом, когда никто не слушает?
Этот вопрос, вероятно, нуждается в лучшем названии, но я не могу его придумать! Пожалуйста, не стесняйтесь редактировать или предлагать свой вариант.
Моей целевой системой является пара Arduino Leonardo, соединенных радиомодулями XBee. Один из них является координатором и должен обмениваться данными с хост-компьютером через соединение USB (виртуальный последовательный порт). Координатор анализирует команды и потенциально пересылает их на конечную точку по радио. Конечная точка отвечает на команды, а также может спонтанно генерировать выходные данные. Оба устройства также могут отправлять диагностические данные на USB/последовательный порт. Конечная точка отправляет сообщения пульса каждые 8 секунд. Если координатор пропускает 2 последовательных пульса, он повторно инициализируется. Если конечная точка отправляет пульс и не получает ответа в течение нескольких секунд, она также будет повторно инициализирована.
Кажется, все это работает довольно хорошо, но я заметил кое-что странное. Если я подключаюсь к обоим устройствам с помощью эмулятора терминала и просто смотрю вывод, то все хорошо и система работает стабильно не менее 14 часов (самое долгое время, которое я тестировал). Я вижу, как происходят сообщения о сердцебиении и выводятся диагностические данные. Однако, как только я отключаю один из последовательных портов, все начинает глючить. Автомат связи XBee истекает по тайм-ауту с обеих сторон и переходит в бесконечный цикл повторной инициализации.
Я изо всех сил пытаюсь понять, почему это может быть, но отключение одного из последовательных портов надежно вызывает проблему в течение 10–20 секунд (я думаю, что истекло время ожидания «нет пульса»).
Похоже, что если ничего не прослушивает последовательный порт, значит, что-то заклинило. Насколько я понимаю, можно читать и писать в порт, к которому ничего не подключено, и эти данные просто попадут в битбакет. Разве это не так? Почему при отключении клиента от последовательного порта все должно зависать?
ОБНОВЛЕНИЕ: я протестировал этот модифицированный скетч "blink":
#include <Arduino.h>
auto& host = Serial;
void setup() {
// инициализируем цифровой вывод LED_BUILTIN как выход.
pinMode(LED_BUILTIN, OUTPUT);
host.begin(115200);
}
// функция цикла запускается снова и снова навсегда
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // включаем светодиод (HIGH - уровень напряжения)
delay(1000); // ждем секунду
digitalWrite(LED_BUILTIN, LOW); // выключаем светодиод, понижая напряжение
delay(1000); // ждем секунду
host.print("Time ");
host.println(millis());
}
Когда что-то (PuTTY) подключено к USB/последовательному порту, я получаю хорошее равномерное соотношение меток и пробелов на светодиоде. Как только я отключаю PuTTY, период «выключения» светодиода становится примерно в два раза длиннее периода «включения», т.е. каждый раз, когда я пишу в последовательный порт, возникает задержка более секунды. Это нормально?
@Tim Long, 👍2
Обсуждение2 ответа
Лучший ответ:
Хорошо, вот мое решение.
Я создал класс-оболочку с именем SafeSerial
. В SafeSerial.h
:
#pragma once
#include <Arduino.h>
class SafeSerial : public Serial_
{
size_t write(const uint8_t* buffer, size_t size) override;
};
В SafeSerial.cpp
:
/*
* Реализация SafeSerial для Arduino Leonardo.
* Переопределяет метод Serial_::write() и проверяет, достаточно ли места
* в выходной буфер. Если нет, вывод отбрасывается.
* Цель состоит в том, чтобы предотвратить блокировку последовательного вывода, когда хост не прослушивается.
* Все остальные операции передаются базовому экземпляру Serial_.
*/
#include "SafeSerial.h"
size_t SafeSerial::write(const uint8_t* buffer, size_t size)
{
/*
* Проверьте, можем ли мы писать без блокировки. Если нам нужно заблокировать,
* то мы предполагаем, что хост отключился.
*/
if (availableForWrite() < size)
return 0;
return Serial_::write(buffer, size);
}
В моем скетче в глобальном масштабе:
SafeSerial host;
Теперь, поскольку я использую <ArduinoSTL>
, и все мои выходные данные проходят через std::cout
, мне нужно предпринять некоторые действия, чтобы правильно настроить эту библиотеку. . Для этого необходимо изменить код библиотеки в соответствии с инструкциями в файле README.md
, чтобы он не использовался по умолчанию для встроенного экземпляра Serial
и не создавал экземпляры по умолчанию для cin
и cout
. Теперь это нужно сделать в скетче, поэтому (опять же в глобальном масштабе):
// cin и cout для ArduinoSTL
namespace std
{
ohserialstream cout(host);
ihserialstream cin(host);
}
и в setup()
:
host.begin(115200);
// Подключаем cin и cout к нашему экземпляру SafeSerial
ArduinoSTL_Serial.connect(host);
... и, наконец, у меня есть полностью неблокирующий последовательный ввод-вывод, даже когда хост отключен, с риском некоторой возможной потери данных. Однако до сих пор в моем приложении потеря данных не казалась проблемой, поскольку я вывожу только минимальный краткий вывод и, как правило, только 1 строку каждые несколько секунд.
Спасибо всем за ценный вклад.
Теоретически с последовательными данными ничего не должно происходить, если ничего не прослушивается.
- Для традиционного UART данные просто отправляются в любом случае. Для этого не нужно, чтобы кто-то слушал. Как свету не нужно, чтобы кто-то его видел, чтобы он зажегся.
- Для USB CDC/ACM данные должны быть удалены еще до попытки отправки.
Последнее, однако, полагается на то, что хост-компьютер «играет в мяч» и корректно закрывает порт. В большинстве операционных систем это обрабатывается ОС и происходит автоматически. Однако в Windows он передается драйверу, и я думаю, что драйвер может даже передать его терминальному программному обеспечению.
Таким образом, в Windows существует вероятность того, что при отключении последовательного порта соединение не закрывается полностью, а USB считает, что он все еще подключен, что блокирует ожидание сбора последовательных данных.
Я обновил свой вопрос, добавив дополнительную информацию и простой тестовый набросок. Когда я отключаю последовательный порт, я вижу что-то вроде секундной задержки каждый раз вокруг моего цикла., @Tim Long
Хорошо, попробуем под другим углом. Похоже, у меня проблема с блокировкой последовательного вывода, хотя к последовательному потоку ничего не подключено. Если бы мне пришлось написать новый класс потока, который обертывает последовательный поток, возможно, я мог бы выполнить ручную проверку, чтобы увидеть, следует ли мне записывать данные в базовый поток или просто отбрасывать их. В этой ситуации, что, по-вашему, я должен проверить?, @Tim Long
@TimLong Ну, единственное, что вы действительно можете проверить, - это «состояние линии» CDC / ACM - и существующий код уже делает это., @Majenko
Вы можете попробовать посмотреть возвращаемое значение Serial.availableForWrite()
, чтобы увидеть, достаточно ли места в буфере..., @Majenko
https://www.arduino.cc/reference/tr/language/functions/communication/serial/availableforwrite/, @Majenko
Похоже, стоит попробовать. Спасибо за отзыв., @Tim Long
- Может ли Arduino Leonardo одновременно использовать USB-порт и последовательные контакты RX TX?
- Mac не обнаруживает последовательный порт для Леонардо
- Проблема с загрузкой скетча в Леонардо
- Arduino Leonardo на Win 8.1 COM-порт пропал
- Последовательные порты на Arduino UNO/Leonardo инициализируются по-разному?
- Как отлаживать ввод MIDI?
- Как определить, какая скорость передачи данных установлена для виртуального COM-порта?
- Arduino Leonardo не печатает ничего на последовательный монитор с моим конкретным кодом
Это зависит от того, какие у вас загадочные устройства, которые обмениваются данными через USB, и как они запрограммированы., @Majenko
@Majenko, [ардуино-леонардо], @Juraj
@Juraj Я никогда не доверяю тегам., @Majenko
добавьте некоторый код включения / выключения светодиода, чтобы облегчить отладку ... например, светодиод горит перед последовательной связью и светодиод выключается после последовательной связи, @jsotola
Вы тоже по сериалу читаете? В конце концов (отключение) последовательного порта вызывает перепад на приемной линии, что приводит к произвольному значению. В зависимости от того, как вы управляете последовательными входами на Arduino, может случиться что угодно., @Sim Son
@jsotola это отличная идея, думаю, я так и сделаю., @Tim Long
@SimSon да, знаю (читай из сериала). Мой синтаксический анализатор команд довольно защищен от бомб, поэтому это не должно быть проблемой., @Tim Long
Arduino Leonardo использует встроенный USB для создания «последовательного порта». Вероятно, поэтому его print ведет себя не так, как Arduino UNO. UNO имеет отдельную микросхему для виртуального ком., @Fahad