Почему Serial.readString() ждёт целую секунду?
Обновлено Редакция GAW
Serial на Arduino — объект класса HardwareSerial, который наследует Stream. Все «удобные» функции чтения — readString(), readStringUntil(), readBytes(), parseInt(), parseFloat() — живут в Stream и ждут каждый следующий байт не дольше таймаута. В конструкторе Stream() ядра 1.8.8 он равен 1000 мс.
Когда возвращаются функции чтения
| Функция | Когда возвращает результат |
|---|---|
readString() | только когда новый байт не пришёл за таймаут — всегда не раньше чем через 1000 мс после последнего символа |
readStringUntil('\n') | сразу по символу конца строки; если его нет — по таймауту |
readBytes(buf, n) | когда принято n байт или вышел таймаут |
parseInt(), parseFloat() | когда после числа пришёл нецифровой символ или вышел таймаут |
Serial.read() | сразу: байт из буфера или −1 |
Отсюда секундная задержка: у readString() нет признака конца, и она всегда дожидается тишины. Монитор порта Arduino IDE умеет дописывать перевод строки, поэтому чаще всего достаточно заменить вызов на readStringUntil('\n') и включить этот режим.
Приём строки без ожидания
char line[32];
uint8_t len = 0;
void loop() {
while (Serial.available()) { // take what is already in the buffer
char c = Serial.read();
if (c == '\n') { // end of line: handle it
line[len] = '\0';
handleCommand(line);
len = 0;
} else if (c != '\r' && len < sizeof(line) - 1) {
line[len++] = c; // drop characters beyond the buffer
}
}
// the rest of loop() keeps running between characters
}
Такой разбор не блокирует loop() и не создаёт объектов String, которые на ATmega328P с его 2 КБ ОЗУ фрагментируют кучу. Как байты попадают в 64-байтный кольцевой буфер и почему он теряет данные, разобрано в уроке «Serial изнутри» и статье «Кольцевой буфер для UART». Обратная сторона — почему тормозит передача — в ответе «Serial.print замедляет программу».
Похожие вопросы
Какой таймаут ставить вместо 1000 мс?
Чуть больше паузы между символами одной посылки. На 9600 бит/с байт с одним стоп-битом передаётся около 1 мс, на 115 200 — около 87 мкс, так что при непрерывной посылке хватает 10–50 мс. Если отправитель делает паузы внутри строки — например, человек печатает в мониторе порта с отправкой по символу, — маленький таймаут разрежет строку.
Почему parseInt() возвращает 0?
parseInt() ждёт цифру тем же таймаутом и, не дождавшись, возвращает 0. Так же выглядит и честный ноль в данных, поэтому отличать их приходится по Serial.available() до вызова или переходить на собственный разбор.
Может ли readString() потерять данные?
Приёмный буфер она вычитывает сама, так что переполнить его ей сложно. Риск в другом: каждый символ дописывается в объект String, и при этом буфер строки перевыделяется через realloc ровно под новую длину. У ATmega328P всего 2 КБ ОЗУ; если памяти не хватит, concat() по исходнику WString.cpp вернёт 0, и символ молча пропадёт. Для длинных посылок надёжнее свой массив фиксированного размера.
Источники
- Arduino. ArduinoCore-avr 1.8.8: cores/arduino/Stream.h, Stream.cpp — Stream.h: конструктор Stream() { _timeout = 1000; }, комментарий — число миллисекунд ожидания следующего символа; Stream.cpp: timedRead() ждёт байт, пока millis() − _startMillis < _timeout, и возвращает −1 по истечении; readString() читает timedRead() до −1; readStringUntil(terminator) — до −1 или до символа-терминатора, сам терминатор не включает; parseInt() и parseFloat() пропускают символы и ждут через тот же timedPeek(); setTimeout(timeout) меняет _timeout
- Arduino. ArduinoCore-avr 1.8.8: cores/arduino/WString.cpp — concat(cstr, length) вызывает reserve(newlen); reserve → changeBuffer: realloc(buffer, maxStrLen + 1) ровно под новую длину; если realloc не удался, reserve и concat возвращают 0, и строка не меняется; concat(char c) дописывает один символ через тот же путь
- Arduino. ArduinoCore-avr 1.8.8: cores/arduino/HardwareSerial.h — HardwareSerial наследует Stream; SERIAL_RX_BUFFER_SIZE — 64 байта при ОЗУ не меньше 1 КБ
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — Features: 512/1K/1K/2K байт встроенной SRAM для ATmega48/88/168/328 — у ATmega328P 2 КБ
Нашли ошибку?
Смотрите также
- Serial и USART0Serial.begin, U2X0, исключение 57600, буферы и прерывания
- Кольцевой буфер UARTБуфер SPSC с маской, ISR приёма и передачи на AVR и STM32, ORE и DOR, размер
- Почему Serial.print замедляет программу Arduino?Буфер 64 байта полон — print ждёт линию; на 9600 бод ~1 мс на байт
- UARTКадр 8N1, скорость и допуск, TX и RX, уровни, USB–UART, AVR и STM32
- Скорость UARTUBRR, BRR, TH1 и ошибка скорости