Взаимодействие с GSM/GPRS Shield для работы с SMS и голосовыми вызовами

GPRS Shield от Seeed Studio — это плата расширения, позволяющая Arduino работать в сетях сотовой связи по технологиям GSM/GPRS для приёма и передачи данных, SMS и голосовой связи.

Плата построена на базе модуля SIMCom SIM900. Также на ней расположены: слот для SIM-карты, стандартные 3,5 мм джеки для аудио-входа и выхода и разъём для внешней антенны.

Общение с платой производится через serial-соединение с помощью набора AT-команд. AT commands — язык инструкций, изначально разработанный для управления настройками модемов, однако оказался настолько удобным, что было выпущено несколько стандартов для мобильных устройств. AT — это просто текстовый протокол, в котором в качестве префикса отдельной команды используется строка AT (от англ. attention), а название и параметры следуют далее так же в текстовом виде. Набор допустимых команд и их параметры описываются стандартом, который поддерживает конкретное устройство. Сейчас наиболее используемые стандарты: GSM 07.07, 07.05.

С помощью джамперов на плате возможно установить используемые для коммуникации контакты: аппаратные 0-й и 1-й или 7-й и 8-й для работы через SoftwareSerial-эмуляцию. По этому каналу в итоге и будут пересылаться AT-команды.

Посмотреть описание и минимальные примеры можно на странице производителя. Мануал для изучения AT команд можно скачать у них же.

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

Что понадобится

Для нескольких демонстраций нам понадобятся следующие составляющие:

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

Приём звонков

Установим SIM-карту на GPRS Shield, а GPRS Shield — на Arduino. С помощью джамперов соединим контакты для работы через SoftwareSerial-эмуляцию.

Необходимые AT-команды

  • AT — простейшая команда, при которой GPRS Shield ничего не делает, в случае правильного подключения возвращает в ответ OK
  • ATA — команда принять входящий вызов. Сам факт вызова устанавливается регулярно приходящими с платы во время звонка сообщениями RING
  • AT+CHUP — отклонить вызов
  • AT+CLCC — позволяет узнать номер звонящего

Код примера

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

Call_receiving.ino
#include <SoftwareSerial.h>
 
//заводим Serial-соединение с GPRS-Shield на 7 и 8 цифровых входах
SoftwareSerial gprsSerial(7, 8);
 
void setup()
{
    // GPRS Shield общается по умолчанию на скорости 19200 бод
    gprsSerial.begin(19200);
}
 
//в строке curStr будем хранить текущую строку, которую передает нам плата
String currStr = ""; 
int updateTime = 0;
 
void loop()
{
    touch();
 
    if (!gprsSerial.available())
        return;
 
    // Считываем очередной символ с платы
    char currSymb = gprsSerial.read();    
 
    if ('\r' == currSymb) { 
        // Получен символ перевода строки, это значит, что текущее
        // сообщение от платы завершено и мы можем на него отреагировать.
        // Если текущая строка - это RING, то значит, нам кто-то звонит
        if (!currStr.compareTo("RING")) {
            //кокетничаем 3 секунды, чтобы дать услышать звонящему гудок
            delay(3000);
            //посылаем команду на поднятие трубки
            gprsSerial.println("ATA");
        }
        currStr = "";
    } else if (currSymb != '\n') {
        // Дополняем текущую команду новым сиволом
        // При этом игнорируем второй символ в последовательности переноса
        // строки: \r\n
        currStr += String(currSymb);
    }
}
 
/*
 * Функция провоцирует поддержание коммуникации с платой даже если
 * та была перезагружена без перезагрузки Arduino
 */
void touch()
{
    if (millis() >= updateTime) {
        gprsSerial.println("AT");
        updateTime += 5000;
    }  
}

Если просто подключить наушники и микрофон в соответствующие разъёмы, после приёма звонка с абонентом можно будет общаться точно так же как и по обычному телефону.

Осуществление звонка при нажатии кнопки

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

Итак, устройство будет выглядить приблизительно так:

Необходимые AT-команды

  • ATD + x — набрать номер x. Если звонок не состоялся, например, нет сигнала базовой станции, то плата отвечает NO DIALTONE.

Код примера

Код будет следующим:

Call_button.ino
#include <SoftwareSerial.h>
 
SoftwareSerial gprsSerial(7, 8);
 
int btnPin = 12;
boolean prevBtn = LOW;
 
void setup()
{
    gprsSerial.begin(19200);
}
 
void loop()
{
    boolean currBtn = digitalRead(btnPin);
    if (prevBtn != currBtn && currBtn == HIGH) {
        //сразу после нажатия кнопки начинаем звонить по заданному номеру
        gprsSerial.println("ATD + +79031034424;");
    }
    prevBtn = currBtn;
}

Отправка SMS при нажатии кнопки

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

Необходимые AT команды

  • AT+CMGF=1 — устанавливает текстовый режим для SMS-сообщений
  • AT + CMGS = "x" — устанавливает в качестве получателя смс номер x
  • (char)26 или ctrl+z — завершает текст сообщения, после этого символа сообщение начинает отправляться

Код примера

Alarm_button.ino
#include <SoftwareSerial.h>
 
SoftwareSerial gprsSerial(7, 8);
 
int btnPin = 12;
boolean prevBtn = LOW;
 
void setup()
{
    gprsSerial.begin(19200);
    delay(500);
}
 
void loop()
{
    boolean currBtn = digitalRead(btnPin);
    if (prevBtn != currBtn && currBtn == HIGH) {
        //сразу после нажатия кнопки отправляем "тревожное" сообщение по заданному номеру
        sendTextMessage();
    }
    prevBtn = currBtn;
}
 
/*
 * Функция отправки SMS-сообщения
 */
void sendTextMessage() {
    // Устанавливает текстовый режим для SMS-сообщений
    gprsSerial.print("AT+CMGF=1\r");
    delay(100); // даём время на усваивание команды
    // Устанавливаем адресата: телефонный номер в международном формате
    gprsSerial.println("AT + CMGS = \"+79031034423\"");
    delay(100);
    // Пишем текст сообщения
    gprsSerial.println("ALARM!");
    delay(100);
    // Отправляем Ctrl+Z, обозначая, что сообщение готово
    gprsSerial.println((char)26);
}

Управление светодиодами с помощью SMS

Теперь попробуем немного поуправлять устройством с помощью SMS. Для этого соберём девайс с двумя светодиодами:

Сделаем так, чтобы можно было посылать SMS устройству и видеть ответные действия:

  • Green on — включается зелёный светодиод
  • Green off — выключается зелёный светодиод
  • Yellow on — включается жёлтый светодиод
  • Yellow off — выключается жёлтый светодиод

Необходимые AT-команды

  • AT+CMGF=1 — устанавливает текстовый режим смс-сообщения
  • AT+IFC=1, 1 — устанавливает программный контроль потоком передачи данных
  • AT+CPBS="SM" — открывает доступ к данным телефонной книги SIM-карты
  • AT+CNMI=1,2,2,1,0 — включает оповещение о новых сообщениях, новые сообщения приходят в следующем формате: +CMT: "<номер телефона>", "", "<дата, время>", а на следующей строчке с первого символа идёт содержимое сообщения

Код примера

Будем использовать следующий код:

gprs_lights.ino
#include <SoftwareSerial.h>
 
SoftwareSerial gprsSerial(7, 8);
 
//для зелёного светодиода будем использовать второй цифровой вход,
//а для жёлтого - третий
int greenPin = 2;
int yellowPin = 3;
 
void setup()
{
    gprsSerial.begin(19200);
    pinMode(greenPin, OUTPUT);
    pinMode(yellowPin, OUTPUT);
 
    // Настраиваем приём сообщений с других устройств
    // Между командами даём время на их обработку
    gprsSerial.print("AT+CMGF=1\r");
    delay(300);
    gprsSerial.print("AT+IFC=1, 1\r");
    delay(300);
    gprsSerial.print("AT+CPBS=\"SM\"\r");
    delay(300);
    gprsSerial.print("AT+CNMI=1,2,2,1,0\r");
    delay(500);
}
 
String currStr = "";
// Переменная принимает значение True, если текущая строка является сообщением
boolean isStringMessage = false;
 
void loop()
{
    if (!gprsSerial.available())
        return;
 
    char currSymb = gprsSerial.read();    
    if ('\r' == currSymb) {
        if (isStringMessage) {
            //если текущая строка - SMS-сообщение,
            //отреагируем на него соответствующим образом
            if (!currStr.compareTo("Green on")) {
                digitalWrite(greenPin, HIGH);
            } else if (!currStr.compareTo("Green off")) {
                digitalWrite(greenPin, LOW);
            } else if (!currStr.compareTo("Yellow on")) {
                digitalWrite(yellowPin, HIGH);
            } else if (!currStr.compareTo("Yellow off")) {
                digitalWrite(yellowPin, LOW);
            }
            isStringMessage = false;
        } else {
            if (currStr.startsWith("+CMT")) {
                //если текущая строка начинается с "+CMT",
                //то следующая строка является сообщением
                isStringMessage = true;
            }
        }
        currStr = "";
    } else if ('\n' != currSymb) {
        currStr += String(currSymb);
    }
}

Результат

Библиотека SIM900

Конечно, управлять GPRS-устройством напрямую через AT-команды не всегда удобно. Их по крайней мере постоянно нужно держать в голове. Некоторая функциональность была реализована в библиотеке SIM900 доступной на Google Code.

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

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

Включение GPRS Shield v2

GPRS Shield ревизии 2 несколько отличается от первой ревизии наличием кнопки Power, включающей и выключающей модуль. После подачи питания на GPRS Shield, для его включения необходимо нажать эту кнопку в течении 2-х секунд. После повторного нажатия плата расширения выключится.

Включить модуль можно и программно. Для этого необходимо подать высокий уровень на пин 9 на 3 секунды, а затем подать низкий уровень на эту ножку.

Пример программного включения модуля:

GPRS_power_On.ino
void setup()
{
  //Включаем GPRS Shield, эмулируя нажатие кнопки POWER
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);    // Подаем High на пин 9
  delay(3000);              // на 3 секунды
  digitalWrite(9, LOW);     // и отпускаем в Low. 
  delay(5000);              // Ждём 5 секунд для старта шилда
}
void loop()
{
  //Пока ничего больше не делаем
}