• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Решено NODE MCU v3 парсинг ответа SIM800L

Vypra

Member
Нужно выводить ответы на команды SIM800L. Модуль подключен к Node MCU v3 через UART.

Начальный код этого парня. Переделал под UART.
Код:
String _response = "";                               // Переменная для хранения ответа модуля

void setup()
{
  Serial.begin(9600);                                //Скорость сериал порта
  send_Email("5", "HELLO");                          // 1 - количество символов в теле сообщения, 2 - сообщение
}

void loop()
{}

void send_Email(String num, String message)
{
  Serial.println("AT");                                                                   // Отправили AT для настройки скорости обмена данными
 
  sendATCommand("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", true);                               // задаем команду выхода в интернет
  sendATCommand("AT+SAPBR=3,1,\"APN\",\"internet\"", true);                               // настройки APN

// .... дальше код отправки емейл удалил чтоб не перегружать форум.
}



String sendATCommand(String cmd, bool waiting)                                             // Функция отправки комманд модулю
{
  String _resp = "";                                                                       // Переменная для хранения результата
  Serial.println(cmd);                                                                     // Отправляем команду модулю
  if (waiting) {                                                                           // Если необходимо дождаться ответа...
    _resp = waitResponse();                                                                // ... ждем, когда будет передан ответ
    Serial.println(_resp);                                                                 // Дублируем ответ в монитор порта
  }
  return _resp;                                                                            // Возвращаем результат. Пусто, если проблема
}


String waitResponse() // Функция ожидания ответа и возврата полученного результата
{
  String _resp = "";                                                                       // Переменная для хранения результата
  long _timeout = millis() + 10000;                                                        // Переменная для отслеживания таймаута (10 секунд)
  while (!Serial.available() && millis() < _timeout)  {};                                  // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то...
  if (Serial.available()) {                                                                // Если есть, что считывать...
    _resp = Serial.readString();                                                           // ... считываем и запоминаем
  }
  else {                                                                                   // Если пришел таймаут, то...
    Serial.println("Timeout...");                                                          // ... оповещаем об этом и...
  }
  return _resp;                                                                            // ... возвращаем результат. Пусто, если проблема
}
Скетч компилится.
ECHO mode на SIM800 отключен. Ответ от модуля должен прилетать просто ОК или ERROR, без дублирования команды в порт.
Проблема в том, что если делать через Serial.println(); команды посылаются. Как только выполнение кода доходит до sendATCommand("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", true); - прилетает

Код:
Soft WDT reset

>>>stack>>>

ctx: cont
sp: 3ffffcc0 end: 3fffffc0 offset: 01b0
3ffffe70:  015c58b6 3fffff2c 3ffffee8 402073de
3ffffe80:  3ffe8685 3ffeee58 00000000 00014cec
3ffffe90:  3ffeee58 3ffeee58 3ffffee8 402031e5
3ffffea0:  3fffdad0 3fffff2c 3ffeee58 402070c8
3ffffeb0:  3ffe8685 0000000a 00000000 00000001
3ffffec0:  3ffeee58 3fffff2c 3fffff20 4020329c
3ffffed0:  3ffe86c0 0000000b 3fffff2c 4020735f
3ffffee0:  3ffeec0a 00000001 3ffefb74 0000000f
3ffffef0:  00000000 3ffeeda8 3fffff2c 402073de
3fffff00:  3ffe86c0 00000004 41be6666 3ffeeec4
3fffff10:  3fffdad0 3ffeeda8 3ffeed9c 402037d0
3fffff20:  3ffefb5c 0000000f 00000000 3ffefafc
3fffff30:  0000000f 0000000b 3ffeeda8 32204c6c
3fffff40:  00393130 00000002 3fffff70 3fffff60
3fffff50:  00000008 07262117 5c576c42 00000007
3fffff60:  00000003 3ffe88a6 00000005 3ffffe46
3fffff70:  00000000 3ffffe4a 3fffdad0 3ffeeec4
3fffff80:  3fffdad0 3ffeeda8 3ffeed9c 40203da0
3fffff90:  00000000 00000000 3ffeee90 40203dec
3fffffa0:  3fffdad0 00000000 3ffeee90 40207994
3fffffb0:  feefeffe feefeffe 3ffe8604 401014c9
<<<stack<<<
??)?? ?F
Любая команда через sendATCommand () - Soft WDT reset.
Что ему может не нравиться? Мне крайне нужен парсинг ответов SIM800 и для других нужд, не только для отправки емейл. Этот код я использовал на Ардуине, но только через софтсериал, и все работало.
 
Последнее редактирование:

pvvx

Активный участник сообщества
while (!Serial.available() && millis() < _timeout) { тут вставьте вызов обработки системных процедур замаскированных в тиме-аут ...};
 

Vypra

Member
while (!Serial.available() && millis() < _timeout) { тут вставьте вызов обработки системных процедур замаскированных в тиме-аут ...};
Сейчас в тысячный раз посмотрел эту строчку и вопрос: а зачем в конце строки ";" ? И в оригинале тоже. Кажется мне не нужно. Приду домой, попробую убрать.
 

CodeNameHawk

Moderator
Команда форума
Сейчас в тысячный раз посмотрел эту строчку и вопрос: а зачем в конце строки ";"
Если просто, то ; нужно для того, что бы ничего не делать(просто ждать символов или истечения времени, не выполняя других команд)правда у вас знак ; не нужен, так есть {}.
Можете выкинуть или {} или ;
В этом и проблема, если импульсов нет, вы повисаете на 10 сек, а есп этого не любит.
Самое простое разбейте 10 секундную задержку на десять задержек по секунде delay(1000);
delay разрешает обрабатывать WiFi и перезагрузки (по WatchDog) не будет.
 

Vypra

Member
В этом и проблема, если импульсов нет, вы повисаете на 10 сек, а есп этого не любит.
Самое простое разбейте 10 секундную задержку на десять задержек по секунде delay(1000);
delay разрешает обрабатывать WiFi и перезагрузки (по WatchDog) не будет.
Ого. Так это косяк не в коде, это проблема глобального масштаба с while на ESP.
Вчера просидел весь вечер с этим. Перепробовал разных вариантов тысячу, курил тему в нете. Пишут вариант добавлять в while цикл delayMicroseconds(1). Не работает. А если выбросить while - все крутится. Делал даже так:
Код:
  while (!Serial.available() )                                  
  {
    yield(); // или Delay (1) или delayMicroseconds(1) не работают
if ((millis() < _timeout ))
   { Serial.println("Timeout...");
   break;
  }
  }
Вопрос открыт: как обойти while? Мне нужно ждать ответа в Serial или пока не пройдет Timeout. Если использовать Delay () на 10 сек, код не будет мониторить Serial.
 

CodeNameHawk

Moderator
Команда форума
Вопрос открыт: как обойти while?
Так я вам уже дал рекомендацию, как обойти.
Код:
uint8_t popytka = 0;
uint8_t prishli_dannyje=0;
do
  delay(1000);
  popytka+=1;
  if (Serial.available())
 {                                                                // Если есть, что считывать...
    _resp = Serial.readString();                                                           // ... считываем и запоминаем
      prishli_dannyje=1;
  }

while ((prishli_dannyje != 1)&&(popytka < 10)) ;                                // Ждем ответа 10 секунд, если пришел ответ или
Но и это не по феншую, ждать данные надо в loop и не используя задержек.
 

Vypra

Member
Точно. Я забыл что есть еще do while ))) Приеду домой, попробую.
Тогда наверное можно так:
Код:
String waitResponse()                                             // Функция ожидания ответа и возврата полученного результата
{
  String _resp = "";                                              // Переменная для хранения результата
  uint8_t timeoff = 0;
  unsigned long _timeout = millis() + 10000;                      // Переменная для отслеживания таймаута (10 секунд)
  do
  { delayMicroseconds(1);                                         // Чем то заняты, чтоб не злить собаку.
    if ((millis() > _timeout ))
    {timeoff = 1;}
  }
  while ((timeoff == 1) || (Serial.available())) ;                    // Крутим do пока не произойдет событие
  timeoff = 0;

  if (Serial.available() )                                       // Если есть, что считывать...
  {
    _resp = Serial.readString();                                 // ... считываем и запоминаем
  }
  else                                                           // Если пришел таймаут, то...
  {
    Serial.println("Timeout...");                                // ... оповещаем об этом и...
    _resp = "Timeout...";
  }

  return _resp;                                                   // ... возвращаем результат. Пусто, если проблема
}
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
Точно. Я забыл что есть еще do while )))
Это вообще ничего не меняет, while тоже выполняет "кучу" операторов, просто мне так удобней писать.
Основная разница do while , что операторы будут выполнены хотя бы один раз.

Тогда наверное можно так:
Попробуйте так, когда не получится, почитайте для чего я вам посоветовал использовать delay.

Еще раз повторю, делайте ожидание символов(строки) в основном цикле, без задержек, так как они вам не нужны.
 
Последнее редактирование:

Vypra

Member
Еще раз повторю, делайте ожидание символов(строки) в основном цикле, без задержек, так как они вам не нужны.
Простите, тогда я не понял задумку Вашего кода.
Ну первое не понятно почему после do нет {}, если знаю только так:
do
{
// последовательность операторов
} while (проверка условия);
Как тогда работает последовательность выполнения кода? Когда натыкается на while - возвращается на do ? Допускаю что чего-то не знаю еще. Научите, пожалуйста.
И по поводу задержки - она конечно мне не нужна, но как отсчитать время, если SIM800 сбойнет и ответ не прилетит в Serial? Нужно же выйти с цикла while.
 

Vypra

Member
Ставьте yield() или delay(0), что одно и тоже собственно.
Ставил все. И yield(), и delay(0), и delayMicroseconds (1).
Не сработало.
Здесь с кодом нужно поработать. Сегодня вечерком еще посижу.
 

nikolz

Well-known member
Точно. Я забыл что есть еще do while ))) Приеду домой, попробую.
Тогда наверное можно так:
Код:
String waitResponse()                                             // Функция ожидания ответа и возврата полученного результата
{
  String _resp = "";                                              // Переменная для хранения результата
  uint8_t timeoff = 0;
  unsigned long _timeout = millis() + 10000;                      // Переменная для отслеживания таймаута (10 секунд)
  do
  { delayMicroseconds(1);                                         // Чем то заняты, чтоб не злить собаку.
    if ((millis() > _timeout ))
    {timeoff = 1;}
  }
  while ((timeoff == 1) || (Serial.available())) ;                    // Крутим do пока не произойдет событие
  timeoff = 0;

  if (Serial.available() )                                       // Если есть, что считывать...
  {
    _resp = Serial.readString();                                 // ... считываем и запоминаем
  }
  else                                                           // Если пришел таймаут, то...
  {
    Serial.println("Timeout...");                                // ... оповещаем об этом и...
    _resp = "Timeout...";
  }

  return _resp;                                                   // ... возвращаем результат. Пусто, если проблема
}
гляжу я на это чудо и возникает вопрос к автору.
Вы на каком языке умеете мыслить? На дурине умеете? На Си умеете?
А на русском? Напишите сначала что хотите сделать на русском. И когда будет все вам понятно тогда переводите это со словарем на любой язык программирования.
Это так, ответ на вопрос "научите"
 

Vypra

Member
Это так, ответ на вопрос "научите"
Я попросил человека научить, потому что подумал, что есть какой-то тайный вариант не ставить {} после do. Оказалось это ошибка, которую должен поймать компилятор. А сайт Ардуино я посещаю постоянно, поскольку учусь. )))
Еще раз повторю, делайте ожидание символов(строки) в основном цикле, без задержек, так как они вам не нужны.
Прошу прощения, наверное я не так объяснил задачу или меня не так поняли. Крутить в Loop для меня не вариант. Поскольку код постоянно расширяется и нужно парсить ответ SIM800 под разные задачи (отправка емейл, отправка СМС, качество связи и т.д.) - нужна универсальная функция, после обращения к которой будет возврат результата в разные блоки кода.

Подрезюмирую: благодарочка, друзья.
Сделал так:
Код:
String waitResponse()                                             // Функция ожидания ответа и возврата полученного результата
{
  String _resp = "";                                              // Переменная для хранения результата
  unsigned long _timeout = millis() + 10000;                      // Переменная для отслеживания таймаута (10 секунд)
  do
  { delay(0);

    if (millis() > _timeout )                                     // Условие выхода 1
    {
      Serial.println("Timeout...");
      break;
    }

    if ( Serial.available())                                      // Условие выхода 2
    { _resp = Serial.readString();
      Serial.println(_resp);
      break;
    }
  }
  while (millis() < _timeout + 10) ;                               // Просто событие, которое не наступит никогда

    return _resp;                                                   // ... возвращаем результат. Пусто, если проблема
}
Может код для Гуру покажется "чудом" и нелепым, но так работает, Timeout отсчитывает 10 сек. По событию Serial.available() еще не выходил, потому как еще какие-то проблемы с соединением по UART Sim800 и ответами от модуля. Главное While побороли. )))
 

CodeNameHawk

Moderator
Команда форума
while (millis() < _timeout + 10) ;
замените на while (1) ;
С delay(0) сильно увеличивается вероятность, что можете попасть в ситуацию, когда по сериал получите не всю строку ответа, а только ее часть.
 
Последнее редактирование:

Vypra

Member
С delay(0) сильно увеличивается вероятность, что можете попасть в ситуацию, когда по сериал получите не всю строку ответа, а только ее часть.
Но пока других вариантов не нашлось. Без этого While не работает. Как ждать тогда Timeout и мониторить порт без перехода к отправке следующей команды АТ на Sim800? Через If как-то замутить можно? Альтернатива While?
 
Сверху Снизу