SoftwareSerial как не терять символы.

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

Здравствуйте, снова я, и проблемма заключается в следующем, куча кода из него кусок который считывает по софтсериал команды с модема, записывает в строку, так вот нередко символы в строку записывалось только часть команды (считывает порт а там пусто) и выходит из цыкла чтения, посылает строку на обработку, но там естественно не нужной информации полностью, так вот я решил это небольшой задержкой "делеем", может кто нибудь посоветовать что нибудь лучше, ох уж негатив прет от 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); //напечатали че получилось
}

 

b707
Offline
Зарегистрирован: 26.05.2017

надо заканчивать чтение не по признаку "данные в сериал кончились", а по получении символов конца строки "\r\n". Пока не получите конец строки - из цикла чтения не выходите.

sadman41
Offline
Зарегистрирован: 19.10.2016

...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.

это второй уровень уже :) сначала конец строки...

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

b707 пишет:

надо заканчивать чтение не по признаку "данные в сериал кончились", а по получении символов конца строки "\r\n". Пока не получите конец строки - из цикла чтения не выходите.

т.е. модем обязательно присылает в конце "\r\n"??

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

sadman41 пишет:

...или выходите по таймауту, если не хотите зависнуть со своей сигнализацией.

не понял? типа поставить еще условие если время кончилось то выходить из while?

sadman41
Offline
Зарегистрирован: 19.10.2016

d13lider пишет:

не понял? типа поставить еще условие если время кончилось то выходить из while?

Ну, а как вы считаете: если модем не хочет вам отвечать - это повод бесконечно крутится в цикле, не совершая иных полезных и необходимых действий?

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

sadman41 пишет:

Ну, а как вы считаете: если модем не хочет вам отвечать - это повод бесконечно крутится в цикле, не совершая иных полезных и необходимых действий?

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:

это второй уровень уже :) сначала конец строки...

А третий уровень - сделать правильно :)))

Признак конца команды оставить на месте, он нужен.

Функция чтения вовсе не обязана ждать конца команды, она просто вычитывает всё, что есть, и если встретила конец команды возвращает истину, если пока ещё нет - ложь. В зависимости от того, что она вернула, вызывающая программа обрабатывает команду или просто занимается своими делами.

Этим Вы убьёте сразу двух зайев:

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;
	}
	//
	// здесь делаем свои дела и никаких тормозов
}

 

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

еще немного усложнил но добавил скорость, что бы не ждать четко 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); //напечатали че получилось
}

 

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

ЕвгенийП пишет:

b707 пишет:

это второй уровень уже :) сначала конец строки...

А третий уровень - сделать правильно :)))

Признак конца команды оставить на месте, он нужен.

Функция чтения вовсе не обязана ждать конца команды, она просто вычитывает всё, что есть, и если встретила конец команды возвращает истину, если пока ещё нет - ложь. В зависимости от того, что она вернула, вызывающая программа обрабатывает команду или просто занимается своими делами.

Этим Вы убьёте сразу двух зайев:

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;
	}
	//
	// здесь делаем свои дела и никаких тормозов
}

 

а если модем затупит и не пришлет признак конца команды? 

'\n'

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Посмотрите как у меня сделано - проще, короче и никакиж задержек вовсе.

Здесь же у Вас слишком сложная логика и ляпы абсолютно неизбежны.

И да, кстати, Вы полностью уверены, что Вам реально нужно забить весь массив нулями (строки 3-6)? Если это чистка от старого, то реально не нужно, достаточно одного нуля в конце новой строки. Если же это таки реально нужно, то для этого существует функция memset (гуглите)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d13lider пишет:

'\n'

Программа будет заниматься своими делами. Задержек-то в ней нет. Просто readModem всегда будет возвращать false.

sadman41
Offline
Зарегистрирован: 19.10.2016

На практике while (mySerial.available()) способен наподкидывать разных подлянок (его же не все применяют с умом). Поэтому лично я прекращаю прием(парсинг) или по терминатору или по достижению конца буфера или по таймауту. Последнее так же помогает прекратить болтать с тормозом на том конце линии, который решил применить к твоему девайсу slow lori attack.

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

ЕвгенийП пишет:

И да, кстати, Вы полностью уверены, что Вам реально нужно забить весь массив нулями (строки 3-6)? Если это чистка от старого, то реально не нужно, достаточно одного нуля в конце новой строки. Если же это таки реально нужно, то для этого существует функция memset (гуглите)

да нужно, я считываю не с первого символа иногда, и с 3-5 с 12 даже.

ЕвгенийП пишет:

memset (гуглите)

спасибо, посмотрю, не разу не слышал ничего подобного

ЕвгенийП пишет:

Программа будет заниматься своими делами. Задержек-то в ней нет. Просто readModem всегда будет возвращать false.

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:

На практике while (mySerial.available()) способен наподкидывать разных подлянок 

В моём коде его можно смело заменить на if (mySerial.available()), всё будет работать точно также.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d13lider пишет:
но с дургой стороны, не придет конец строки, допустим я отсылаю смс, с командой снять с охраны, строка содержит номер, команду, но вот конец строки оборвался,я забыл что смс не дошла, ну в общем ничего не делаю, открыл допустим с брелка. (а команда то в буфере еще хранится) и тут оператор присылает смс, и конец строки доходит, и команда которая хранилась в буфере, начинает обрабатыватся и снимает авто с охраны

Ну это уже вопрос логики программы - думайте. Я показал Вам как читать без задержек, а дальше ...

Например, при поступлении самого первого символа в буффер (когда указатель был нулём) Вы можете запоминать миллис, и если в течении 10 секунд, скажем, команда так и не сформировалась - принудительно очищать буффер (сбрасывать указатель в 0). Это уже дело Вашей логики - безтормозное чтение я Вам показал - оно по любому работает,а дальше думайте.

sadman41
Offline
Зарегистрирован: 19.10.2016

ЕвгенийП пишет:

sadman41 пишет:

На практике while (mySerial.available()) способен наподкидывать разных подлянок 

В моём коде его можно смело заменить на if (mySerial.available()), всё будет работать точно также.

Вобщем-то я так и делаю, так как по пути еще и парсинг провожу.

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

ЕвгенийП пишет:

 безтормозное чтение я Вам показал

чет не понял, это функция будет выполнятся вне зависимости от того чем занимается процессор? ноподобие "прерывания" или "таймера"?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нет, Там же в примере всё есть - она просто вызывается при каждом проходе loop. Разумеется, если Вы в loop поставите delay(100500), она не будет вызываться. Если уже делать по уму, делать по уму всё, а не одну только функцию. Я просто показал Вам как читать сериал не используя задержек.

d13lider
d13lider аватар
Offline
Зарегистрирован: 19.10.2015

ЕвгенийП пишет:

Нет, Там же в примере всё есть - она просто вызывается при каждом проходе loop. Разумеется, если Вы в loop поставите delay(100500), она не будет вызываться. Если уже делать по уму, делать по уму всё, а не одну только функцию. Я просто показал Вам как читать сериал не используя задержек.

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