Перейти к содержанию
    

GSM модем + TNKernel

Добрый день. Есть GSM модем, управление, которого производится AT командами.

Общий вид "общения" с модемом:

1. Отправляется команда

2. Ответ модема на команду (может отсутствовать)

3. Строка "OK", что мол команда выполнена.

 

Соответственно есть команды, которые возвращают значение состояния модема, например:

AT+CREG?

 

+CREG: 0, 1

OK

 

А есть, которые просто выдают строчку ОК, что команда принята и все, например:

AT

 

OK

 

Собственно и нужно отслеживать каждый ответ модема и разбирать его, сейчас я сделал что-то подобное. Функция отправки простой AT команды модему:

 

uint8_t modem_check_at(void) {
  uint32_t flag, rc;

  uart3_ex_send_string("AT\r\n");
  
  rc = tn_event_wait(&modem_event, MODEM_SAY_OK, TN_EVENT_WCOND_OR, &flag, 300);
  if (rc != TERR_NO_ERR) {
    dbg_send("AT fail\r\n");
    return ACTION_FAIL;
  }

  dbg_send("AT ok\r\n");
  tn_event_clear(&modem_event, ~(MODEM_SAY_OK));
  return ACTION_OK;
}

 

А в самом прерывании, если от модема пришла строка содержащее слово ОК, то выставляется флаг MODEM_SAY_OK:

  if (strncmp(modem_buffer, "OK", 2) == 0) {
    tn_event_iset(&modem_event, MODEM_SAY_OK);
    return;
  }

 

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

Например команда: AT+CREG?

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

Поэтому простой проверкой на слово ОК не обойтись, нужно разбирать, то что ответил модем.

Сделал таким образом: все строчки, которые пришли от модема складываются в очередь, которую уже должна обработать функция, которая отправляла данную команду модему, опять же пример:

uint8_t modem_creg(void) {
  uint32_t rc, flag, number_queue;
  
  uart3_ex_send_string("AT+CREG?\r\n");
  
  rc = tn_queue_receive(&queue_modem_buffer, (void**)&number_queue, 300);
  if (rc != TERR_NO_ERR) {
    dbg_send("CREG fail!\r\n");
    return ACTION_FAIL;
  }
  
  dbg_send("Answer CREG - ");
  dbg_send(modem_queue_buffer[number_queue]);
  dbg_send_rn();
}

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

 

Но в таком случае в очередь может отправится ЭХО модема и вместо ответа "+CREG: 0,2" в функции увидим "AT+CREG?", как вариант прогонять очередь до тех пор пока не попадется нужный нам ответ:

  do {
    rc = tn_queue_receive_polling(&queue_modem_buffer, (void**)&number_queue);

    if (strncmp(modem_queue_buffer[number_queue], "+CREG", 5) == 0) {
      return number_queue;
    }
  } while (rc == TERR_NO_ERR);

 

Но это кажется сильно натянутым решением...

Эхо от модема отключать не желательно,хотя это отчасти и решило бы проблему, но опять же не полностью.

И кроме однострочных команд, модем может выдать много строчные команды.

 

В общем, у кого-нибудь был опыт работы с АТ командами, кто как реализовал? Заранее благодарен за любой совет/пинок в нужную/правильную сторону.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Moderator: К вопросам операционных систем тема относится чуть менее, чем никак, посему - перенес.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Делал в двух вариантах: без распознавания чисел, и с распознаванием. В этом случае, вроде бы, можно обойтись без распознавания чисел (если надо, то приведу и распознаванием).

 

Без распознавания. Сначала составляем таблицу всех нужных нам ответов модема, при этом к числам в ответе относимся как к части текста:

DB8 "OK"                    ' 1
DB8 "ERROR"                 ' 2
DB8 "RDY"                   ' 3
DB8 "+CFUN: 1"              ' 4
DB8 "+CFUN: 0"              ' 5
DB8 "Call Ready"            ' 6
DB8 "+CPIN: READY"          ' 7
DB8 "+CPIN: NOT INSERTED"   ' 8
DB8 "+CREG 0,1"             ' 9
DB8 "+CREG 0,0"             ' 10
DB8 "SMS Ready"             ' 11
и так далее

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

 

Например ответ модема:

 +CREG 0,1
OK

будет помещён в очередь как последовательность байтов: 9, 1

А ответ:

 +CREG 0,0
OK

в очереди будет: 10, 1

 

Ещё для примера - текст при включении модема:

RDY
+CFUN: 1
+CPIN: READY
Call Ready
SMS Ready

в очереди будет выглядеть так: 3, 4, 7, 6, 11

 

А всё что не распозналось (эхо и какие-то ненужные ответы) - в очереди будет отсутствовать вообще.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если сделать так:

перед отправкой команды модему в глобальную перемену заносим начало ответа на команду, в случае с командой AT+CREG?

это будет "+CREG", а в прерывании, вернее в функции обработчике буфера в прерывании будем сравнивать полученный ответ от модема с этой переменной. Тогда не нужно будет прочесывать каждый раз таблицу...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно и так - это лишь вопрос удобства написания конкретной программы :laughing:

 

Мне нужно было рулить ещё нескольким внешними устройствами, а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды. Это очень долго (модем может ещё и зависнуть). А терять данные от других внешних устройств не хотелось.

Потому приём данных от COM-порта был перепоручен обработчику прерываний UART, который в завершении приёма очередной строки сверялся с таблицей, и выкладывал в глобальную переменную код этой строки. Сама таблица была 30-40 строчек, и её "прочёсывание" после ответа модема - основную программу особо не тормозило.

 

А уже основная программа, в промежутках между обращениями к остальным устройствам, парсила глобальную переменную на неравенство "0", и только в этом случае отвлекалась на обработку ответа.

 

Если у вас программа линейная и других задач для процессора нет - то вполне можно загрузить его ожиданием приёма данных от модема на все 100%. Почему бы и нет :rolleyes:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну кроме работы с модемом нужно повешать еще и другие задачи, поэтому и использую операционку:

один таск под модем,

второй под работу с ГПС и т.д. каждый таск под свое устройство.

а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды

 

Не совсем понял, а почему может составлять секунды? Модем висит на одном ЮАРТе, другие девайсы на другом и т.п под каждый юарт свой обработчик прерывания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А как Вы будете обрабатывать unsolicited messages?

 

Не совсем понял, а почему может составлять секунды?

Вероятно, имелось в виду, что gsm модуль чисто физически может отвечать с задержкой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А как Вы будете обрабатывать unsolicited messages?

 

В функции обработчике буфера от модема (в прерывании) до того как отправить буфер очередью проверяются такие данные от модема:

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

 

2. > - готов отправить данные серверу.

 

3. SEND OK - аналогично с флагом ОК только этот флаг ожидается после отправки данных "на сервер".

 

4. +RECEIVE: - получили данные "с сервера" и записываем приходящие данные в прерывании не в буфер модема, а в буфер входящих сообщений.

 

И уже если эти 4 строчки не совпали смотрим отправлять ли очередь или нет, опять же в зависимости от того на какую команду ответ мы ждем.

 

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

modem.rar

Изменено пользователем hound

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А, вот какой обработчик. Это совсем другое дело! :biggrin:

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

 

Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+CREG: 0,1","OK". То, что отмечено как "\r" и "\n" - это непечатные символы <cr> (0x0d) и <lf>(0x0a) соответственно.

d5c376936842.jpg

Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А, вот какой обработчик. Это совсем другое дело! :biggrin:

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

 

Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+CREG: 0,1","OK". То, что отмечено как "\r" и "\n" - это непечатные символы <cr> (0x0d) и <lf>(0x0a) соответственно.

d5c376936842.jpg

Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения?

 

По концу строки <0xOD><0xOA>

В прерывании:

  if (ch == 0x0D) return;
  if (ch == 0x0A) {
    modem_line_buffer.data[modem_line_buffer.pos] = 0x00;
    modem_line_buffer.pos = 0;
    modem_process_buffer();
    return;
  }

 

Дальше уже в функции modem_process_buffer() идет обработка сообщения

void modem_process_buffer(void) {
  if (strlen(modem_line_buffer.data) < 1) return;
  
  if (strncmp(modem_line_buffer.data, "OK", 2) == 0) {
    tn_event_iset(&modem_event, MODEM_SAY_OK);
    return;
  }  
  
  if (strncmp(modem_line_buffer.data, "SEND OK", 7) == 0) {
    tn_event_iset(&modem_event, MODEM_SEND_OK);
    return;
  }  
  
  if (strncmp(modem_line_buffer.data, "+RECEIVE", 7) == 0) {
    uint16_t length;
    length = get_modem_token_int(modem_line_buffer.data, 1);
    modem_status.read_flag = TRUE;
    modem_receive_buffer_reset(&modem_receive_buffer, length);
    return;
  }
  
  if (strncmp(modem_line_buffer.data, wait_queue.data, wait_queue.length) == 0) {
    modem_send_queue();
  }
}

 

Где, wait_queue.data, wait_queue.lengt это строка и ее длина для анализа ответа от модема, т.е отправили мы команду AT+CREG?

и от нее нам нужно получить ответ вида +CREG: 2

Вот значение "+CREG" мы и записывает wait_queue.data.

И уже кроме этого ответа ничего в очередь не попадает, а если что-то пришло от сервера, то в прерывании мы схватим сообщение "+RECEIVE", которое в очередь не попадет уж точно.

Как-то так.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В прерывании разбор строк лучше не делать. Прерывание должно отработать максимально быстро и выйти. В какой-то момент могут возникать "глюки" - или не успел отработать или потерял символ. Имхо лучше запоминать позиции концевиков 0x0D 0x0A, а разбор делать в основной программе.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...