Отправка и получение SMS, используя GSM модуль SIM300

В этой части статьи мы рассмотрим функции, связанные с текстовыми сообщениями. К концу этой статьи вы будете иметь точное представление о том, как ожидать текстовое сообщение, читать сообщение, отправлять новое текстовое сообщение и удалять полученное сообщение. Мы уже разбирали основы в предыдущей статье Интерфейс связи между SIM300 и ATmega32. В ней приводиться схема и команды с примером кода для модуля SIM300.

После прочтения предыдущей статьи вы узнали, как соединить SIM300, AVR ATmega32 и ЖК-дисплей, чтобы получить испытательный стенд. Кроме того, в статье подробно рассматривается  асинхронная последовательная связь между SIM300 и микроконтроллером AVR, которая осуществляется с помощью AVR USART. В статье показано использование библиотеки AVR USART для передачи и приема данных в/от GSM модуля. Мы также рассмотрели функцию возврата команды, используемую для работы с модулем, работа с ней показана в демонстрационном коде. Все эти команды используются в этой статье, поэтому мы рекомендуем вам прочесть предыдущую статью и собрать демонстрационный проект, показанный там.

Ожидание текстового сообщения (SMS)

Если текстовое сообщение (SMS) поступает на GSM модуль SIM300, то он отправляет незапрашиваемый ответ <CR> <LF> + CMTI: <mem>, <n> <CR> <LF>

В предыдущей статье уже говорилось, что <CR> является управляющим символом,  ASCII код которого  0D(Hex) и <LF>,  ASCII код которого 0A (Hex). Вы узнаете о новой вещи – незапрашиваемый ответ. Ранее говорилось, что за командой следует ответ. Но ответ показанный выше, не привязан к команде и может прийти в любой момент. Это незапрашиваемый ответ (unsolicited response).

Значение mem обозначает место хранения SMS сообщения. Обычно это значение SM, что означает память SIM-карты.

Значение n означает ячейку, в которой хранится SMS. В зависимости от размера памяти вашей SIM-карты, в ней 20 или около того ячеек. При получении сообщения, оно сохраняется с наименьшим номером в пустую ячейку.  Допустим, вы получили 4 сообщения, и удалили 1, и тогда 5 сообщение сохраниться в первую ячейку.

Пример кода, показывающий ожидание текстового сообщения:

  int8_t   SIM300WaitForMsg(uint8_t *id)  {     //Wait for a unsolicited response for 250ms       uint8_t len=SIM300WaitForResponse(250);       if(len==0)        return SIM300_TIMEOUT;       sim300_buffer[len-1]='';       //Check if the response is +CMTI (Incoming msg indicator)     if(strncasecmp(sim300_buffer+2,"+CMTI:",6)==0)     {        char str_id[4];          char *start;          start=strchr(sim300_buffer,',');        start++;          strcpy(str_id,start);          *id=atoi(str_id);          return SIM300_OK;     }     else        return SIM300_FAIL;  }  

Анализ кода

1. Мы ждем ответа от SIM300 с тайм-аутом 250 миллисекунд. Это значит, что если ничего не приходит в течение 250 миллисекунд, мы перестаем ждать!
2. Если мы получили ответ, функция SIM300WaitForResponse () сообщает его длину до последнего <CR>. Поэтому если мы получили ответ <CR><LF>+CMTI: SM,1<CR><LF> то len будет 14.
3. Следующая строка sim300_buffer[len-1]=’’; Ставит нулевое значение на место len-1, и теперь у нас 13 знаков (конец ответа <CR>). Ответ становится таким <CR><LF>+CMTI: SM,1
4. Теперь мы проверяем, что первые 6 символов +CMTI: , вернее, мы проверяем первые 6 символов только потому, что за +CMTI следует <n>. Как вы помните, это номер ячейки, в которой хранится сообщение. Также при сравнении мы пренебрегаем регистром, а это значит, что CMTI+ или +cMtI совпадают. Этот тип сравнения легко реализуется с помощью стандартной функции strncasecmp () из стандартной библиотеки С . Если вы учили C, то вы должны помнить STRCMP(), и поймете strncasecmp ().В названии функции добавилось только n и case. n в названии указывает на то, что не нужно сравнить всю строку, а нужно проверить только первые n букв. case в названии указывает на сверку без учета регистра. Также вы заметили, что мы сравниваем не непосредственно sim300_buffer (в нем находиться ответ), а используем sim300_buffer+2, что убирает первые <LF><CR> в ответе.
5. Если ответ верен, то необходимо извлечь значение <n>, то есть номер ячейки в которой хранится сообщение. Поэтому мы ищем первую запятую (,) в ответе. Это делается с помощью функции strchr() из стандартной библиотеки. Запустите функцию указав строку с ",1".
6. Мы запускаем поиск командой start++, и находим строку с ",1", но помните, что это ещё строка. Поэтому мы используем стандартную функции библиотеки atoi(), которая преобразует строку в целое число. Его мы храним в *id. Помните, что параметр id переходит по ссылочному типу, если вы не знаете этого, то перечитайте вашу книгу по C.
7. Наконец мы отравляем константу SIM300_OK, которая указывает на успех и определенна в sim300.h

Чтение содержания текстовых сообщений

Для чтения текстового сообщения используется команда AT+CMGR=<n> где <n> целое значение, обозначающее номер ячейки из которой необходимо считать сообщение.  Как я уже говорил, что их несколько слотов для входящих сообщений.

Ответ выглядит так:
+CMGR: "STATUS","OA",,"SCTS"<CR><LF>Message Body<CR><LF><CR><LF>OK<CR><LF>

STATUS показывает состояние сообщения. Его значение может быть REC UNREAD или REC READ.

ОА — Originating Address (Адрес отправителя). Показывает номер мобильного телефона отправителя.

SCTS (Service Center Time Stamp) — Время получения сообщения

Этот простой пример для того, чтобы убрать лишнюю информацию, т.е. STATUS, OA и SCTS и получить только само сообщение.

При попытке чтения сообщения может случиться три события:
1. Успешное чтение. В этом случае мы получаем ответ показанный выше.
2. Пустая ячейка! Это означает, что была сделана попытка чтения содержимого ячейки, но она пуста. В этом случае ответ такой: <CR><LF>OK<CR><LF>
3. SIM-карта не готова! В этом случае возвращается ошибка +CMS ERROR: 517.

Наша функция обрабатывает все три ситуации.

  int8_t   SIM300ReadMsg(uint8_t i, char *msg)  {     //Clear pending data in queue     UFlushBuffer();       //String for storing the command to be sent     char cmd[16];       //Build command string     sprintf(cmd,"AT+CMGR=%d",i);       //Send Command     SIM300Cmd(cmd);       uint8_t len=SIM300WaitForResponse(1000);       if(len==0)        return SIM300_TIMEOUT;       sim300_buffer[len-1]='';       //Check of SIM NOT Ready error     if(strcasecmp(sim300_buffer+2,"+CMS ERROR: 517")==0)     {        //SIM NOT Ready        return SIM300_SIM_NOT_READY;     }       //MSG Slot Empty     if(strcasecmp(sim300_buffer+2,"OK")==0)     {        return SIM300_MSG_EMPTY;     }       //Now read the actual msg text     len=SIM300WaitForResponse(1000);       if(len==0)        return SIM300_TIMEOUT;       sim300_buffer[len-1]='';     strcpy(msg,sim300_buffer+1);//+1 for removing trailing LF of prev line       return SIM300_OK;  }  

Анализ кода

1. Отменяем ожидание данных буфером.
2. Командная строка пишется с использование функции sprintf() из библиотеки.  
   1. sprintf(cmd,"AT+CMGR=%d",i);
   2. В этой строке значение %d заменяется на значение i.
3. Команда отправляется модулю SIM300.
4. Ожидание ответа.
5. Анализ ответа.
6. Наконец сообщение читается и копируется в память указанную *msg с использованием функций стандартной библиотеки strcpy().

Отправка текстового сообщения

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

Num (IN) — Номер телефона, на который надо отправить сообщение "+7903 XXXXXXX"
MSG (IN) — Текст сообщения "Текст сообщения"
msg_ref (OUT) — После успешной отправки, функция сохраняет уникальный указатель сообщения в этой переменной.

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

SIM300_TIMEOUT – Такой ответ приходит при проблемах с линией связи или когда GSM модуль не отвечает или выключен.
SIM300_FAIL – Не удалось отправить сообщение. Возможно, недостаточно денег на счету.
SIM300_OK — Сообщение отправлено успешно!

  int8_t   SIM300SendMsg(const char *num, const char *msg,uint8_t *msg_ref)  {     UFlushBuffer();       char cmd[25];       sprintf(cmd,"AT+CMGS= %s",num);       cmd[8]=0x22; //"       uint8_t n=strlen(cmd);       cmd[n]=0x22; //"     cmd[n+1]='';       //Send Command     SIM300Cmd(cmd);       _delay_ms(100);       UWriteString(msg);       UWriteData(0x1A);  	  	//Wait for echo     while(   UDataAvailable()