SoftwareSerial как не терять символы.
- Войдите на сайт для отправки комментариев
Ср, 27/06/2018 - 13:41
Здравствуйте, снова я, и проблемма заключается в следующем, куча кода из него кусок который считывает по софтсериал команды с модема, записывает в строку, так вот нередко символы в строку записывалось только часть команды (считывает порт а там пусто) и выходит из цыкла чтения, посылает строку на обработку, но там естественно не нужной информации полностью, так вот я решил это небольшой задержкой "делеем", может кто нибудь посоветовать что нибудь лучше, ох уж негатив прет от delay...
if (MY_GSM.available() > 0) { //есть данные от GSM модуля
i = 0;
while (i < 131) // тут обнуляем биты(нужно для других задач массив)
{ buf[i] = '\0';
i++;
}
val = "";
i = 0;
boolean print_available = 1; // переменная для сброса
while (print_available == 1) { // выполняем пока после проверки не увидели что буфер чист
if (MY_GSM.available() > 0) { // //есть данные от GSM модуля
ch = MY_GSM.read(); // записываем
buf[i] = ch; // присваиваем в массив
val += ch; // присваиваем в строку
i++; // увеличили число для массива
}
else if (MY_GSM.available() == 0) // если увидели что кончились данные
{
delay(30); // подождем еще немного
if (MY_GSM.available() == 0) // еще раз посмотрели что данные кончились
{
print_available = 0; // выключили цыкл
}
}
}
buf[i] = '\0'; // поставили последний символ в буфер
i = 0; // сбросили
Serial.println(val); //напечатали че получилось
}
надо заканчивать чтение не по признаку "данные в сериал кончились", а по получении символов конца строки "\r\n". Пока не получите конец строки - из цикла чтения не выходите.
...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.
...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.
это второй уровень уже :) сначала конец строки...
надо заканчивать чтение не по признаку "данные в сериал кончились", а по получении символов конца строки "\r\n". Пока не получите конец строки - из цикла чтения не выходите.
т.е. модем обязательно присылает в конце "\r\n"??
...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.
не понял? типа поставить еще условие если время кончилось то выходить из while?
не понял? типа поставить еще условие если время кончилось то выходить из while?
Ну, а как вы считаете: если модем не хочет вам отвечать - это повод бесконечно крутится в цикле, не совершая иных полезных и необходимых действий?
Ну, а как вы считаете: если модем не хочет вам отвечать - это повод бесконечно крутится в цикле, не совершая иных полезных и необходимых действий?
так модем тут причем? софваресериал когда то, да очистится, потому что функция опустошения есть, не могу найти ни одного варианта что бы поостоянно что то присутствовало в сериал, ведь даже помехи на RX даже не смогут повесить эту функцию, т.к. есть стартовый и конечный бит. что помехми сделать 1 шанс на миллион, и то этот бит просто очистится и забудится
это второй уровень уже :) сначала конец строки...
А третий уровень - сделать правильно :)))
Признак конца команды оставить на месте, он нужен.
Функция чтения вовсе не обязана ждать конца команды, она просто вычитывает всё, что есть, и если встретила конец команды возвращает истину, если пока ещё нет - ложь. В зависимости от того, что она вернула, вызывающая программа обрабатывает команду или просто занимается своими делами.
Этим Вы убьёте сразу двух зайев:
1. никаких задержек, программа постоянно делает свои дела (там что её надо делать - кнопки опрашивает или еще чего)
2. В случае если две (или более) команды "слиплись" в одну посылку, Вы всегда чётко различите их.
Структурно это выглядит примерно так:
#include <SoftwareSerial.h> void setup() { } SoftwareSerial mySerial(3,4); static const char commandEndTrigger = '\n'; // признак конца команды ('\n' - для примера) static const int maxCommandLength = 32; // максимально возможная длина команды + 1 (32 - для примера) static char commandBuffer[maxCommandLength]; // буффер для накапливания поступившей команды) static int bufPointer = 0; // первое свободное место в буффере bool readModem(void) { while (mySerial.available()) { const char c = mySerial.read(); if (c == commandEndTrigger) { // если получен признак конца команды commandBuffer[bufPointer++] = '\0'; // сам признак выбрасываем, а в буффер помещаем признак конца строки return true; // Выходим, сообщая, что команда получена полностью. // При этом не читаем новые символы, даже если они там есть. // Они от следующей команды - потом прочитаем, при слежующем вызове } commandBuffer[bufPointer++] = c; // // По уму здесь нужна праверка на переполнения буффера. // Сами сделаете } return false; } void loop(void) { if (readModem()) { // если комнда полностью поступила // здесь обрабатываем поступившую команду // При этом ОБЯЗАТЕЛЬНО нужно очистить буффер для новых команды // для очистки буффера достаточно сьросить в 0 указатель на свободное место bufPointer = 0; } // // здесь делаем свои дела и никаких тормозов }еще немного усложнил но добавил скорость, что бы не ждать четко 30мс, вдруг за эти 30мс заполнится буфер, и потеряются символы, добавил еще одну функцию
if (MY_GSM.available() > 0) { //есть данные от GSM модуля i = 0; while (i < 131) // тут обнуляем биты(нужно для других задач массив) { buf[i] = '\0'; i++; } val = ""; i = 0; boolean print_available = 1; // переменная для сброса while (print_available == 1) { // выполняем пока после проверки не увидели что буфер чист if (MY_GSM.available() > 0) { // //есть данные от GSM модуля ch = MY_GSM.read(); // записываем buf[i] = ch; // присваиваем в массив val += ch; // присваиваем в строку i++; // увеличили число для массива } else if (MY_GSM.available() == 0) // если увидели что кончились данные { // добавил---------------- int inter = 0; boolean program_set = 1; while (program_set == 1) { inter++; delay(inter); if (inter > 10 || MY_GSM.available() > 0) { program_set = 0; } } // добавил^^^^^^^^^^^^^^^^^^^^ if (MY_GSM.available() == 0) // еще раз посмотрели что данные кончились { print_available = 0; // выключили цыкл } } } buf[i] = '\0'; // поставили последний символ в буфер i = 0; // сбросили Serial.println(val); //напечатали че получилось }это второй уровень уже :) сначала конец строки...
А третий уровень - сделать правильно :)))
Признак конца команды оставить на месте, он нужен.
Функция чтения вовсе не обязана ждать конца команды, она просто вычитывает всё, что есть, и если встретила конец команды возвращает истину, если пока ещё нет - ложь. В зависимости от того, что она вернула, вызывающая программа обрабатывает команду или просто занимается своими делами.
Этим Вы убьёте сразу двух зайев:
1. никаких задержек, программа постоянно делает свои дела (там что её надо делать - кнопки опрашивает или еще чего)
2. В случае если две (или более) команды "слиплись" в одну посылку, Вы всегда чётко различите их.
Структурно это выглядит примерно так:
#include <SoftwareSerial.h> void setup() { } SoftwareSerial mySerial(3,4); static const char commandEndTrigger = '\n'; // признак конца команды ('\n' - для примера) static const int maxCommandLength = 32; // максимально возможная длина команды + 1 (32 - для примера) static char commandBuffer[maxCommandLength]; // буффер для накапливания поступившей команды) static int bufPointer = 0; // первое свободное место в буффере bool readModem(void) { while (mySerial.available()) { const char c = mySerial.read(); if (c == commandEndTrigger) { // если получен признак конца команды commandBuffer[bufPointer++] = '\0'; // сам признак выбрасываем, а в буффер помещаем признак конца строки return true; // Выходим, сообщая, что команда получена полностью. // При этом не читаем новые символы, даже если они там есть. // Они от следующей команды - потом прочитаем, при слежующем вызове } commandBuffer[bufPointer++] = c; // // По уму здесь нужна праверка на переполнения буффера. // Сами сделаете } return false; } void loop(void) { if (readModem()) { // если комнда полностью поступила // здесь обрабатываем поступившую команду // При этом ОБЯЗАТЕЛЬНО нужно очистить буффер для новых команды // для очистки буффера достаточно сьросить в 0 указатель на свободное место bufPointer = 0; } // // здесь делаем свои дела и никаких тормозов }а если модем затупит и не пришлет признак конца команды?
Посмотрите как у меня сделано - проще, короче и никакиж задержек вовсе.
Здесь же у Вас слишком сложная логика и ляпы абсолютно неизбежны.
И да, кстати, Вы полностью уверены, что Вам реально нужно забить весь массив нулями (строки 3-6)? Если это чистка от старого, то реально не нужно, достаточно одного нуля в конце новой строки. Если же это таки реально нужно, то для этого существует функция memset (гуглите)
Программа будет заниматься своими делами. Задержек-то в ней нет. Просто readModem всегда будет возвращать false.
На практике
while(mySerial.available())способен наподкидывать разных подлянок (его же не все применяют с умом). Поэтому лично я прекращаю прием(парсинг) или по терминатору или по достижению конца буфера или по таймауту. Последнее так же помогает прекратить болтать с тормозом на том конце линии, который решил применить к твоему девайсу slow lori attack.И да, кстати, Вы полностью уверены, что Вам реально нужно забить весь массив нулями (строки 3-6)? Если это чистка от старого, то реально не нужно, достаточно одного нуля в конце новой строки. Если же это таки реально нужно, то для этого существует функция memset (гуглите)
да нужно, я считываю не с первого символа иногда, и с 3-5 с 12 даже.
memset (гуглите)
спасибо, посмотрю, не разу не слышал ничего подобного
Программа будет заниматься своими делами. Задержек-то в ней нет. Просто readModem всегда будет возвращать false.
ну тоже верно, впринципе меня "не полная" команда - не устроит.. но с дургой стороны, не придет конец строки, допустим я отсылаю смс, с командой снять с охраны, строка содержит номер, команду, но вот конец строки оборвался,я забыл что смс не дошла, ну в общем ничего не делаю, открыл допустим с брелка. (а команда то в буфере еще хранится) и тут оператор присылает смс, и конец строки доходит, и команда которая хранилась в буфере, начинает обрабатыватся и снимает авто с охраны
На практике
while(mySerial.available())способен наподкидывать разных подлянокВ моём коде его можно смело заменить на if (mySerial.available()), всё будет работать точно также.
Ну это уже вопрос логики программы - думайте. Я показал Вам как читать без задержек, а дальше ...
Например, при поступлении самого первого символа в буффер (когда указатель был нулём) Вы можете запоминать миллис, и если в течении 10 секунд, скажем, команда так и не сформировалась - принудительно очищать буффер (сбрасывать указатель в 0). Это уже дело Вашей логики - безтормозное чтение я Вам показал - оно по любому работает,а дальше думайте.
На практике
while(mySerial.available())способен наподкидывать разных подлянокВ моём коде его можно смело заменить на if (mySerial.available()), всё будет работать точно также.
Вобщем-то я так и делаю, так как по пути еще и парсинг провожу.
безтормозное чтение я Вам показал
чет не понял, это функция будет выполнятся вне зависимости от того чем занимается процессор? ноподобие "прерывания" или "таймера"?
Нет, Там же в примере всё есть - она просто вызывается при каждом проходе loop. Разумеется, если Вы в loop поставите delay(100500), она не будет вызываться. Если уже делать по уму, делать по уму всё, а не одну только функцию. Я просто показал Вам как читать сериал не используя задержек.
Нет, Там же в примере всё есть - она просто вызывается при каждом проходе loop. Разумеется, если Вы в loop поставите delay(100500), она не будет вызываться. Если уже делать по уму, делать по уму всё, а не одну только функцию. Я просто показал Вам как читать сериал не используя задержек.
вот теперь допонял доконца, спасибо, буду тестировать оба варианта, когда соберу железо, какой будет лучше тот и оставлю), как всегда спасибо вам большое!