Проблема с чтением из SoftwareSerial

AKOTb
Offline
Зарегистрирован: 24.01.2017
Добрый день, уважаемые форумчане.

Занялся сборкой устройства для дистанционного управления обогревателем в гараже. Для реализации использую клон Arduino Nano на 168 микросхеме, связь обеспечивает модуль Neoway M590, далее нагрузкой управляет реле. Данные отправляю на сервер народного мониторинга, откуда отправляю и команду на включение или выключение нагревателя.

С контролем температуры реализовать передачу данных на narodmon труда не составило, в интернете полно примеров реализации. А вот с тем, чтобы встретить команду от сервера возникла проблема.

Для работы с модулем в него надо в определенной последовательности вбить последовательность строк для подготовки модуля для передачи данных по GPRS. Далее необходимо отправить данные для сервера, это тоже определенная последовательность, содержащая индивидуальный номер прибора, показания с датчиков и два символа завершения передачи ##. После чего с сервера мы получаем ОК, или команду управления вместо ОК.

Когда я вручную ввожу данные используя такую конструкцию в скетче:

void bypass(){  //передает ответы модема напрямую в serial
  if (gsm.available())
      Serial.write(gsm.read());
    if (Serial.available())
      gsm.write(Serial.read())
 }

То после того, как я передаю данные на сервер, то всегда получаю команду управления от сервера в виде:

+TCPRECV:0,Х#ИМЯ_КОМАНДЫ", где X - длина имени команды

Однако когда я записываю свои действия следующим образом:



#include <SoftwareSerial.h>
SoftwareSerial gsm(8, 7); // RX, TX
 

void gprssend(){
DHT11.read(DHT11PIN); // читаем данные с градусника
char val[61];
snprintf(val, sizeof(val), "#ХХХХХХХХХХХХХХХХ#akotb\n#H1DHT11#%d\n#T1DHT11#%d\n#V1VCC10#%d\n##",
DHT11.humidity, DHT11.temperature, 5);
gsm.println("AT+TCPCLOSE=0"); // закрываем соединение, на всякий случай

while(1){ // в цикле соединяемся с сервером народмон
gsm.println("AT+TCPSETUP=0,185.245.187.136,8283");
delay(2500);
if (gsm.find("+TCPSETUP:0,OK")) break; // если соединились, выходим из цикла
Serial.println("tcp_err");
// если нет, проверяем соединины ли с интернетом
gsm.flush();
gsm.println("at+xiic?");
delay(100);
if (gsm.find("0.0.0.0")){
gprsconnect(); // если нет, то подключаемся
delay(2000);
}
}
// отправляем 60 байт
gsm.println("at+tcpsend=0,60");
delay(100);
Serial.available();
gsm.println(val);
//delay(2500);
Serial.println("sendOK");
if (gsm.find("+TCPRECV:0,7,#warm1")){ Serial.println("WARM1"); W1 = !W1;}
else if (gsm.find("+TCPRECV:0,7,#warm2")){ Serial.println("WARM2"); W2 = !W2;}
else if (gsm.find("+TCPRECV:0,6,#ref1")) TIME = 5;
else if (gsm.find("+TCPRECV:0,6,#ref2")) TIME = 30;
}
digitalWrite(IN1, W1);
digitalWrite(IN2, W2);
Serial.println(TIME);
Serial.println(W1);
Serial.println(W2);
gsm.println("AT+TCPCLOSE=0"); // закрываем соединение
Serial.print("!!!");
}
 

То по какой-то причине ардуинка в упор не видит команды от сервера. К примеру при ручной передаче я четко вижу, что сервер передал "+TCPRECV:0,7,#warm1", однако когда я пишу if (gsm.find("+TCPRECV:0,7,#warm1")){ Serial.println("WARM1"); W1 = !W1;}, то ничего не происходит, как будто такая строка в порт не заходит.

Я пробовал укоротить проводник между модулем и ардуиной до 1см, не помогло. Пробовал вместо "+TCPRECV:0,7,#warm1" писать только первый элемент строки "+", в этом случае срабатывало, однако оно срабатывало даже если команду с народмона я не направлял. Пробовал уменьшать длину команды, пробовал изменять имя команды на числовое значение, не помогло.

Я совершенно не понимаю как это работает. Когда я делаю gsm.find, то я ищу в конкретной строке в конкретный момент времени или вообще во всем, что зашло в порт с момента его инициализации?
 
Если у кого есть идеи, почему ардуино может слепо игнорировать мои команды, прошу помочь, у меня идеи закончились. 
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AKOTb пишет:
Я совершенно не понимаю как это работает. Когда я делаю gsm.find, то я ищу в конкретной строке в конкретный момент времени или вообще во всем, что зашло в порт с момента его инициализации?

Совет: никогда не используйте команды, которых не понимаете. Напишите лучше свое - ручками. Так, по крайней мере, будете уверены, что контролируете процесс.
AKOTb
Offline
Зарегистрирован: 24.01.2017

Добрый день.
Спасибо за совет. Возможно вы подскажете как можно определить пришла в порт искомая строка или нет?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Самый простой способ - выводить все, что приходит, в консоль и там проверять глазками.

AKOTb
Offline
Зарегистрирован: 24.01.2017

Вы невнимательно читали первое сообщение в теме.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

AKOTb пишет:
Вы невнимательно читали первое сообщение в теме.

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

AKOTb
Offline
Зарегистрирован: 24.01.2017

Rumata пишет:

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

Я понимаю что оформление следует поправить, но на этом форуме править сообщения нельзя, или я не нашел как это делать

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AKOTb пишет:
Вы невнимательно читали первое сообщение в теме.

Да, в общем-то, у меня нет такой обязанности внимательно читать все сообщения. Если бы Вы хотели, чтобы на что-то было обращено особое внимание, вполне можно было на этом акцентировать.

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

Ну и еще одно замечание: IMHO 168 содержит слишком мало памяти, чтобы полноценно работать со строками. Поэтому было бы целесообразно сначала отладить весь скетч на Меге, и только потом пытаться упихать его в 168.

 

PS. И - да, требовать внимательного прочтения от так оформленного текста IMHO выходит за рамки разумного.

b707
Онлайн
Зарегистрирован: 26.05.2017

AKOTb -  выложите код, во-первых, по правилам форума. Во-вторых - целиком. В нынешнем обрывке мне совершенно непонятно, откуда берется функция gsm.find(). gsm - это экземпляр класса SoftwareSerial, у которого, насколько мне известно, нет метода find()

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

ТС - и в принципе. Заставьте программу работать без сети, а уж потом добавьте сеть.

AKOTb
Offline
Зарегистрирован: 24.01.2017

Проблему решил установив в конце строки передачи данных символ перевода каретки. Теперь возник вопрос оптимизации кода.

Для 168 микросхемы сообщение об объеме занимаемой скетчем программы выглядит так:

Скетч использует 7248 байт (50%) памяти устройства. Всего доступно 14336 байт.
Глобальные переменные используют 669 байт (65%) динамической памяти, оставляя 355 байт для локальных переменных. Максимум: 1024 байт.
 
Конечно ещё не в красной зоне, но мне кажется что этот скетч можно как-то уменьшить, я не специалист, поэтому прошу помощи.
 
#include <SoftwareSerial.h>
#include <dht11.h>
#define DHT11PIN 11  // пин DHT11
boolean W1 = true;
boolean W2 = true;
float LT = 0;
int TIME = 30;
int IN1 = 4;

SoftwareSerial gsm(8, 7); // RX, TX
dht11 DHT11;

void gprsconnect() {
  gsm.println("AT+CGATT?");                 // подключаем GPRS
        while(!gsm.find("+CGATT: 1")){
          Serial.print(". ");
          delay(100);
          }
        Serial.print(4);  // GPRS OK
        gsm.println("AT+XISP=0");                 // выбираем TCP/IP протокол
        while(!gsm.find("OK")){
          Serial.print(". ");
          delay(100);
          }
        Serial.println();
        Serial.println(5); //TCP OK      
        
        gsm.println("AT+CGDCONT=1,\"IP\",\"internet.beeline.ru\"");       // регистрируемся в сети GPRS
        while(!gsm.find("OK")){
          Serial.print(". ");
          delay(100);
          }
        Serial.println();
        Serial.println(6); //"REG OK"
        
        gsm.println("AT+XGAUTH=1,1,\"beeline\",\"beeline\"");           //авторизация
        while(!gsm.find("OK")){
          Serial.print(". ");
          delay(100);
          gsm.println("AT+XGAUTH=1,1,\"beeline\",\"beeline\"");
          }
        Serial.println();
        Serial.println(7); // "AUT OK"

        gsm.println("AT+XISP=0");  // включаем РРР
        delay(100);
        gsm.println("at+xiic=1");   
        delay(100);
        do{             // проверяем выдали ли нам IP
        gsm.println("at+xiic?");         
        Serial.print(".");
        delay(300);  
        }
        while(gsm.find("0.0.0.0"));
        gsm.println("AT+XIIC=1");                        //Установка PPP-соединения
        while(!gsm.find("OK")){
          Serial.print(". ");
          delay(100);
          }
        Serial.println();
        Serial.println(8); // "PPP OK"
}


void gprssend(){
    DHT11.read(DHT11PIN); // читаем данные с градусника 
     char val[61]; 
     snprintf(val, sizeof(val), "#ХХХХХХХХХХ#akotb\n#H1DHT11#%d\n#T1DHT11#%d\n#V1VCC10#%d\n##",
          DHT11.humidity, DHT11.temperature, 5);
     gsm.println("AT+TCPCLOSE=0");  // закрываем соединение, на всякий случай 
  while(1){ // в цикле соединяемся с сервером народмон
    gsm.println("AT+TCPSETUP=0,185.245.187.136,8283");   
    delay(2500);
    if (gsm.find("+TCPSETUP:0,OK")) break; // если соединились, выходим из цикла
    Serial.println(9);  //"tcp_err"  
                   // если нет, проверяем соединины ли с интернетом
    gsm.flush(); 
    gsm.println("at+xiic?");
    delay(100);  
    if (gsm.find("0.0.0.0")){
      gprsconnect(); // если нет, то подключаемся      
      delay(2000);
    }
  }
            // отправляем 47 байт 
  gsm.println("at+tcpsend=0,60");
  delay(100); 
  gsm.println(val);  
  gsm.println("/n");
  delay(250);
  Serial.println(10);
  if (gsm.find("+TCPRECV:0,7,#warm1")){Serial.println(881); W1 = !W1;}
  digitalWrite(IN1, W1);
  gsm.println("AT+TCPCLOSE=0");  // закрываем соединение
  Serial.println(999);
}  
 
void setup(){  
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, HIGH);
  Serial.begin(9600);
  gsm.begin(9600);             /// незабываем указать скорость работы UART модема
   while(!gsm.find("+PBREADY")){             // при включении ждем отклик от модема        
    Serial.print(". ");                
   }
        gsm.println("ATE0");
        Serial.println();
        Serial.println(1); // MODEM READY
        Serial.println(2); //OPERATOR SEARCH
          while(!gsm.find("+COPS: 0,0,\"Beeline\"")){             // ждем когда найдется оператор        
          gsm.println("AT+COPS?");
          Serial.print(". ");        
          delay(500);
          }
        Serial.println();        
        Serial.println("3"); //BEELINE
gprsconnect();
gprssend();
   }
    
 
void loop() {
   if (gsm.find("RING")) gprssend();
    if ((millis() - LT) > (TIME*60000))
    {
      gprssend();
      LT = millis();
    }
    }

 

 

b707
Онлайн
Зарегистрирован: 26.05.2017

АКОТ, похоже вы не мастер отвечать на вопросы. Так откуда все-таки берется метод gsm.find() ?

А на тему оптимизации - начните с применения макроса F() - с вашими строчками должны выиграть байтов двести оперативки

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ну для экономии ОЗУ можно воспользоваться макросом F() к текстовым строкам.

AKOTb
Offline
Зарегистрирован: 24.01.2017

b707 пишет:

АКОТ, похоже вы не мастер отвечать на вопросы. Так откуда все-таки берется метод gsm.find() ?

стандартный метод, я ничего не изобретал, вот у гайвера про него нашел информацию
https://alexgyver.ru/lessons/serial/

Serial.find(target), Serial.find(target, length)

 

за совет спасибо. Буду изучать

AKOTb
Offline
Зарегистрирован: 24.01.2017

Ух как это круто!

Скетч использует 7338 байт (51%) памяти устройства. Всего доступно 14336 байт.
Глобальные переменные используют 387 байт (37%) динамической памяти, оставляя 637 байт для локальных переменных. Максимум: 1024 байт.
 
Огромное спасибо за совет всем!
Upper
Offline
Зарегистрирован: 23.06.2020
126     if ((millis() - LT) > (TIME*60000))
127     {
128       gprssend();
129       LT = millis();
130     }

Здесь путаница с типами.

LT у вас зачем то float. После 60000 надо дописать UL т.е.  60000UL

AKOTb
Offline
Зарегистрирован: 24.01.2017

Upper пишет:

Здесь путаница с типами.

LT у вас зачем то float. После 60000 надо дописать UL т.е.  60000UL

Спасибо, исправил.

Посоветуйте по примененной мною конструкции:

    if (gsm.find("+TCPRECV:0,7,#warm1")){Serial.println(F("WARM1")); W1 = !W1;}
    else if (gsm.find("+TCPRECV:0,7,#warm2")){Serial.print(F("WARM2")); W2 = !W2;}
    else if (gsm.find("+TCPRECV:0,6,#ref1")){Serial.print(F("TCPRCV_REF1")); TIME = 5;}

Мне кажется, что она не рациональная, когда я хочу найти строку "+TCPRECV:0,6,#ref1", то никогда её не нахожу, так как она стоит далеко. Я полагаю, что тут оптимальнее было бы записывать в строку то, что находится между +TCPRECV:0 и концом строки, и уже в дальнейшем работать с этим. Но как это записать не знаю пока. Можно конечно просто попробовать String = gsm.read(), ну туда может и пустая запись попасть. Буду думать.

b707
Онлайн
Зарегистрирован: 26.05.2017

AKOTb пишет:

Посоветуйте по примененной мною конструкции:

    if (gsm.find("+TCPRECV:0,7,#warm1")){Serial.println(F("WARM1")); W1 = !W1;}
    else if (gsm.find("+TCPRECV:0,7,#warm2")){Serial.print(F("WARM2")); W2 = !W2;}
    else if (gsm.find("+TCPRECV:0,6,#ref1")){Serial.print(F("TCPRCV_REF1")); TIME = 5;}

Мне кажется, что она не рациональная, когда я хочу найти строку "+TCPRECV:0,6,#ref1", то никогда её не нахожу, так как она стоит далеко.

Абсолютно верно вам кажется, использовать find таким образом нельзя. Эта функция ищет строку во входных данных Сериал, то есть читает приходящие в порт символы и сравнивает их со строкой в первом if(). Функция работает до тех пор, пока не найдет строку или данные в Сериал не кончатся. Только после этого программа перейдет к условию во втором if(), однако не будет искать вторую строчку в уже принятых данных, а будет ждать новых. Таким образом, если в Сериал приходит только одна строка из трех  - строки во втором и последующих elseif() не будут найдены никогда.

Цитата:
Я полагаю, что тут оптимальнее было бы записывать в строку то, что находится между +TCPRECV:0 и концом строки, и уже в дальнейшем работать с этим. Но как это записать не знаю пока.

да, в вашем случае правильнее СНАЧАЛА принять данные, сохранить куда-то, а уже потом в этих данных искать нужные строки. Символы "+TCPRECV:0", однаковые во всех строках - можно использовать как маркер для определения, что именно сохранять

 

Upper
Offline
Зарегистрирован: 23.06.2020

AKOTb пишет:

Посоветуйте по примененной мною конструкции:

    if (gsm.find("+TCPRECV:0,7,#warm1")){Serial.println(F("WARM1")); W1 = !W1;}

На днях здесь было обсуждение по похожей теме. http://arduino.ru/forum/programmirovanie/nuzhna-pomoshch-s-uart#comment-589960 Без изменений вам не подходит, но как пример посмотрите.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AKOTb пишет:

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

https://alexgyver.ru/lessons/serial/

Serial.find(target), Serial.find(target, length)

Почитал описание, стремная функция. Я бы не рискнул использовать ее в своих проектах - слишком много побочных эффектов.

 

Upd:

AKOTb пишет:

Посоветуйте по примененной мною конструкции:

    if (gsm.find("+TCPRECV:0,7,#warm1")){Serial.println(F("WARM1")); W1 = !W1;}
    else if (gsm.find("+TCPRECV:0,7,#warm2")){Serial.print(F("WARM2")); W2 = !W2;}
    else if (gsm.find("+TCPRECV:0,6,#ref1")){Serial.print(F("TCPRCV_REF1")); TIME = 5;}

Мне кажется, что она не рациональная, когда я хочу найти строку "+TCPRECV:0,6,#ref1", то никогда её не нахожу, так как она стоит далеко.

Ну а вот и пример, почему ее не следует использовать.

AKOTb
Offline
Зарегистрирован: 24.01.2017

Всем спасибо, с Вашими советами удалось реализовать стабильную работу.

Написал следующую функцию, которую вызываю сразу после передачи данных с датчиков температуры

void SSRead() {
  while(1){
  while (gsm.available() > 0) {         // ПОКА есть что то на вход    
    strData += (char)gsm.read();        // забиваем строку принятыми данными
    recievedFlag = true;                   // поднять флаг что получили данные
    delay(2);
  }
  if (recievedFlag) {                      // если данные получены
       if (strData.substring(11,20) == "0,7,#warm")
        {
        if (strData.substring(20,21).toInt() == 1) 
            {
              W1 = !W1;
              Serial.println(F("WARM1")); 
            }
        else 
            {
              W2 = !W2;
              Serial.println(F("WARM2"));
            }
        strData = ""; 
        recievedFlag = false;
        break;
        }
    
    else if (strData.substring(15,19) == "#ref")
        {
          Serial.print(F("TCPRCV_REF1")); 
          TIME = strData.substring(19,21).toInt();
          strData = "";
          recievedFlag = false;
          break;
        }
        
    else if (strData.substring(11,17) == "0,3,OK"){Serial.println(F("Нет новых команд")); Serial.println(strData.substring(0,17)); strData = ""; recievedFlag = false; break;}
    else if (strData.substring(12,18) == "0,Link"){strData = ""; recievedFlag = false; break;}
    
    strData = "";                          // очистить
    recievedFlag = false;                  // опустить флаг
   
  }
}
}

Благодаря такому способу реализации, я могу считать с отправленной команды данные, к примеру ранее я создавал команду ref1 чтобы обновление данных шло раз в 5 минут, а команду ref2 ,чтобы раз в 30 минут. Теперь же я реализовал чтение числа после ref, и стало гораздо удобнее. Также вызов нагревателя я организовал одним If, возможно это не имело смысла, ещё подумаю над этим.

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

Всем спасибо) 

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

победой можно считать избавление от String (что еще более важно для атмеги 168) и неблокирующее чтение из Serial без delay и while 

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

Upper пишет:
На днях здесь было обсуждение по похожей теме. http://arduino.ru/forum/programmirovanie/nuzhna-pomoshch-s-uart#comment-589960 Без изменений вам не подходит, но как пример посмотрите.

здесь более подходящая для ТС тема и код. Почитайте внимательно ту тему сначла

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

да макрос F() еще недоиспользован )

AKOTb
Offline
Зарегистрирован: 24.01.2017

Макрос F как оказалось с операторами сравнения работает нестабильно. Через раз, а иногда и вовсе не срабатывает. Вынужденная мера убрать макросы Эф из while и if.
По вопросу избавления от string спасибо за совет. Как посетит вдохновение, обязательно займусь усовершенствованием кода. Тут другая задача появилась. В течении длительного теста что-то в связке ардуина+модем стало зависать. Вот думаю над организацией какого-то аналога watchdog'а. Хотя возможно стоит внимательнее почитать в мониторе порта, что там а это время происходит. Сложность в том что проблема вылезла пока что единожды, а плавающие ошибки ловить сложнее всего.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AKOTb пишет:
Макрос F как оказалось с операторами сравнения работает нестабильно. Через раз, а иногда и вовсе не срабатывает.
Макросы не могут работать нестабильно.

Нестабильно может работать программа. Но тогда - все вопросы к программисту.

Green
Онлайн
Зарегистрирован: 01.10.2015

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

AKOTb
Offline
Зарегистрирован: 24.01.2017

andriano пишет:

Нестабильно может работать программа. Но тогда - все вопросы к программисту.

Безусловно моей компетенции в этом вопросе недостаточно, но по своим наблюдениям заметил, что при введении в скетч этой конструкции появляются проблемы, убрал, стало лучше. 

Всю ночь тестировал на стабильность устройство, пока работает.

AKOTb
Offline
Зарегистрирован: 24.01.2017

Green пишет:

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

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

AKOTb пишет:

...но по своим наблюдениям заметил, что при введении в скетч этой конструкции появляются проблемы, убрал, стало лучше. 

Конструкцию мало "вести в скетч", нужно еще ей  правильно воспользоваться. 

Почему-то начинающие программисты винят в неустойчивости работы программы что угодно: компилятор, макросы, библиотеки, но никогда не себя любимого.

Upper
Offline
Зарегистрирован: 23.06.2020

AKOTb пишет:

Написал следующую функцию, которую вызываю сразу после передачи данных с датчиков температуры

void SSRead() {
  while(1){
  while (gsm.available() > 0) {         // ПОКА есть что то на вход    
    strData += (char)gsm.read();
        

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

strData лучше объявить внутри функции SSRead(). Тогда будет меньше вероятность утечки памяти. См. http://arduino.ru/forum/programmirovanie/etyudy-dlya-nachinayushchikh-pamyat-3-dinamicheskie-i-avtomaticheskie-peremen параграфы про String

При заполнении strData желательно ограничить (контролировать) ее длину.

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

AKOTb пишет:
По вопросу избавления от string спасибо за совет. Как посетит вдохновение, обязательно займусь усовершенствованием кода. Тут другая задача появилась. В течении длительного теста что-то в связке ардуина+модем стало зависать. .....Сложность в том что проблема вылезла пока что единожды, а плавающие ошибки ловить сложнее всего.

дак задача может это вовсе и не другая. String-и любят пожирать ресурсы МК, особенно у начинающих юзеров, не умеющих эти трусы готовить. И уж тем более это актуально для атмеги 168. А когда заканчивается ОЗУ, чудеса бывают самые разные и естественно их сложно локализовать и отловить. Так что я не зря вам даже ссылку с примером дал. тот пример поправить под ваши нужды было не трудно, ну как хотите

#include <SoftwareSerial.h>
SoftwareSerial gsm(8, 7); // RX, TX

#define BUF_SIZE 60       // размер буфера парсилки
char currStr[BUF_SIZE+3]; // буфер парсилки  
#define MAX_CHAR_COMMAND 10 // максимальное количество символов в команде
                                 enum {OK      ,  WARM ,  REF ,  LINK,  COMMAND_QUANTITY};   //пишем список команд
const char comm[][MAX_CHAR_COMMAND] = {"0,3,OK", "warm", "ref", "Link"}; // массив строк команд (очередность должна совпадать со списком выше)

const char strBegin[] = "+TCPRECV"; // с чего должна начинаться строка от модема


#define VAR_QUANTITY 2 // здесь задаём максимальное количество переменных после команды 
                       // (числа писать через пробел после команды, например, warm 2 пропарсится val[0]==2
                       //  а также пробел между числами, например Link 20 567 пропарсится:  val[0]==20, а val[1]==567 

char *sTr[VAR_QUANTITY+1];   // это нужно для парсинга


bool W1=0, W2=0;  // ваши вэ
int TIME = 30;    // ваши тайм

//------------------------------функция исполнения полученной команды
void runCommand(byte command)
{
 int val[VAR_QUANTITY];        // переменные, в которые парсим значения из поступившей строки
 Serial.print (comm[command]); // распечатаем поступившую команду в отладку

 //парсим переменные
 for (int i = 1; i<=VAR_QUANTITY; i++ ) 
 { 
   sTr[i]=strchr(sTr[i-1], ' ')+1; 
   val[i-1]= atoi (sTr[i]);
 }
      // делаем нужные действия на соответствующие команды:
     if (command==WARM){Serial.println(val[0]); if (val[0]==1) W1=!W1; else if (val[0]==2) W2=!W2; }          
else if (command==REF) {Serial.print (F("  TIME = ")); Serial.println(val[0]); TIME = val[0];} 
else if (command==LINK){Serial.println(val[0]); Serial.println(val[1]);}                     
else if (command==OK)  {Serial.println(F("     NO new commands"));} 
 }

//------------------------функция чтения информации от модема
void Modem_read()
{
  if (!gsm.available()) return; // если данных нет игнорируем эту функцию
  char currSymb[2] = {0};          // символьный кэш
  currSymb[0] = gsm.read();     // читаем очередной символ
  static bool stringEnd = 0;       // флаг переполнения буфера

 if (currSymb[0] == '\r' || stringEnd == 1)   // если полностью получили строку - парсим:
 {
  bool commCorrect = 0;   // флаг найдена ли нужная команда
  // парсим все команды:
  for (int i=0; i<COMMAND_QUANTITY; i++) 
     {
      if (strstr(currStr, strBegin)>0){ // если строка началась с +TCPRECV, парсим
       if ((sTr[0] =strstr(currStr, comm[i]))>0) {runCommand (i); commCorrect = 1; break;}}
     }

if (!commCorrect)Serial.println ("Command is not correct!"); // поругаемся, если нет такой команды
 
 currStr[0] = 0; stringEnd = 0; // в конце парсинга нулим буфер
 } 

// если не конец строки, то прибавляем очередной байт к буферу : 
else if ( currSymb[0] != '\n')  strcat (currStr,currSymb); 
// если буфер закончился , то режем строку на этом месте: 
     if (strlen(currStr)>=BUF_SIZE) {stringEnd = 1;}

}
//--------------------------------

void setup() 
{
 Serial.begin(9600);
 gsm.begin(9600);
}

void loop() 
{
Modem_read(); // функция чтения из UART
// тут остальной код
}

 

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

Макс, вот тебе в копилку: есть такая штука для хранения строк, называется PROGMEM. И всякие к ней функции имеются в avr lib: https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga... -очень полезные.

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

прогмем то я пользуюсь когда надо, но все равно пасиб, посмотрю

AKOTb
Offline
Зарегистрирован: 24.01.2017

Спасибо, ребят! Вот реально приятно, когда людям не безразличен твой проект. Протестирую обновление, обязательно отпишусь!

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

sadman41 пишет:
Макс, вот тебе в копилку: есть такая штука для хранения строк, называется PROGMEM. И всякие к ней функции имеются в avr lib: https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#gaa0f5fff512c8e23adc06844aa73c557b -очень полезные.

типа так? 

#include <SoftwareSerial.h>
SoftwareSerial gsm(8, 7); // RX, TX

#define BUF_SIZE 60       // размер буфера парсилки
char currStr[BUF_SIZE+3]; // буфер парсилки  
#define MAX_CHAR_COMMAND 10 // максимальное количество символов в команде
                                        enum {OK      ,  WARM ,  REF ,  LINK,  COMMAND_QUANTITY};   //пишем список команд
const char comm[][MAX_CHAR_COMMAND]PROGMEM = {"0,3,OK", "warm", "ref", "Link"}; // массив строк команд (очередность должна совпадать со списком выше)

const char strBegin[] PROGMEM = "+TCPRECV"; // с чего должна начинаться строка от модема


#define VAR_QUANTITY 2 // здесь задаём максимальное количество переменных после команды 
                       // (числа писать через пробел после команды, например, warm 2 пропарсится val[0]==2
                       //  а также пробел между числами, например Link 20 567 пропарсится:  val[0]==20, а val[1]==567 

char *sTr[VAR_QUANTITY+1];   // это нужно для парсинга


bool W1=0, W2=0;  // ваши вэ
int TIME = 30;    // ваши тайм



//------------------------------функция исполнения полученной команды
void runCommand(byte command)
{
int val[VAR_QUANTITY];        // переменные, в которые парсим значения из поступившей строки
char buff[MAX_CHAR_COMMAND+1] = {0};
strcpy_P(buff,(const char*)comm[command]); 
 Serial.print (buff); // распечатаем поступившую команду в отладку

 //парсим переменные
 for (int i = 1; i<=VAR_QUANTITY; i++ ) 
 { 
   sTr[i]=strchr(sTr[i-1], ' ')+1; 
   val[i-1]= atoi (sTr[i]);
 }
      // делаем нужные действия на соответствующие команды:
     if (command==WARM){Serial.println(val[0]); if (val[0]==1) W1=!W1; else if (val[0]==2) W2=!W2; }          
else if (command==REF) {Serial.print (F("  TIME = ")); Serial.println(val[0]); TIME = val[0];} 
else if (command==LINK){Serial.println(val[0]); Serial.println(val[1]);}                     
else if (command==OK)  {Serial.println(F("     NO new commands"));} 
 }

//------------------------функция чтения информации от модема
void Modem_read()
{
  if (!gsm.available()) return; // если данных нет игнорируем эту функцию
  char currSymb[2] = {0};       // символьный кэш
  currSymb[0] = gsm.read();     // читаем очередной символ
  static bool stringEnd = 0;    // флаг переполнения буфера

 if (currSymb[0] == '\r' || stringEnd == 1)   // если полностью получили строку - парсим:
 {
  bool commCorrect = 0;   // флаг найдена ли нужная команда
  // парсим все команды:
  for (int i=0; i<COMMAND_QUANTITY; i++) 
     {
      if (strstr_P(currStr, (const char*)strBegin)>0){ // если строка началась с +TCPRECV, парсим
       if ((sTr[0] =strstr_P(currStr, (const char*)comm[i]))>0) {runCommand (i); commCorrect = 1; break;}}
     }

if (!commCorrect)Serial.println ("Command is not correct!"); // поругаемся, если нет такой команды
 
 currStr[0] = 0; stringEnd = 0; // в конце парсинга нулим буфер
 } 

// если не конец строки, то прибавляем очередной байт к буферу : 
else if ( currSymb[0] != '\n')  strcat (currStr,currSymb); 
// если буфер закончился , то режем строку на этом месте: 
     if (strlen(currStr)>=BUF_SIZE) {stringEnd = 1;}

}
//--------------------------------

void setup() 
{
 Serial.begin(9600);
 gsm.begin(9600);
}

void loop() 
{
Modem_read(); // функция чтения из UART
// тут остальной код
}