Построчный парсинг и SerialEvent

IgorKa
Offline
Зарегистрирован: 06.11.2016

Еще раз доброе время суток всем!

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

Решил использовать прерывания в виде serialEvent(). Посмотрев пример вот что у меня получилось:

#include <SoftwareSerial.h>

int sinhro = 0;
String bufer = "";
String string2 = "ADSL link down\n";
String stringOne = "ADSL2/ADSL2+ connection\n";

char* AT_command_string = "admin";        //логин и пароль
char* AT_command_string2 = "adsl info --show";   // команда

String inputString = "";
boolean stringComplete = false;

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  Serial.begin(115200);
  mySerial.begin(9600);
  pinMode(8, INPUT_PULLUP);
 
}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    //  mySerial.println(inputString);
    }
  }
}


void loop() {
 // read_serial();



  if (digitalRead(8) == LOW) {
    digitalWrite(13, HIGH);
    sinhro = 1;
  }


  if (sinhro == 1) {
    delay(1000);
    Serial.write(0x0A);            // переход на новую строку (в некоторых модемах можно не выполнять)
    delay(2000);                   // ждем 2 сек.
    Serial.write(AT_command_string);// отправляем логин
    Serial.write(0x0A);             // переход на новую строку
    delay(2000);                    // ждем 
    Serial.write('\n');// отправляем пароль
    //  Serial.write(0x0A);             // переход на новую строку
    delay(2000);                    // ждем 
    Serial.flush();                 // очищаем буфер
    Serial.write(AT_command_string2); // отправляем команду
    Serial.write(0x0A);               // переход на новую строку
    sinhro = 0;
    

if (stringComplete) {
    mySerial.println(inputString);
   
   // очищаем строку:
    inputString = "";
    stringComplete = false;
 
  }

serialEvent();
  
  }
}

Пока пытаюсь просто разобрать прилетающую от модема в Serial информацию по строкам и вывести все это в softSerial. Моделирую  в Proteuse через подключенный к com-порту ПК модем.

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

Помогите разобраться где я накосячил

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Вы не задумывались, что с модема может прилетать '\n' не вконце строки, а в начале ?

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

brokly пишет:

Вы не задумывались, что с модема может прилетать '\n' не вконце строки, а в начале ?

я запретил думать

IgorKa
Offline
Зарегистрирован: 06.11.2016

О, великий Клапауций, прости засранца , но я не знал о твоем запрете и случайно взял и подумал. Вот лог снятый в Putty и открытый в Notepad++ с отображением всех символов

Клапауций 234
Offline
Зарегистрирован: 24.10.2016

каждая строка у тебя оканчивается двумя символами - '\r', '\n'

IgorKa
Offline
Зарегистрирован: 06.11.2016

в каждой строке '\n' идет последним? Так ведь и в коде идет проверка

if (inChar == '\n') {
      stringComplete = true;}

и если условие  выполняется то считается что строка принята. А '\r' , если я правильно понимаю,  просто дописывается к содержимому строки. Но пока не пойму как это влияет на описанные косяки?

IgorKa
Offline
Зарегистрирован: 06.11.2016

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

#include <SoftwareSerial.h>

int i=0;
int s=0;
int sinhro = 0;
String bufer = "";
String string2 = "ADSL link down\n";
String stringOne = "ADSL2/ADSL2+ connection\n";

char* AT_command_string = "admin";        //логин и пароль
char* AT_command_string2 = "adsl info --show";   // команда


String inputString = "";
boolean stringComplete = false;


SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  Serial.begin(115200);
  mySerial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(8, INPUT_PULLUP);
 
}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();//delayMicroseconds(100);
if (inChar == '\n') {  i++; //считаем строки
    
}
      if ((i>=15 && i<=16) || (i>=18 && i<=20) ||(i>=34&&i<=39)|| i==59) // пишем строки 13-20,34-39 и 59
      {
inputString += inChar;
   if (inChar == '\n') { stringComplete = true; }
   
}
}
}


void loop() {
 
  if (digitalRead(8) == LOW) {
    digitalWrite(13, HIGH);
    sinhro = 1;
  }


  if (sinhro == 1) {
    delay(1000);
    Serial.write(0x0A);            // переход на новую строку (в некоторых модемах можно не выполнять)
    delay(2000);                   // ждем 2 сек.
    Serial.write(AT_command_string);// отправляем логин
    Serial.write(0x0A);             // переход на новую строку
    delay(2000);                    // ждем секунду
    Serial.write('\n');// отправляем пароль
    //  Serial.write(0x0A);             // переход на новую строку
    delay(1000);                    // ждем секунду
    Serial.flush();                 // очищаем буфер

    Serial.write(AT_command_string2); // отправляем команду
    Serial.write(0x0A);               // переход на новую строку
    sinhro = 0;

    serialEvent();

if (stringComplete) {
    mySerial.println(inputString);
    mySerial.print("inputString length=");mySerial.println(inputString.length());

    
    // очищаем строку:
    inputString = "";
    stringComplete = false;
    i=0;
    while(Serial.available()) // очищаем буфер
  {  delay(2); Serial.read();
  }
  }
  }
}

Помогите все-таки разобраться почему

1. inputString выводится не построчно, а одной строкой. Вот результат вывода inputString.length()

inputString length=208

2. Почему возникает задержка с выводом (такое впечатление что при первом нажатии кнопки инфа пишется в буфер, и только при втором нажатии считывается из него)

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

советую попробовать все это принять не софтверсериалом, а хардварным, и о результате собщите.

мне кажется софтвер и может косячить, то успевает то не успевает  все принять.

и кстати скетч сколько памяти занимает? и поместится ли в буфер все, может банально не помещается?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

А почему не использовать это?

  if (Serial.available()) {
    String buffer;
    buffer = Serial.readString();
  }

 

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

qwone пишет:

А почему не использовать это?

  if (Serial.available()) {
    String buffer;
    buffer = Serial.readString();
  }

 

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

FR
Offline
Зарегистрирован: 29.12.2015

Ребят, подскажите пожалуйста:

Будет ли работать функция SerialEvent с софтовым сериалом?

Аппаратный порт занят приемом данных с GPS, и использует эту (SerialEvent) функцию. А софтовым надо просто слушать другой порт и если придет команда (2 байта)  = выполнить ряд действий (временно забив на данные с GPS) и все. Главное, не пропустить эту команду. Вот, думаю попробовать тоже использовать SerialEvent, для этой задачи.

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Нет, не будет.

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

FR пишет:

А софтовым надо просто слушать другой порт и если придет команда (2 байта)  = выполнить ряд действий (временно забив на данные с GPS) и все.

а зачем обязательно SerialEvent  ? - эта функция не делает ничего, что нельзя было бы сделать самому просто в loop() 

Если вы принимаете данные с GPS постоянно, без перерывов - даже с функцией SerialEvent вы можете пропустить команду с другого Сериала, эта функция ничего не гарантирует

strarbit
Offline
Зарегистрирован: 12.06.2016
Logik
Offline
Зарегистрирован: 05.08.2014

И нахрена эту какашку сюда притянул. А самому глянуть на то дерьмо что у них кодом зовется не? Просто людям советуем? Ну смотрим

void NewSoftSerial::write(uint8_t b)
{
  if (_tx_delay == 0)
    return;

  uint8_t oldSREG = SREG;
  cli();  // turn off interrupts for a clean txmit

  // Write the start bit
  tx_pin_write(_inverse_logic ? HIGH : LOW);
  tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);

  // Write each of the 8 bits
  if (_inverse_logic)
  {
    for (byte mask = 0x01; mask; mask <<= 1)
    {
      if (b & mask) // choose bit
        tx_pin_write(LOW); // send 1
      else
        tx_pin_write(HIGH); // send 0
    
      tunedDelay(_tx_delay);
    }

 И шо? А вот шо. Пока идет write прерывания cli(); - запрещены, а значить прием данных не производится. И если на Rx в этот момент начнет поступать нечто то оно будет пропущено или искажено. Каким образом внешнее устройство должно заподозрить что именно сейчас такое произойдет и не передавать что либо во избежании потерь? Правильный софтсириал должен уметь принять переданное им же. Т.е соединяем у контроллера Tx и Rx и приняли то что отправили. Сразу скажу - это возможно, но то дерьмо что ардуине суют такое не умеет.

FR
Offline
Зарегистрирован: 29.12.2015

Спасибо!

Да, тоже пришел к таким мыслям.  Программа Принимает пакеты с GPS, снимает показания с датчиков, обрабатывает их и выплевывает в сериал вместе с данными GPS. Долго пытался отладить, чтобы не было пропусков, но получилось. Именно SerialEvent помог и более высокие скорости ком-порта. 

Для софтового сделаю отдельную функцию, по типу SerialEvent.

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

FR пишет:

Да, тоже пришел к таким мыслям. .............

SerialEvent помог и более высокие скорости ком-порта. 

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

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

MaksVV
Offline
Зарегистрирован: 06.08.2015

не знаю советовать или нет, классика из интернетов на кружевных Стрингах

#include <SoftwareSerial.h>
SoftwareSerial MODEM (10, 11); // RX, TX
#define STRING_LENGTH 55   // максимальная длина строки (количество символов) от модема
String currStr = "";

void readMODEM()      
{
  if (!MODEM.available()) return;
  char currSymb = MODEM.read();
 static bool stringEnd = 0;
//  Serial.write(currSymb);   // раскоментировать, если нужна отладка в Serial 
  
  if ('\r' == currSymb || stringEnd == 1) // если строка принята - парсим её. 
    {
      if (currStr.indexOf(F("YA MODEM")) > -1) {тут что-то делаем, если в строке от модема есть такой текст "YA MODEM"}
           
      currStr = ""; stringEnd = 0;
    } 
 
    else if ('\n' != currSymb)  currStr += String(currSymb);
    if (currStr.length()>STRING_LENGTH) stringEnd = 1;
}


void setup() 
{
  MODEM.begin (19200); 
  Serial.begin (19200); 
}

void loop() 
{
  readMODEM();
  // тут остальная программа, без delay-ев 
}