Подключение SIM800 к ESP32. Прием и отправка SMS сообщений.

Начну с подключения GSM модуля SIM800. Хотя оно подробно описано в серии статей по этому GSM модулю, но есть один нюнас. Во всех предыдущих статьях я говорил, что модуль потребляет до 2A в пике, поэтому его нельзя запитывать от USB порта ноутбка. Рассмотрю, как запитывать:

Немало вопросов в интернет как параллельно запитать микроконтроллер и GSM модуль, чтобы можно было подключать USB кабель для заливки прошивки.

Подключение внешего источника питания к ESP32 и SIM800

Как подключить внешний мощный источник питания 5V к ESP32, чтобы запитать SIM800, потребляющий до 2A в пике? Рассмотрим, сделано ли что-то схематике ESP32/ESP8266, чтобы не сжечь USB порт ПК при подключении внешнего источника питания.

На схеме от производителя виден диод Шоттки BAT-760 в цепи питания +5V. Этот диод присутствует не на всех платах, поэтому прежде чем подключить USB от ПК к плате запитанной от внешнего источника +5V нужно убедится, что на плате диод распаян. Даже если нет схемы платы, то проверить несложно. Черный планарный диод относительно большого размера хорошо выделяется на плате. Катод диода идет на PIN +5V, а анод на вывод +5V питания USB разъема.
Если диода нет, то его нужно добавить в схему питания.

Получение SMS. Инициализация.

Для начала нужно проверить, что тариф допускает отправку сообщений. Например, у Билайна на SIM-картах привязанных к основной голосовой но для использования в планшете, ноутбуке и пр. под Интернет нет опции отправки SMS. Можно потратить немало времени, пытаясь отправить SMS с такой SIM-ки.

Для работы с SIM800L я использую библиотеку TinyGSM.

Для отправки SMS используется простой код:

  bool res = modemGSM.sendSMS("+7960XXXXXX", "GSM modem with IMEA " + modemGSM.getIMEI() + " has started.");
  Serial.println("SMS sending status: " + String(res ? "OK" : "fail"));

Он простой и хорошо работает.

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

AT команда AT+CNMI определяет будет ли принятое сообщение буферизовываться на SIM карте или будет лишь отображаться и передаваться напрямую в TE (например, микроконтроллеру). В последнем случае на SIM карте не будут сохранятся полученные SMS. Это довольно эксремальный вариант работы, поскольку в случае если по каким-то причинам микроконтроллер не вычитал SMS, то может быть проигнорирована важная команда управления.

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

Для инициализации я использую несколько команд:

void smsInit()
{
        modemGSM.sendAT(GF("+CPMS=SM,SM,SM")); //Storage to store SMS
        modemGSM.waitResponse();

        modemGSM.sendAT(GF("+CMGF=1")); //Text type messages instead of PDU
        modemGSM.waitResponse();

        modemGSM.sendAT(GF("+CNMI=1,0"));
        modemGSM.waitResponse();

        modemGSM.sendAT(GF("+CSCS=\"GSM\""));
        modemGSM.waitResponse();
}

AT+CPMS задает память для сохранения SMS сообщений:

  • SM – Память SIM-карты
  • ME – Память модема/телефона
  • MT – Это общая память SIM-карты и модема, т.е. MT = SM+ME
  • BM – Память для широковещательных сообщений сети
  • SR – Память для отчетов (о доставке и т.п.)

Память модема делится на три логических секции и поэтому при вызове +CPMS можно передавать три аргумента:

  • Первая — для просмотра, чтения и удаления сообщений
  • Вторая — для сохранения и отправки исходящих сообщений.
  • Третья — только для вновь полученных сообщений.

Я не нашел примеров работы с этими тремя видами памяти. Если кто-то найдет информацию — буду благодарен, если поделитесь. Для надежности буду использовать только SM.

Далее задаем формат SMS сообщений AT+CMGF=1 (тесктовые сообщения). Для формата PDU используется AT+CMGF=0. Формат PDU
(Protocol Data Unit) используется по умолчанию. Он более информативный, но и более сложный для парсинга. Для управления IoT устройством достаточно простого текстового формата.

Например, команда AT+CNMI=1,2,0,0,0 говорит модулю SIM800 либо направлять толкьо что полученные SMS сообщения напрямую микроконтроллеру, или сохранять их в хранилище сообщений и затем уведомлять микроконтроллер о их размещении в хранилище. При использовании этой команды пришедшее SMS приходит в Serial порт микроконтроллера в виде:

15:52:58.028 -> +CMT: "+79601XXXXX","","19/05/02,15:52:53+12"
15:52:58.062 -> Test again

При этом пришедшие SMS сообщения не сохраняются в памяти SIM-карты.

Чтобы пришедшее SMS сообщение сохранилось в памяти SIM-ки нужно использовать команду AT+CNMI=1,0 или AT+CNMI=0,0. Мне не удалось найти обстоятельно описанные примеры использования команды.

Последняя команда AT+CSCS=»GSM» в функции инициализации устанавливает charset «GSM» для TE. Возможные значения:

  • «GSM» GSM 7 bit default alphabet (3GPP TS 23.038);
  • «UCS2» 16-bit universal multiple-octet coded character set (ISO/IEC10646); UCS2 character strings are converted to hexadecimal numbers from 0000 to FFFF; e.g. «004100620063» equals three 16-bit characters with decimal values 65, 98 and 99
  • «IRA» International reference alphabet (ITU-T T.50)
  • «HEX» Character strings consist only of hexadecimal numbers from 00 to FF;
  • «PCCP» PC character set Code
  • «PCDN» PC Danish/Norwegian character set «8859-1» ISO 8859 Latin 1 character set

Отправка SMS

При использовании указанной выше команды AT+CNMI=1,0 при приходе SMS строчек

09:33:19.153 -> +CMT: "+79601XXXXX","","19/05/05,09:33:15+12"
09:33:19.187 -> Test the SMS send

индициирующих о приходе сообщения не будет. Нужно вычитывать сообщения. Чтобы вычитать все SMS сообщения я написал функцию:

enum ReadSMSMode
{
  ReceivedUnread,
  ReceivedRead,
  StoredUnsent,
  StoredSent,
  All
};

void readAllSMSs(ReadSMSMode mode = ReadSMSMode::AllSMS)
{
  smsInit();
  String readMode = "ALL"; 
  switch (mode)
  {
    case ReadSMSMode::ReceivedUnread:
      readMode = "REC UNREAD";
      break;
    case ReadSMSMode::ReceivedRead:
      readMode = "REC READ";
      break;
    case ReadSMSMode::StoredUnsent:
      readMode = "STO UNSENT";
      break;
    case ReadSMSMode::StoredSent:
      readMode = "STO SENT";
      break;
    case ReadSMSMode::AllSMS:
      readMode = "ALL";
      break;
  }
  
  modemGSM.sendAT(GF("+CMGL=\"" + readMode + "\"")); 
  Serial.println("Read all SMSs.");
  while (true)
  {
    if (modemGSM.waitResponse(10000L, GF(GSM_NL "+CMGL:"), GFP(GSM_OK), GFP(GSM_ERROR))) 
    {
      String data = modemGSM.stream.readStringUntil('\n');
      data.trim();
      if (data.length() == 0)
        break;
      Serial.println("Data: " + data);
      String msg = modemGSM.stream.readStringUntil('\n');
      Serial.println("Message: " + msg);
    }
    else
    {
      break;
    }
  }
}

Чтобы прочитать SMS с определенным id функция:

void readSMS(uint8_t i)
{
  char message[300];
  
  modemGSM.sendAT(GF("+CMGF=1"));
  modemGSM.waitResponse();
  modemGSM.sendAT(GF("+CNMI=1,2,0,0,0"));
  modemGSM.waitResponse();
  
  modemGSM.sendAT(GF("+CMGR="), i);
  if (modemGSM.waitResponse(10000L, GF(GSM_NL "+CMGR:"))) 
  {

    String header = modemGSM.stream.readStringUntil('\n');
    String body = modemGSM.stream.readStringUntil('\n');

    Serial.println("Header: " + header);
    Serial.println("Body: " + body);
  }
}

После того как SMS сообщение получено, распарсено и команда отработана, сообщение его лучше удалить. Для удаления SMS сообщения с определенным id используется функция:

bool deleteSmsMessage(const uint8_t index) 
{
    modemGSM.sendAT(GF("+CMGD="), index, GF(","), 0); // Delete SMS Message from <mem1> location
    return modemGSM.waitResponse(5000L) == 1;
}

Для удаления сообщений определенного типа использую функцию:

enum DeleteSmsMode
{
  Read     = 1,
  Unread   = 2,
  Sent     = 3,
  Unsent   = 4,
  Received = 5,
  All      = 6
};

bool deleteAllSmsMessages(DeleteSmsMode method);
bool deleteAllSmsMessages(DeleteSmsMode method) 
{
    // Select SMS Message Format: PDU mode. Spares us space now
    modemGSM.sendAT(GF("+CMGF=0"));
    
    if (modemGSM.waitResponse() != 1) {
        return false;
    }

    modemGSM.sendAT(GF("+CMGDA="), static_cast<const uint8_t>(method)); 
    const bool ok = modemGSM.waitResponse(25000L) == 1;

    modemGSM.sendAT(GF("+CMGF=1"));
    modemGSM.waitResponse();

    return ok;
}

Результат

22:21:40.333 -> Read all SMSs.
22:21:40.367 -> Data: 1,"REC READ","+79601XXXXX","","19/05/05,16:52:16+12"
22:21:40.435 -> Message: Test again. 

22:21:40.469 -> Data: 2,"REC READ","+79601XXXXX","","19/05/05,16:53:01+12"
22:21:40.502 -> Message: And again

22:21:40.537 -> Data: 3,"REC READ","+79601XXXXX","","19/05/05,17:12:05+12"
22:21:40.604 -> Message: And one again

22:21:40.637 -> Data: 4,"REC READ","+79601XXXXX","","19/05/05,17:58:59+12"
22:21:40.706 -> Message: Ones again

22:21:40.706 -> Data: 5,"REC READ","+79601XXXXX","","19/05/05,21:27:58+12"
22:21:40.777 -> Message: New message again. 

Полезные ссылки

Spread the love
Запись опубликована в рубрике IT рецепты с метками , , , , , . Добавьте в закладки постоянную ссылку.