SoftwareSerial читается до тех пор, пока не станет пустым, работает только с задержкой

Первоначально я собирался заставить свою программу работать только на ESP, пока она мне не понадобится на Arduino UNO (потому что я подозреваю, что мой ESP сломан). Мне пришлось добавить поддержку SoftwareSerial, но поскольку я изначально не собирался этого делать, строки, которые я отправляю из своего приложения, не имеют терминаторов. Я думал, что это будет хорошо, потому что я мог бы сделать что-то вроде:

void loop()
{
    while (ble.available() > 0)
    {
        data += (char)ble.read();
    }

    if (ble.available() <= 0 && data.length() > 0)
    {
        ble.println(data);
        //do stuff
        data = "";
    }
}

Все, что вы делаете, - это создаете строку из полученных символов. Затем он проверяет, не осталось ли чего читать, и материал уже был прочитан (т.е. Данные имеют длину), а затем распечатывает данные. Я ожидаю, что результат будет (если входная строка в 03:255,255,255):

03:255,255,255

На самом деле я получаю следующее:

0
3
:
2
5
5
,
//и так далее

Это потому, что по какой-то причине, даже если ble.available() должен возвращать число, а не 0 в операторе if , он возвращает 0, и поэтому оператор всегда будет истинным. Единственный способ исправить это - добавить небольшую задержку в конце цикла. Будь то:

delay(10)

или даже это, в цикле while:

Serial.println(ble.available()) //Это как-то вводит достаточную задержку

Без необходимости изменять мое приложение, могу ли я сделать это без задержки? С задержками это кажется хакерским, особенно потому, что, насколько я знаю, для старых или новых устройств эта задержка может быть слишком высокой или слишком низкой.

, 👍1


1 ответ


Лучший ответ:

4

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

Теперь с этой информацией посмотрите на свой код:

void loop()
{
    while (ble.available() > 0)
    {
        data += (char)ble.read();
    }

    if (ble.available() <= 0 && data.length() > 0)
    {
        ble.println(data);
        // делать что-то
        data = "";
    }
}

Предположим, Arduino получает один байт, затем происходит небольшая пауза, затем принимаются следующие данные. Код войдет в цикл while и считает полученный байт из последовательного буфера. Чтобы остаться в цикле while, следующий байт должен быть получен во время выполнения кода внутри цикла while. Это невероятно коротко. Таким образом, после одного байта внутри буфера больше ничего нет, поэтому цикл while завершается, потому что ble.available() возвращает ноль. Затем выполняется оператор if, также довольно быстро. Скорее всего, ble.available() все равно вернет ноль. Но вы уже поместили символ в буфер, поэтому оператор if выполняется.

Задержка решает вашу проблему, потому что тогда вы читаете намного медленнее, чем получаете. Данные в основном принимаются во время задержки. Затем остальная часть вашего кода может прочитать то, что уже было получено и помещено в буфер.

** Что делать вместо delay(): Вам нужно убедиться, что вы всегда читаете полное сообщение. Существуют разные способы, в зависимости от ваших требований. Если ваши сообщения всегда имеют одинаковую длину, вы можете подождать, пока не будет получено указанное количество символов, и только потом прочитать их. Но это не удастся, если вы по какой-то причине (например, сброс Arduino) получите частичное сообщение, которое вызовет рассогласование. Вы можете очистить буфер приема, когда прочитаете полное сообщение, это может помочь, если ваши передачи данных происходят нечасто.

Вы можете попытаться проанализировать входящие данные в соответствии с форматом сообщения (который должен оставаться постоянным для этой цели), например:

Прочтите номер.
Если за ним следует двоеточие, то это новое сообщение.
Затем прочитайте номер
    читать запятую
    считанное число
    читать запятую
    считанное число
Повторите.

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


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

Примечание:

  • Ничто из вышеперечисленного не является специфичным для SoftwareSerial. То же самое относится и к аппаратному сериалу.
  • Я подозреваю, что код работал с вашим ESP, потому что у вас был какой-то другой код, который вводил достаточно большую задержку (точно так же, как ваш вызов delay()).
,

Ну ладно ладно это имеет смысл! Мне нравится идея кругового буфера, где я могу просто удалить то, что я уже проанализировал, но, к сожалению, формат непоследователен, как и длина. Я думаю, что самый простой способ - просто сделать так, чтобы строки имели символы терминатора. Но все же спасибо за ответ!, @DreamingInsanity

@DreamingInsanity Если вы думаете, что мой ответ правильный, вы можете принять его как правильный и таким образом отметить вопрос как ответ., @chrisl

Да, я виноват! Я забыл об этом вчера вечером., @DreamingInsanity