Управление GSM модулем с AVR (часть 2)

AVR
В предыдущей части мы отправляли команды на модуль.



Теперь о получении команд с модуля.

Модуль отправляет множество команд. Например, OK, RING, ERROR…
Иногда нужно, чтобы при получении команды контроллер смог опознать её и выполнить какое-то действие. Например, получен входящий звонок. Модуль при этом отправляет в контроллер:


RING

RING

RING


В зависимости от настроек модуля, может отправлять ещё и номер того, кто звонит. Пока нет никакой программы, контроллер ничего с этим сделать не сможет (в лучшем случае) или (в худшем) сделает что не то, а то и вовсе зависнет (не сможет выйти из прерывания).

Требования к коду обработки:
1. Минимальное количество времени на сохранение полученных команд. Никаких задержек в программе прерывания быть не должно. Потом уже с полученным массивом будем делать что угодно.

2. Сохранение всех полученных команд в одном буфере. Для разделения отдельных будем использовать символ $.

3. Распознавание распространенных команд в числовые коды. Например, OK будет 1, ERROR — 4, RING — 2.

Приведу заголовки из предыдущей статьи с поправками:


#define BUF_SIZE 128   //Исходящий буфер
#define BUF_MASK (BUF_SIZE-1)
#define IN_BUF_SIZE 64 //Входящий буфер
#define IN_BUF_MASK (IN_BUF_SIZE-1)

volatile char buffer[BUF_SIZE]="";
volatile char inbuf[IN_BUF_SIZE]="$"; //inner buffer of USART
volatile uint8_t ind_in=0, ind_out=0, rxind_out=0, rxind_in=0, mess = 0;
volatile uint8_t com_detect=0;  //сюда будет записана обнаруженная команда

#define TIMEOUT 100     //на случай если команда так и не принята


Пишем обработчик прерывания приёма данных:


//recieving Data from RS232
ISR (USART_RXC_vect)		
{
	uint8_t tmp;
	tmp = UDR;
	if (tmp == 0x0D) 	//получен конец команды - <enter>
		{
		mess++; //one more message
		inbuf[rxind_in++] = '$'; //вставляем разделитель в буфер
		rxind_in &= IN_BUF_MASK;
		}
    else 
		{
		if (tmp != 0x0A) //очистка непонятного символа с модуля
			{
			inbuf[rxind_in++] = tmp;   //записываем в буфер
			rxind_in &= IN_BUF_MASK;
			}
		}
	sei ();
}



Теперь у нас все команды записаны в буфере. Можно в свободное время проверить переменную mess и если она не равна нулю запустить обработчик команды. В самом проекте были добавлены команды для LCD экрана. Здесь я их пропущу за ненадобностью.


void rx_check_in (void)
    {
    uint8_t count=0;
    com_detect = 0;  //обнуление команды (чтобы не мешал предыдущий мусор)
    while (1)
	{
	if (inbuf[rxind_out] != '$') //обнаружен конец команды (разделитель)
        	{
		com_detect ^=  inbuf[rxind_out++]; //делаем XOR полученным символам
		rxind_out &= IN_BUF_MASK;
		count++;  //считаем, сколько символов в команде
		}
	else 
		{
		rxind_out++;
		rxind_out &= IN_BUF_MASK;
		code_com (count);           //!! важная часть - раскодировать команду 
		break;
		}
	}
    }


Полученные символы мы пропускаем через мясорубку. Делаем XOR операцию. Получаем таким образом уникальный код (не уверен на счёт уникальности, но пока не подводило). R^I^N^G нам даст 0x12. O^K даст 0x04. Этот код и количество символов (в команде) сохранены в переменных com_detect (глобальная) и count. Теперь запустим обработчик:


void code_com (uint8_t count)
	{
	switch (com_detect)
		{
		case (0x12): if (count == 4) com_detect = 2; break; //R^I^N^G
		case (0x58): if (count == 5) com_detect = 3; break; //ERROR
		case (0x04): if (count == 2) com_detect = 1; break; //OK
		case (0x5C): if (count == 3) com_detect = 4; break; //ATI
		default: com_detect = 0;
		}
	}


Распознали команду. Количество символов я ввёл для надёжности на случай если в длинной команде XOR код совпадёт. Распознаваемые команды можно добавлять. Нужно только подсчитать (или макросом) XOR код желаемой команды и присвоить ей цифру.

Теперь в com_detect у нас полученная команда. Теперь устройство может отреагировать SMS сообщением на полученный звонок:


while (1)
    {
    if (mess != 0) //if we have mess in buffer
	{
	// code 
	mess--;   //minus one
	rx_check_in ();  //распознаём отдельную команду
	if (com_detect == 2)   //если была команда RING (код 2)
	    {                                  //Посылаем сообщение
                                                // и принимаем входящие команды (OK)
	    if (!send_sms (1,NUM0)) ErrMes (); //если после отправки не было команды OK
	    }                                  //тогда выдать сообщение о ошибке протокола
	com_detect = 0; //обнуляем команду
    }


Так можно обрабатывать разные полученные команды.

Итог: устройство умеет отправлять сообщение на телефон и умеет реагировать на различные команды от GSM модуля.

Спасибо за внимание.

Готовый проект для Pinboard II выкладываю как пример.
  • +2
  • 23 сентября 2013, 17:00
  • ilus
  • 4
Файлы в топике: pb_2.jpg, shema_2.jpg, bku46i5VJJ8.jpg, tc35.jpg

Комментарии (18)

RSS свернуть / развернуть
Какое прерывание происходит, когда модуль отправляет команды?
0
Прерывание опустошение регистра отправки. Когда аппарат отправки забирает в себя байт данных из UDR, регистр пуст и происходит прерывание. Сразу записываем туда следующий байт из буфера.
0
Оригинально Вы придумали — сравнивать не строку ответа а хеш от строки. Немного смущают несколько моментов

1. Магические цифры — case (0x04). Но от этого можно уйти (например через макрос)
2. Вероятность коллизии хеш.
3. Вы ожидаете, что ответ не будет содержать дополнительных символов (в принципе так и должно быть послали АТ получили ОК). Но, например, в модеме SIM300D есть полезная штука — indications. Indications – это когда модем самостоятельно инициирует вывод в консоль, по какому-то событию. Этот режим можно выключить, но иногда он полезен. Например, при получении SMS, модем посылает (без запроса) хосту что-то типа «+CMTI: ….». Причем, это может случится в любой момент, даже тогда когда мы ожидаем ответ. Тобиш, может возникнуть ситуация, когда мы послали АТ, а в ответ получили

+CMTI: …
ОК


В этом случае хеш не сойдется, хотя ОК мы получили.

Но идея интересная, спасибо за статью.
0
  • avatar
  • e_mc2
  • 23 сентября 2013, 21:16
Магические цифры — есть… Пока не полностью избавился. Макрос пока не написал.
На счёт OK — общий не сойдётся, но я беру за сообщение отдельные строки — от ентера до ентера.
То есть для меня

+CMTI: ..

и

OK


две отдельные команды ответа. То есть OK мы получим как 0х04
0
Вообще, у модуля есть команда ATV Set result code format mode, которая позволяет перевести его в режим ответа Numeric format и тогда вместо ОК будет приходить 0, вместо CONNECT — 1, вместо RING — 2 и т.д. Их гораздо проще отлавливать и парсить.
0
А сообщение как прочитать и распознать?
0
а причем тут сообщение? как я понял речь идет о том как ответы типа ОК, ERROR и пр. отличить друг от друга используя минимум ресурсов хоста. Может я не так понял.
0
Всё верно, режим, конечно, полезный. И вполне подходит к устройству.
Я имел ввиду, если, например, понадобится распознать сообщение или номер входящего звонка. (приведённая в топе программа не делает этого)
0
Всмысле отловить сообщение, текст которого (вернее его хеш) заложен в программе? Или номер звонящего сравнить с разрешенным номером? Если да — то конечно что-то в этом есть, но XOR наверно мало, хотя бы двухбайтовую контрольную сумму считать нужно. А так великовата вероятность совпадения)). Это варианты ответа модуля можно все посчитать и увидеть что XOR у них разный, с неизвестным заранее текстом такое может и не прокатить.
0
В зависимости от настроек модуля, он может сразу выдавать что-то подобное:

RING +123456789

По команде RING (получена команда — 2) срабатывает функция сравнивания номера (из следующей пачки полученных символов)
0
Странная это идея — команды ловить управляющему контроллеру. Ведь он их сам и шлёт.
Ответы надо обрабатывать, а не свои же команды. И не xor-ом уж точно.
0
Он не свои команды получает, а именно те ответы, что с модуля приходят. К примеру, получено сообщение или получен входящий звонок. Или ответы на команды — OK или ERROR.
Можно не xor'ом. Тогда чем? Сравнивать все входящие команды с эталонными? Много памяти программы уйдёт. Может подскажешь другой алгоритм?
0
Он не свои команды получает, а именно те ответы, что с модуля приходят.
А, понял…
Можно не xor'ом.
xor ну никак не годится.
Тогда чем
Ну допустим на команду должно прийти несколько ответов — OK, ERROR, NO CARRIER. Тогда с этими строками и сравнивать.
Много памяти программы уйдёт.
Для SMS/GPRS/CSD и прочего и 1кБ вроде как не ушло у меня в программе. Хотя в m16 процент заметный. Но оно и понятно что такие вещи других МК требуют. У меня раньше была mega128, сейчас xmega192 с 16кБ ОЗУ.
0
у меня на самом устройстве mega8 стоит. Это ещё меньше памяти. Сейчас у меня где-то 2 кб программы — это ещё без функции получения смс сообщений.
Полностью согласен, что лучше всё-таки сравнивать с записанными в память. Но чем xor тут плох? Я пересчитал больше 20 разных команд и ни разу не совпал по xor сумме. Вдобавок я проверяю длинну команды.
0
у меня на самом устройстве mega8 стоит
Да, тяжело…
Но чем xor тут плох
Ну если так мало памяти, то может и xor сойдёт. Хотя лучше бы сравнивать с тем что надо, а не хешом каким-то. Не так уж много памяти оно требует, вам ведь GPRS/СЫВ/АЕЗ поднимать не надо? Да на 8кБ это сомнительно всё.
0
Для хэширования строк не очень дорога хэш-функция Пирсона
0
Очень ценная информация, спасибо! Думаю, тут подойдёт.
0
Подскажите пожалуйста.
А где заказывали платы? Минимальная партия? Сроки? Цены?
-1
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.