Arduino Nano: Отправить несколько байтов по последовательному каналу с минимальной задержкой, насколько это возможно

Мой сценарий выглядит следующим образом:

У меня есть несколько кнопок, подключенных к Arduino Nano, который подключен к моему компьютеру через Serial. Теперь я хочу посылать несколько байтов всякий раз, когда нажимается кнопка, для интерпретации моим компьютером. Тем не менее, я заметил значительную, нерегулируемую задержку (кажется, что-то между почти мгновенной передачей до задержки легко ~300 миллисекунд), пока сообщение не будет получено моим компьютером, и я хочу избавиться от этого.

Я понимаю, что ардуино используют последовательный буфер и отправляют данные только после того, как буфер заполнен (или, возможно, какой - то тайм-аут), и я думаю, что это виновник-однако я не вижу способа заставить Arduino отправить данные как можно скорее. Я попытался просто отправить ровно 64 байта (что, согласно тому, что я прочитал, должно быть размером буфера), но это тоже не помогает (могу ли я уменьшить размер буфера или есть способ отправлять байты ровно до тех пор, пока буфер не заполнится?)

Я также не думаю, что baudrate что-то меняет, так как я испытываю проблемы с "пингом", а не с пропускной способностью (и я также пробовал что-то от 9600 до 115200).

Может быть, я тоже совершенно неправ, и задержка происходит на стороне моего компьютера, но я не знаю, есть ли что - нибудь, чтобы изменить это-я смотрю на последовательный монитор или на небольшой скрипт pyserial, оба из которых имеют задержку:

arduino = serial.Serial(serial_port}, 115200, timeout=.1)
while True:
    data = arduino.readline().decode("UTF-8").strip()
    if data:
        print(data)

Код на стороне Arduino сводится к следующему:

#include <digitalWriteFast.h>

#define button1_pin 2

int button1_state = 0;
int last_button1_state = 0;

void setup() {
  Serial.begin(115200);
  pinMode(button1_pin, INPUT_PULLUP); 
  while (!Serial) { delay(50); }
  Serial.println("Started");
}

void loop() {
  button1_state = digitalReadFast(button1_pin);
  
  if (button1_state != last_button1_state) {
    if (button1_state == LOW) {
      Serial.write("d\n");
    } else {
      Serial.write("u\n");
    }    
    last_button1_state = button1_state;
    delay(20);
  }
}

Кроме того есть еще одна странная вещь которую я заметил но не уверен что она вообще правдоподобна: Когда окно последовательного монитора (того, который поставляется с Arduino IDE) не полностью заполнено (это означает, что есть только некоторые строки 1-20, и окно не нужно прокручивать), я замечаю вышеупомянутую задержку, но если последовательный монитор заполнен (так что новый контент будет на экране). последняя строка и окно автоматически прокручивается немного), я этого не делаю. Как только я очищаю вывод окна, оно снова там. Я не знаю, что делать с этим наблюдением, потому что у меня определенно нет никакой разумной подсказки, почему это может быть так, так что, возможно, это тоже просто мое воображение :пожимание плечами:. При использовании python-кода такого поведения нет, и transimission всегда имеет пинг не менее 200-300 мс.

Я надеюсь, что моя проблема понятна, и, как я уже сказал, я действительно не уверен, что даже является виновником (и я вижу, что также весьма вероятно, что это на стороне моего компьютера), но есть ли у кого-нибудь идеи, как я могу уменьшить эту задержку? Большое спасибо!

, 👍0

Обсуждение

"отправляйте данные только после того, как буфер заполнен" - вы здесь неправильно думаете. Как только какой-либо байт оказывается в буфере отправки (помещается туда Serial.write() или его братьями и сестрами), передача начинается в фоновом режиме (организованном через прерывания). Baudrate также определяет, сколько времени требуется для передачи байта, поэтому он также в некотором смысле коррелирует с "пингом". Вы пробовали изменить значение тайм-аута в коде python? На данный момент у вас есть 0.1 (я думаю, это секунды). Меняет ли это что-нибудь в результате?, @chrisl

О, как приятно это знать, спасибо! Но это только смущает меня больше tbh ^^ И нет, изменение тайм-аута ничего не меняет. Из-за ответа @hcheung я попробовал другие значения timeout, но, как уже упоминалось, тайм-аут-это только максимальное время, когда один вызов readline () ждет, пока он не вернется, и ни увеличение, ни уменьшение что-то не меняет. Однако обратите внимание, что я вижу те же проблемы в последовательном мониторе Arduino IDE, поэтому я не думаю, что эта проблема на стороне " pyserial`., @Chris Stenkamp

Мой вопрос решен, спасибо за ваш вклад :), @Chris Stenkamp


2 ответа


3

"Замедление для ускорения". - китайская пословица

Проблема в вашем коде python. Не уверен, что такое операционная система вашего компьютера, но если вы используете Mac или Linux, если вы запустите top в командной строке, вы увидите, что у вас высокая загрузка процессора, вызванная вашим скриптом python. Это происходит потому, что ваш python в то время как True loop постоянно зацикливается, не давая возможности другим процессам на вашем компьютере использовать процессор, имейте в виду, что Linux, macOS или Windows не разработаны как операционная система реального времени.

Так как же решить эту проблему? поместите ваш цикл while True в спящий режим на короткий промежуток времени, это позволит другим процессам на вашем компьютере иметь возможность работать, и вы увидите, что загрузка процессора значительно снизится, и все будет работать гладко.

import time
arduino = serial.Serial(serial_port}, 115200, timeout=.1)
while True:
    data = arduino.readline().decode("UTF-8").strip()
    if data:
        print(data)
    time.sleep(0.01)

С этой небольшой задержкой я увидел, что загрузка процессора упала с 90% до 3,5%. Это довольно распространенная ошибка, которую допускают многие программисты на python, в том числе некоторые довольно опытные программисты, не знакомые с сетевым программированием.

Попробуйте и дайте мне знать.

,

Интересное предложение! Во - первых, я использую Linux. Во - вторых, я знаю разницу между активным и пассивным ожиданием, но я, насколько я понимаю, когда у меня есть последовательное соединение с таймаутом, это пассивное ожидание-насколько я понимаю документацию pyserial @ https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial , readline() возвращает либо если он что - то получает, либо тайм-аут (0,1 секунды в моем случае) - что означает, что time.sleep(0,01) является сверхплотным, так как он уже ждет 0,1 секунды на итерацию. У меня также есть только максимальная загрузка процессора 1% по этому сценарию., @Chris Stenkamp

Кроме того, я вижу те же проблемы на последовательном мониторе Arduino IDE, что заставило меня думать, что проблема в первую очередь на стороне Arduino, @Chris Stenkamp

Ха Ха а теперь к большому НО: Поскольку я не доверял тому, что видел в "печати" serial Monitor/python, я также написал еще один тестовый скрипт, который реагирует на щелчок, посылая последовательное сообщение обратно на Arduino, вызывая светодиод, и, насколько я мог судить, этот светодиод имел ту же задержку, что и отпечатки... однако в потоке, который реагирует на нажатие кнопки, мой код был while True: if not led_queue.empty(): serial.write(led_queue.get()), который очень АКТИВНО ждет, и как только я добавил time.sleep(0.1) при таком методе светодиод реагировал мгновенно!!! Значит, ты все-таки прав!, @Chris Stenkamp

Тайм-аут отличается тем, что он предназначен для выхода из постоянного последовательного опроса. Arduino-это MCU, который архитектурно отличается от CPU и предназначен для запуска суперцикла без особых фоновых процессов, работающих как Linux., @hcheung

Но дело в том, что когда я устанавливаю тайм-аут последовательного соединения, скажем, на две секунды, и добавляю "печать" в тело цикла (не зависимо от того, есть данные или нет), печать выполняется только каждые две секунды (или всякий раз, когда есть данные, отправленные с Arduino). - таким образом, тайм-аут действует так же, как и добавленный `time.sleep", @Chris Stenkamp


0

Отвечая на мой собственный вопрос на случай, если кто - нибудь в конце концов столкнется с той же проблемой: ответ @hcheung был почти правильным-см. Комментарии к этому вопросу.

Однако важный вынос: не доверяйте последовательному монитору или печати Python, чтобы проверить, как быстро arduino и ваш компьютер общаются!

Когда я вернулся к Arduino, чтобы позволить ему запустить светодиод, я заметил, что фактическая передача определенно быстрее, чем мой глаз, далеко от задержки 200 мс, о которой я упоминал в вопросе. Так что проблемы для меня были

а) другой код, который я использовал для проверки соединения, но не опубликовал здесь, был неисправен
б) Задержка, которую я видел в печати Serial Monitor/python, - это не задержка последовательной связи, а, по-видимому, где-то позже при печати соответствующего контента.

Последовательная связь определенно быстрее, чем человеческий глаз :)

,

TL;DR: Каждый раз, когда задействована ОС, оконная система и т. Д., Которые явно не являются жесткими в реальном времени, результаты не являются детерминированными по определению., @Dave Newton