31 августа 2017
Кравченко Виктор

GSM-модуль SIM800L: часть 2 — программирование, звонки, прием/отправка SMS, управление по SMS

Цифровые устройства Arduino Arduino Lang Умный дом Датчики, модули Протоколы Микроконтроллеры и мини ПК
01
Содержание:
02
У данной статьи есть видеоверсия!
Подписывайтесь на канал, чтобы быть в курсе обновлений!

03

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

04
Для полноценной работы с GSM/GPRS-модулем SIM800L понадобится официальный справочник по AT-командам — SIM800 Series_AT Command Manual_V1.10.pdf (4,01 MB).
05

Перед тем как приступить к программированию взаимодействия с GSM-модулем, необходимо ознакомиться с типами AT-команд.

06

Синтаксис AT-команд

Как уже было сказано выше, общение с модулем происходит при помощи AT-команд. Каждая AT-команда должна начинаться с двух букв AT, набранных в любом регистре: AT, aT, At, at. Команда должна начинаться с новой строки (параметр Newline в окне Serial).

07

Все AT-команды синтаксически делятся на 3 основные группы: базовые, с параметром S и расширенные.

08

Базовые команды имеют следующий синтаксис: AT<x><n> или AT&<x><n>, где <x> является командой, а <n> — передаваемым параметром (параметрами). Параметр(-ы) <n> является необязательным и в случае его отсутствия будет использовано значение по-умолчанию. Примерами могут служить, отправляемая ранее, команда ATI, возвращающая идентификационные данные модуля, или команда AT&V возвращающая текущую конфигурацию модуля.

09 На заметку:
Команды ATV и AT&V — это разные команды.
10

Команды с параметром S выглядят следующим образом: ATS<i>=<m>, где <i> — индекс S-регистра, а <m> — значение, которое ему необходимо присвоить. В случае отсутствия значения <m>, будет присвоено значение по-умолчанию. Например, регистр ATS0 отвечает за количество гудков перед автоматическим ответом на входящий вызов, и, соответственно, команда ATS0 установит значение по умолчанию — 0 (не отвечать на вызов), а команда ATS0=2 заставит отвечать модуль автоматически после 2 гудка.

11

Расширенные команды могут вызываться в нескольких режимах:

AT-команды (от англ. ATtention «внимание») — набор команд, состоящий из серий коротких текстовых строк, которые объединяются вместе, чтобы сформировать полные команды операций, таких как набор номера, начала соединения или изменения параметров подключения.

Подробно с полным набором AT-команд можно ознакомиться в официальном руководстве — SIM800 Series_AT Command Manual_V1.09.pdf (3,07 MB)
12
Режим Синтаксис Описание
Справка AT+<x>=? Модуль возвращает список возможных параметров и диапазон, принимаемых ими значений
Чтение состояния AT+<x>? Модуль возвращает текущее состояние параметра(-ов)
Команда записи AT+<x>=<...> Команда устанавливает значения параметров, определяемых пользователем
Выполнение команды AT+<x> Исполнение команды
13

Примером использования расширенных команд могут являться команды для управления FM-радио — AT+FMOPEN, AT+FMCLOSE и т.д. Об этом будет подробно рассказано в соответствующем разделе данной статьи.

14

В качестве примера данной группы команд, также можно привести команду AT+IPR. Командой AT+IPR=? можно узнать значения, которые может принимать параметр. Командой AT+IPR? можно узнать текущую скорость обмена данными с модулем, а командой AT+IPR=<...> — задать другую скорость.

15
16 На заметку:
Для того, чтобы использовать скорости выше 9600 бод, необходимо спаивать элементы схемы. В схеме, собранной на макетной плате, при обмене данными на высоких скоростях возникают проблемы.
17

В одной строке можно указывать несколько команд. Все последующие команды (исключая первую) записываются без префикса AT. После каждой расширенной команды необходимо ставить точку с запятой ;. После базовых команд или команд с параметром S, точка с запятой ; не нужна. Пример:

18
1
ATV0E1+DDET=1,0,1;Q0S0=1+CLIP=1;+CMGF=1;&W
19

Длина всей строки с несколькими командами не должна превышать 556 символов (не считая начального префикса AT).

20

AT-командой A/ можно повторить предыдущую команду.

21

За дублирование в терминале отправленной команды отвечает параметр Echo Mode. Для изменения значения этого параметра существует команда ATE<value>, где <value> — значение параметра, 0 — выключен, 1 — включен (по умолчанию). Например, команда ATE0 отключит этот режим, а команда ATE1 включит.

22

Существует также команда ATV<value>, которая отвечает за формат ответов модуля на отправляемые команды — текстовый (<value>=1, по-умолчанию, ответы вида OK, CONNECT, RING, ERROR и т. д.) и цифровой код (<value>=0, ответы вида 0, 1, 2, 3 и т. д.). Например, команда ATV0 включит цифровой формат ответов, а команда ATV1 — текстовый.

Подробно, таблица соответствия цифровых кодов текстовым, приведена в разделе 2.2.25 Справочника по AT-командам (3,07 MB))
23

Работа с ошибками

Как правило, в ответ на отправленные команды, в случае успешного их исполнения, модуль возвращает OK. Но если команда не была исполнена, модуль сообщит об ошибке исполнения.

24

В модуле SIM800L существует параметр, устанавливающий степень информативности выпадающих ошибок.

25
Описание Команда Структура ответа Пример ответа
Получение актуального значения параметра AT+CMEE? +CMEE: <n>
OK
<n> — информирование об ошибках
0 — сообщение о собственно наличии ошибки ERROR
1 — ответ содержащий цифровой код ошибки (расшифровка кодов в разделе 19 Справочника по AT-командам (3,07 MB))
2 — ответ содержащий описание ошибки
+CMEE: 0

OK
Команда записи нового значения AT+CMEE=<n> OK OK
26

Для работы через терминал в ручном режиме удобно использовать значение этого параметра 2AT+CMEE=2.

27

Теперь, после установки расширенного информирования об ошибках, можно попробовать отправить несуществующую команду ATE?, на что модем ответит развернуто: +CME ERROR: unknown (неизвестная команда).

28

Незапрашиваемые уведомления

Есть ещё одна категория сообщений получаемых от модуля — незапрашиваемые уведомления. Это сообщения, которые, как видно из названия, могут приходить без совершения пользователем каких-либо действий.

29

Самыми распространенными примерами незапрашиваемых уведомлений, являются:

Незапрашиваемое уведомление или незапрашиваемый код результата (англ. Unsolicited notification, Unsolicited Result Code) — сообщение от GSM-модуля, генерируемое самим модулем в ответ на изменение своего состояния, не спровоцированное отправленной AT-командой.


Полный перечень незапрашивамых уведомлений приведен в разделе 19.3 Справочника по AT-командам (3,07 MB))
30
Уведомление Описание Пример
RING Уведомление входящего вызова RING
+CMTI Уведомление прихода нового SMS-сообщения +CMTI: "SM",2
+CLIP Автоопределитель номера во время входящего звонка +CLIP: "+78004522441",145,"",0,"",0
+CUSD Получение ответа на отправленный USSD-запрос +CUSD: 0, " Vash balans 198.02 r.
Dlya Vas — nedelya besplatnogo SMS-obsh'eniya s druz'yami! Podkl.: *319#", 15
UNDER-VOLTAGE POWER DOWN
UNDER-VOLTAGE WARNNING
OVER-VOLTAGE POWER DOWN
OVER-VOLTAGE WARNNING
Сообщения о некорректном напряжении модуля UNDER-VOLTAGE WARNNING
+CMTE Сообщения о некорректной температуре модуля +CMTE: 1
31 На заметку:
Команды ATE и ATV никак не влияют на формат незапрашиваемых уведомлений (Unsolicited Result Code).
32

О сохранении установленных параметров

Несмотря на то, что модуль SIM800L это сложное устройство со своей энергонезависимой памятью, не каждый параметр, установленный какой-либо AT-командой, может быть в ней сохранен. В документации по AT-командам, у каждой команды есть свойство Режим сохранения (Parameter Saving Mode), которое дает пользователю информацию о том, что случится с установленным параметром после перезагрузки устройства. Это очень важное свойство AT-команд, необходимо учитывать при написании программ. Например, в программе используется АОН (автоматический определитель номера), который включается командой AT+CLIP=1. Но этот параметр не сохраняемый, а значит активировать его нужно при каждом запуске. В то же время режим DTMF (активируется командой AT+DDET=1), может быть сохранен и после перезагрузки его значение не изменится.

33

Поведение каждого из параметров, характеризуется одним из трех значений свойства Parameter Saving Mode:

  • NO_SAVE — параметр(ы) команды не сохраняется
  • AT&W_SAVE — параметр(ы) команды сохраняется по команде AT&W
  • AUTO_SAVE — параметр(ы) команды сохраняется автоматически

34

Как правильно программировать взаимодействие с модемом — команды и ответы

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

35 На заметку:
В готовых проектах, в целях экономии памяти МК рекомендуется использовать комбинацию параметров: отключенный Echo Mode (ATE0), цифровой формат ответов модуля (ATV0) и цифровой код ошибок (AT+CMEE=1). Для единовременной установки всех параметров можно выполнить следующую команду (&W от AT&W — сохранить):
1
ATE0V0+CMEE=1;&W

В образовательных целях и тестировании ПО: включенный (по умолчанию) Echo Mode (ATE1), текстовый (по умолчанию) формат ответов модуля (ATV1) и текстовый код ошибок (AT+CMEE=2).
1
ATE1V1+CMEE=2;&W
36

Все примеры далее выполняются после единожды выполненной в терминале команды ATE1V1+CMEE=2;&W.

37

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

38 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <SoftwareSerial.h> SoftwareSerial SIM800(8, 9); // 8 - RX Arduino (TX SIM800L), 9 - TX Arduino (RX SIM800L) void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером Serial.println("Start!"); SIM800.begin(9600); // Скорость обмена данными с модемом SIM800.println("AT"); // Автонастройка скорости SIM800.println("AT+CLVL?"); // Запрашиваем громкость динамика SIM800.println("AT+CMGF=1"); // Включить TextMode для SMS SIM800.println("AT+DDET=1,0,1"); // Включить DTMF SIM800.println("AT+CLIP=1"); // Включить АОН } void loop() { if (SIM800.available()) // Ожидаем прихода данных (ответа) от модема... Serial.write(SIM800.read()); // ...и выводим их в Serial if (Serial.available()) // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }
39
Ответ на вторую команду пришел четвертым. Причем порядок исполнения постоянно меняется.
Ответ на вторую команду пришел четвертым. Причем порядок исполнения постоянно меняется.
40

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

41

Но что произойдет, если в списке команд будет команда, которая исполнится с ошибкой (AT+DDET=1,0,3)?

42 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <SoftwareSerial.h> SoftwareSerial SIM800(8, 9); // 8 - RX Arduino (TX SIM800L), 9 - TX Arduino (RX SIM800L) void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером Serial.println("Start!"); SIM800.begin(9600); // Скорость обмена данными с модемом SIM800.println("AT"); // Автонастройка скорости SIM800.println("AT+CLVL?"); // Запрашиваем громкость динамика SIM800.println("AT+CMGF=1"); // Включить TextMode для SMS
SIM800.println("AT+DDET=1,0,3"); // Включить DTMF - в этой строке умышленно допущена ошибка - недопустимый параметр
SIM800.println("AT+CLIP=1"); // Включить АОН } void loop() { if (SIM800.available()) // Ожидаем прихода данных (ответа) от модема... Serial.write(SIM800.read()); // ...и выводим их в Serial if (Serial.available()) // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }
43
44

Вряд ли кто-то со 100% уверенностью сможет сказать, в ответ на исполнение каких команд модуль ответил OK, в какой команде ошибка, и какова судьба последней команды AT+CLIP=1?

45

Первой мыслью, которая может прийти в голову — дожидаться выполнения каждой команды, и потом отправлять следующую. А паузу мы можем делать при помощи функции delay():

46 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <SoftwareSerial.h> SoftwareSerial SIM800(8, 9); // 8 - RX Arduino (TX SIM800L), 9 - TX Arduino (RX SIM800L) void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером Serial.println("Start!"); SIM800.begin(9600); // Скорость обмена данными с модемом SIM800.println("AT"); // Автонастройка скорости delay(500); SIM800.println("AT+CLVL?"); // Запрашиваем громкость динамика delay(500); SIM800.println("AT+CMGF=1"); // Включить TextMode для SMS delay(500); SIM800.println("AT+DDET=1,0,3"); // Включить DTMF - в этой строке умышленно допущена ошибка - недопустимый параметр delay(500); SIM800.println("AT+CLIP=1"); // Включить АОН delay(500); } void loop() { if (SIM800.available()) // Ожидаем прихода данных (ответа) от модема... Serial.write(SIM800.read()); // ...и выводим их в Serial if (Serial.available()) // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }
47

Запустив скетч, можно с удивлением обнаружить, что после команды AT+DDET=1,0,3 наступает глубокая неизвестность о её дальнейшей судьбе и судьбе последней команды.

48
Что дальше?...
Что дальше?...
49

Такой же ошибкой грешит большинство примеров в сети по отправке SMS. Некорректный пример:

50 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
void sendSMS(String phone, String message) // Некорректный пример { SIM800.print("AT+CMGF=1\r"); // Устанавливаем текстовый (не PDU) формат сообщений delay(100); // Даем модулю отработать команду SIM800.println("AT+CMGS=\"" + phone + "\""); // Задаем номер телефона адресата delay(100); SIM800.println(message); // Вводим сообщение delay(100); SIM800.println((char)26); // Уведомляем GSM-модуль об окончании ввода delay(100); SIM800.println(); delay(4000); // Ожидаем отправки }
51 Важно:
Неправильно устанавливать паузы для того, чтобы дать модулю выполнить команду. Дело в том, что во время ожидания МК «парализован» и игнорирует всю информацию отправляемую модулем.
52 На заметку:
Корректная функция отправки SMS будет приведена в соответствующем разделе ниже
53

Более корректным является подход, при котором МК остановит исполнение программы до тех пор, пока не будет получен ответ на отправленную команду. Для этого, автор предлагает использовать следующие 2 функции — sendATComand() и waitResponse():

54 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема }
55

В функции sendATCommand(cmd, waiting) первый параметр содержит отправляемую команду, а второй параметр waiting задает, нужно ли программе дожидаться ответа от GSM-модуля или нет.

56 На заметку:
Если Echo Mode выключен, строки 8-10 в предыдущем коде (функция sendATCommand()) можно закомментировать.
57

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

58 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Автонастройка скорости sendATCommand("AT+CLVL?", true); // Запрашиваем громкость динамика sendATCommand("AT+CMGF=1", true); // Включить TextMode для SMS sendATCommand("AT+DDET=1,0,3", true); // Включить DTMF - в этой строке умышленно допущена ошибка - недопустимый параметр
do {
_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН
_response.trim(); // Убираем пробельные символы в начале и конце
} while (_response != "OK"); // Не пускать дальше, пока модем не вернет ОК
Serial.println("CLI enabled"); // Информируем, что АОН включен // Проверка пройдена, модем сообщил о готовности, можно запускать основной цикл... // ... } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) // Ожидаем прихода данных (ответа) от модема... Serial.write(SIM800.read()); // ...и выводим их в Serial if (Serial.available()) // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }
59
Все команды строго по порядку, друг за другом, вне зависимости от статуса исполнения
Все команды строго по порядку, друг за другом, вне зависимости от статуса исполнения
60

Теперь остается немного модифицировать процедуру loop() таким образом, чтобы можно было анализировать прочие незапрашиваемые ответы и уведомления в основном теле программы:

61 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа Serial.println(_response); // Если нужно выводим в монитор порта // ... здесь можно анализировать данные полученные от GSM-модуля } if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; }
62

Здесь и далее, предлагается использовать данный шаблон в качестве основы для написания программ:

63 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Автонастройка скорости // sendATCommand("AT+CLVL?", true); // Запрашиваем громкость динамика // sendATCommand("AT+CMGF=1", true); // Включить TextMode для SMS // sendATCommand("AT+DDET=1,0,0", true); // Включить DTMF } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа Serial.println(_response); // Если нужно выводим в монитор порта // ... здесь можно анализировать данные полученные от GSM-модуля } if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; }
64

Входящие/исходящие вызовы

Возможны ситуации, когда необходимо воспользоваться голосовыми функциями модуля SIM800L, а именно осуществлять исходящие звонки в качестве оперативных уведомлений, либо принимать входящие звонки, для осуществления управления по DTMF. Для использования голосовых функций по прямому назначению, к аудиоинтерфейсам микрофона (MICP и MICN) и колонок (SPKP и SPKN) можно напрямую подключить раздельные динамик и микрофон:

Постоянная ссылка

Динамик 30 мм (0.5 Вт, 8 Ом)

Динамик 30 мм (0.5 Вт, 8 Ом)

Диаметр 3 см, толщина 5 мм

Постоянная ссылка

Микрофон петличный (петличка) noname

Микрофон петличный (петличка) noname

Качество соответствует цене. Исключительно в расход, для экспериментов.

65
66

Либо проводную гарнитуру, через разъем Jack 3.5 мм PJ-342:

68
В данном примере полярность подключения значения не имеет
В данном примере полярность подключения значения не имеет
69

Основные команды для управления голосовыми функциями представлены в таблице:

70
Описание Команда, описание параметров Ответы Пример
Осуществить исходящий вызов ATD<phonenumber>;
<phonenumber> — номер вызываемого абонента в полном формате (+78004522441 или 88004522441)
OK,
BUSY,
NO DIALTONE и др.
ATD+78004522441;

OK
Повторить набор последнего набранного номера ATDL OK,
BUSY,
NO DIALTONE и др.
ATDL

OK
Установка громкости динамика во время разговора AT+CLVL=<level>
<level> — необходимый уровень громкости (целое число в диапазоне 0...100)
автоматически сохраняемое значение
OK,
ERROR
AT+CLVL=100

OK
Установка громкости звонка AT+CRSL=<level>
<level> — необходимый уровень громкости (целое число в диапазоне 0...100)
автоматически сохраняемое значение
OK,
ERROR
AT+CRSL=100

OK
Ответить на входящий звонок ATA
все команды записанные в этой же строке после команды ATA игнорируются
OK ATA

OK
Сбросить входящий звонок ATH
все команды записанные в этой же строке после команды ATH игнорируются
OK ATA

OK
Автоматический ответ при входящем вызове ATS0=<n>
<n> — количество гудков до автоматического ответа при входящем вызове:
0 — автоответ отключен
1...255 — количество гудков, после которых модуль автоматически ответит на входящий вызов.
при установке слишком большого количества гудков, вызов может быть прерван оператором
OK,
ERROR
ATS0=2

OK
Включение автоопределителя номера (АОН) AT+CLIP=<n>
<n> — параметр включения функции:
0 — АОН выключен
1 — АОН включен
параметр не сохраняется, необходима установка при каждом запуске, по умолчанию значение 0
OK AT+CLIP=1

OK
Незапрашиваемое уведомление +CLIP +CLIP: <number>,<type>[,<subaddr>,<satype>,<alphaId>,<CLI validity>]
<number> — телефонный номер вызывающего абонента
<type> — формат указанного телефонного номера
129 — тип неизвестен
161 — национальный номер
145 — номер в международном формате
177 — внутренний номер оператора
<subaddr> — дополнительный номер
<satype> — формат дополнительного номера
<alphaId> — порядковый номер абонента в телефонной книге (если записан)
<CLI validity> — статус АОН
0 — номер определен
1 — абонент запретил определять свой номер
2 — АОН недоступен или ограничен сетью
OK RING

+CLIP: "+78004522441",145,"",0,"",0
Незапрашиваемое уведомление RING (входящий вызов) RING
RING
71

Для осуществления исходящих вызовов используется команда ATD<phonenumber>; где <phonenumber> — номер телефона в любом полном формате — +7928******* или 8928*******, например, ATD+78001000800;.

72 На заметку:
Нельзя забывать двоеточие в конце команды — в этом случае модем отреагирует сообщением NO CARRIER (не доступно)
73

Ответить на входящий звонок в ручном режиме можно командой ATA. Либо командой ATS0=<n> можно установить возможность автоматического ответа после <n>-гудков.

74 На заметку:
Исходящий вызов, также как и уже установленное соединение, можно отменить/прервать командой ATH. Отмена невозможна в некоторых состояниях, например, во время установки соединения.
75

Для того, чтобы реагировать на входящие звонки, нужно отслеживать незапрашиваемое уведомление RING, и далее предпринимать действия — либо отвечать на входящий вызов командой ATA, либо сбрасывать его командой ATH.

76 На заметку:
Для того, чтобы автоматически отвечать на все входящие вызовы достаточно один раз установить автоответ командой ATS0=1.
77 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Отправили AT для настройки скорости обмена данными // Команды настройки модема при каждом запуске //_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН //_response = sendATCommand("AT+DDET=1", true); // Включаем DTMF } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа _response.trim(); // Убираем лишние пробелы в начале и конце Serial.println(_response); // Если нужно выводим в монитор порта
if (_response.startsWith("RING")) { // Есть входящий вызов
sendATCommand("ATA", true); // Отвечаем на вызов
// или
sendATCommand("ATH", true); // Отклоняем вызов
}
} if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; }
78
79

Чтобы обеспечить индивидуальный подход к каждому вызову необходимо включить автоопределитель номера (АОН) — команда AT+CLIP=1 (по умолчанию отключен). После того как АОН включен, каждое незапрашиваемое уведомление RING будет дополнительно содержать информацию о номере телефона:

80
81

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

82 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Отправили AT для настройки скорости обмена данными // Команды настройки модема при каждом запуске
_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН
//_response = sendATCommand("AT+DDET=1", true); // Включаем DTMF } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа _response.trim(); // Убираем лишние пробелы в начале и конце Serial.println(_response); // Если нужно выводим в монитор порта
String whiteListPhones = "+7928xxxxxxx, +7928xxxxxxx, +7928xxxxxxx"; // Белый список телефонов
if (_response.startsWith("RING")) { // Есть входящий вызов
int phoneindex = _response.indexOf("+CLIP: \"");// Есть ли информация об определении номера, если да, то phoneindex>-1
String innerPhone = ""; // Переменная для хранения определенного номера
if (phoneindex >= 0) { // Если информация была найдена
phoneindex += 8; // Парсим строку и ...
innerPhone = _response.substring(phoneindex, _response.indexOf("\"", phoneindex)); // ...получаем номер
Serial.println("Number: " + innerPhone); // Выводим номер в монитор порта
}
// Проверяем, чтобы длина номера была больше 6 цифр, и номер должен быть в списке
if (innerPhone.length() >= 7 && whiteListPhones.indexOf(innerPhone) >= 0) {
sendATCommand("ATA", true); // Если да, то отвечаем на вызов
}
else {
sendATCommand("ATH", true); // Если нет, то отклоняем вызов
}
}
}
if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; }
83
84

Прием/отправка/удаление SMS

Тема, которая акутальна при взаимодействии с GSM-модулем в 95% случаев — это, конечно же, прием и отправка SMS, при помощи которых происходит удаленное управление чем-либо, например, микроконтроллером.

85 На заметку:
В данном разделе будет рассмотрена работа с SMS в текстовом формате (Text Mode), все примеры будут также приведены для работы с SMS в текстовом формате. Работа с SMS в PDU-формате (PDU mode) — тема отдельной статьи и здесь будет опущена. Текстовый формат намного проще, но он не позволяет обмениваться SMS-сообщениями на языках, отличных от английского. PDU-формат имеет более обширные возможности, но достаточно сложен в освоении и использовании.
86

Для взаимодействия с SMS существует внушительный список команд. Самые используемые из них приведены в таблице:

87
Описание Команда Параметр(ы) Ответ Пример(ы)
Выбор формата SMS AT+CMGF=<mode> <mode> — формат сообщений, значения:
0 — PDU-формат (по умолчанию)
1 — текстовый формат
OK AT+CMGF=1 формат SMS в текстовый формат (Text Mode)
Удалить SMS AT+CMGD=<index>[,<delflag>] <index> — порядковый номер удаляемого сообщения в заданной группе (>0)
<delflag> — фильтр удаляемых сообщений (необязат.), значения:
0 — удалить сообщение с заданным порядковым номером <index>
1...3 — удаление сообщений, оставляя сообщения по разным критериям (см. datasheet)
4 — удалить все сообщения, включая непрочитанные
ОК AT+CMGD=0,4 удаление всех сообщений
Удалить все SMS AT+CMGDA=<type> <type> — тип удаляемых сообщений, значения (для TextMode):
"DEL READ" — удалить все прочитанные SMS
"DEL UNREAD" — удалить все непрочитанные SMS
"DEL SENT" — удалить все отправленные SMS
"DEL UNSENT" — удалить все неотправленные SMS
"DEL INBOX" — удалить все полученные SMS
"DEL ALL" — удалить все SMS
ОК AT+CMGDA="DEL ALL" удаление всех сообщений
Получить список SMS AT+CMGL=<stat>[,<mode>] <stat> — фильтр, значения
"REC UNREAD" — полученные непрочитанные SMS
"REC READ" — полученные прочитанные SMS
"STO UNSENT" — сохраненные непрочитанные SMS
"STO SENT" — сохраненные непрочитанные SMS
"ALL" — все SMS
<mode> — фильтр удаляемых сообщений (необязат., используется при <index>=0), значения:
0 — изменить статус сообщений на «прочитано»
1 — оставить статус без изменения
ОК AT+CMGL="REC UNREAD",1 получить принятые непрочитанные сообщения, статус не изменять
Ответ:
+CMGL: 1,"REC UNREAD","w4348435","","17/05/14,15:10:02+12"
041C04270421002....7043E0432

+CMGL: 2,"REC UNREAD","+7928xxxxxxx","","17/05/15,23:34:03+12"
Test

+CMGL: 3,"REC UNREAD","+7928xxxxxxx","","17/05/22,18:13:25+12"
Balance

OK
Прочитать SMS AT+CMGR=<index>[,<mode>] <index> — порядковый номер сообщения
<mode> — действие после прочтения, значения:
0 — изменить статус сообщений на «прочитано» (по-умолчанию)
1 — оставить статус без изменения
ОК AT+CMGR=7,1 прочитать 7-ое сообщение, статус не изменять
Ответ:
+CMGR: "REC READ","+7928xxxxxxx","","17/05/22,18:13:25+12"
Balance

OK
Отправить SMS AT+CMGS=<da>[,<toda>]<CR>текст сообщения<ctrl-Z/ESC> <da> — телефон адресата в международном формате, в кавычках
<toda> — дополнительный номер (не используется)
<CR> — начало строки, после отправки модуль переходит в режим приема текста сообщения
После того как текст сообщения передан, необходимо отправить либо <ctrl-Z> для отправки сообщения, либо <ESC> для отмены.
+CMGS: <n>

OK
AT+CMGS="+7928xxxxxxx"
>
test message
>
+CMGS: 81

OK
Незапрашиваемое уведомление о приходе нового SMS +CMTI: <mem>,<index> <mem> — хранилище сообщений
"SM" — SIM-карта
"ME" — память модема/телефона
<index> — индекс полученного сообщения (показывает общее число полученных непрочитанных SMS)
+CMTI: "SM",3
Получить информацию о хранилищах SMS AT+CPMS?

Ответ:
+CPMS: <mem1>,<used1>,<total1>, <mem2>,<used2>,<total2>, <mem3>,<used3>,<total3>
<mem1> — чтение и удаление сообщений происходит их этого хранилища
<mem2> — сообщение будет записано в это хранилище и отправлено
<mem3> — получаемые сообщения будут записываться в это хранилище
<mem1>, <mem2>, <mem3> могут иметь следующие значения:
"SM" — память SIM-карты
"ME" — память модема/телефона
"SM_P" — предпочтительна память SIM-карты
"ME_P" — предпочтительна память модема/телефона
"MТ" — память SIM-карты и модема/телефона (SM+ME)
<usedx> — количество сообщений в хранилище <memx>
<totalx> — максимальное количество сообщений сохраняемое в хранилище <memx>
OK AT+CPMS?
88 На заметку:
В любом случае, команд для работы с SMS немного больше и все они подробно описаны в официальном мануале по AT-командам (3,07 MB) в 4 разделе.
89

Перед работой с SMS, для использования текстового формата, его нужно включить командой AT+CMGF=1. Поскольку этот параметр сохраняется по команде AT&W, в скетче он будет устанавливаться в начале в формате «несколько команд в одной строке»: AT+CMGF=1;&W

90

Отправка SMS. Формат отправки SMS в текстовом формате выглядит следующим образом:

  • AT+CMGS=<phonenumber>, где <phonenumber> — полный номер телефона адресата, в любом формате в кавычках, например AT+CMGS="+78001000800". После выполнения этой команды появится приглашение ввода текста SMS — >. После ввода сообщения, о его окончании нужно сообщить — передать Ctrl+Z (либо ESC для выхода без отправки). Программно, сочетание клавиш Ctrl+Z можно заменить как (char)26 (так как ASCII код символа — 26. SUB, 1A — substitute, символ окончания файла).

91 На заметку:
Как отправить комбинацию клавиш Ctrl+Z или символ ESC через командную строку терминала Serial?

Если отправка SMS осуществляется через окно Serial среды Arduino IDE, то после ввода основного текста сообщения необходимо отправить модулю команду завершения SMS — Ctrl+Z. Сделать это можно копированием символа между кавычками "

" и последующей его вставкой в поле отправки данных.



Чтобы отправить символ ESC (для отмены сообщения) нужно скопировать его между этими кавычками — "

" .
92

Пример программной отправки SMS (для удобства, функционал отправки SMS вынесен в отдельную функцию sendSMS()). В данном примере также используется контроль статуса отправки. В случае неудачной попытки отправки, сообщение об этом появится в терминале:

93 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Отправили AT для настройки скорости обмена данными // Команды настройки модема при каждом запуске //_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН //_response = sendATCommand("AT+DDET=1", true); // Включаем DTMF _response = sendATCommand("AT+CMGF=1;&W", true); // Включаем текстовый режима SMS (Text mode) и сразу сохраняем значение (AT&W)!
sendSMS("+7928xxxxxxx", "test message");
} String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа _response.trim(); // Убираем лишние пробелы в начале и конце Serial.println(_response); // Если нужно выводим в монитор порта //....
if (_response.startsWith("+CMGS:")) { // Пришло сообщение об отправке SMS
int index = _response.lastIndexOf("\r\n");// Находим последний перенос строки, перед статусом
String result = _response.substring(index + 2, _response.length()); // Получаем статус
result.trim(); // Убираем пробельные символы в начале/конце
if (result == "OK") { // Если результат ОК - все нормально
Serial.println ("Message was sent. OK");
}
else { // Если нет, нужно повторить отправку
Serial.println ("Message was not sent. Error");
}
}
} if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; }
void sendSMS(String phone, String message)
{
sendATCommand("AT+CMGS=\"" + phone + "\"", true); // Переходим в режим ввода текстового сообщения
sendATCommand(message + "\r\n" + (String)((char)26), true); // После текста отправляем перенос строки и Ctrl+Z
}
94
95

Прием и чтение SMS. Здесь не так все просто, как при отправке сообщений. Дело в том, что все принятые сообщения хранятся в памяти SIM-карты. И память эта ограничена. Объем памяти можно узнать командой AT+CPMS?. Если допустить переполнение памяти, сообщения больше не смогут приходить. Поэтому, чтобы сохранить работоспособность приложения, после получения каждого сообщения, его нужно обрабатывать и удалять. Мануал предоставляет большой набор команд, при помощи которых можно совершать любые манипуляции с принятыми сообщениями.

96

Во время прихода SMS, SIM800L генерирует незапрашиваемое уведомление вида +CMTI: "SM",4. После прихода такого уведомления, можно программно инициировать процедуру чтения полученного сообщения командой AT+CMGR=<index>,<mode>, где в качестве параметра <index> необходимо указать индекс, полученный в уведомлении — +CMTI: "SM",4. В приведенном ниже примере, после получения и обработки пришедшего сообщения, все сообщения удаляются командой AT+CMGDA="DEL ALL" для экономии памяти модуля:

97 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX String _response = ""; // Переменная для хранения ответа модуля void setup() { Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Отправили AT для настройки скорости обмена данными // Команды настройки модема при каждом запуске //_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН //_response = sendATCommand("AT+DDET=1", true); // Включаем DTMF _response = sendATCommand("AT+CMGF=1;&W", true); // Включаем текстовый режима SMS (Text mode) и сразу сохраняем значение (AT&W)! } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } void loop() { if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа _response.trim(); // Убираем лишние пробелы в начале и конце Serial.println(_response); // Если нужно выводим в монитор порта //.... if (_response.startsWith("+CMTI:")) { // Пришло сообщение об отправке SMS int index = _response.lastIndexOf(","); // Находим последнюю запятую, перед индексом String result = _response.substring(index + 1, _response.length()); // Получаем индекс result.trim(); // Убираем пробельные символы в начале/конце _response=sendATCommand("AT+CMGR="+result, true); // Получить содержимое SMS parseSMS(_response); // Распарсить SMS на элементы sendATCommand("AT+CMGDA=\"DEL ALL\"", true); // Удалить все сообщения, чтобы не забивали память модуля } } if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; } void parseSMS(String msg) { String msgheader = ""; String msgbody = ""; String msgphone = ""; msg = msg.substring(msg.indexOf("+CMGR: ")); msgheader = msg.substring(0, msg.indexOf("\r")); msgbody = msg.substring(msgheader.length() + 2); msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK")); msgbody.trim(); int firstIndex = msgheader.indexOf("\",\"") + 3; int secondIndex = msgheader.indexOf("\",\"", firstIndex); msgphone = msgheader.substring(firstIndex, secondIndex); Serial.println("Phone: "+msgphone); Serial.println("Message: "+msgbody); // Далее пишем логику обработки SMS-команд. // Здесь также можно реализовывать проверку по номеру телефона // И если номер некорректный, то просто удалить сообщение. } void sendSMS(String phone, String message) { sendATCommand("AT+CMGS=\"" + phone + "\"", true); // Переходим в режим ввода текстового сообщения sendATCommand(message + "\r\n" + (String)((char)26), true); // После текста отправляем перенос строки и Ctrl+Z }
98
99

Ну а что произойдет, если МК пропустит незапрашиваемое уведомление о приходе SMS? Ему придется ждать прихода следующего сообщения, что является неприемлемым сценарием. И если будет принято сразу несколько сообщений — будет обработано только последнее. Для подстраховки от таких ситуаций необходимо ввести периодическую (например, раз в минуту) проверку наличия непрочитанных сообщений. В случае наличия таковых, каждое сообщение из списка будет обработано и удалено. Пример скетча приведен ниже в разделе об управлении по SMS.

100

Управление по SMS

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

101
Резисторы для подключения светодиодов 100 Ом, для подключения модуля — 10 КОм
Резисторы для подключения светодиодов 100 Ом, для подключения модуля — 10 КОм

О там как правильно подключать светодиоды и каких ошибок следует избегать при их подключении написано в статье Как правильно подключать светодиоды
102

Команды будут состоять из двух цифр — первая цифра будет обозначать номер светодиода (1-3), вторая — его состояние (1 — включен, 0 — выключен). Получается, что допустимых команд (сочетаний цифр) всего 6: 11, 10, 21, 20, 31, 30. Прочие команды будут игнорироваться. Информация о статусе исполнения или некорректности команды, будет выводиться в терминал. Скетч:

103 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <SoftwareSerial.h> // Библиотека програмной реализации обмена по UART-протоколу SoftwareSerial SIM800(8, 9); // RX, TX int pins[3] = {5, 6, 7}; // Пины с подключенными светодиодами String _response = ""; // Переменная для хранения ответа модуля long lastUpdate = millis(); // Время последнего обновления long updatePeriod = 60000; // Проверять каждую минуту String phones = "+7928xxxxxxx, +7920xxxxxxx, +7918xxxxxxx"; // Белый список телефонов void setup() { for (int i = 0; i < 3; i++) { pinMode(pins[i], OUTPUT); // Настраиваем пины в OUTPUT } Serial.begin(9600); // Скорость обмена данными с компьютером SIM800.begin(9600); // Скорость обмена данными с модемом Serial.println("Start!"); sendATCommand("AT", true); // Отправили AT для настройки скорости обмена данными //sendATCommand("AT+CMGDA=\"DEL ALL\"", true); // Удаляем все SMS, чтобы не забивать память // Команды настройки модема при каждом запуске //_response = sendATCommand("AT+CLIP=1", true); // Включаем АОН //_response = sendATCommand("AT+DDET=1", true); // Включаем DTMF sendATCommand("AT+CMGF=1;&W", true); // Включаем текстовый режима SMS (Text mode) и сразу сохраняем значение (AT&W)! lastUpdate = millis(); // Обнуляем таймер } String sendATCommand(String cmd, bool waiting) { String _resp = ""; // Переменная для хранения результата Serial.println(cmd); // Дублируем команду в монитор порта SIM800.println(cmd); // Отправляем команду модулю if (waiting) { // Если необходимо дождаться ответа... _resp = waitResponse(); // ... ждем, когда будет передан ответ // Если Echo Mode выключен (ATE0), то эти 3 строки можно закомментировать if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); // Дублируем ответ в монитор порта } return _resp; // Возвращаем результат. Пусто, если проблема } String waitResponse() { // Функция ожидания ответа и возврата полученного результата String _resp = ""; // Переменная для хранения результата long _timeout = millis() + 10000; // Переменная для отслеживания таймаута (10 секунд) while (!SIM800.available() && millis() < _timeout) {}; // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то... if (SIM800.available()) { // Если есть, что считывать... _resp = SIM800.readString(); // ... считываем и запоминаем } else { // Если пришел таймаут, то... Serial.println("Timeout..."); // ... оповещаем об этом и... } return _resp; // ... возвращаем результат. Пусто, если проблема } bool hasmsg = false; // Флаг наличия сообщений к удалению void loop() { if (lastUpdate + updatePeriod < millis() ) { // Пора проверить наличие новых сообщений do { _response = sendATCommand("AT+CMGL=\"REC UNREAD\",1", true);// Отправляем запрос чтения непрочитанных сообщений if (_response.indexOf("+CMGL: ") > -1) { // Если есть хоть одно, получаем его индекс int msgIndex = _response.substring(_response.indexOf("+CMGL: ") + 7, _response.indexOf("\"REC UNREAD\"", _response.indexOf("+CMGL: "))).toInt(); char i = 0; // Объявляем счетчик попыток do { i++; // Увеличиваем счетчик _response = sendATCommand("AT+CMGR=" + (String)msgIndex + ",1", true); // Пробуем получить текст SMS по индексу _response.trim(); // Убираем пробелы в начале/конце if (_response.endsWith("OK")) { // Если ответ заканчивается на "ОК" if (!hasmsg) hasmsg = true; // Ставим флаг наличия сообщений для удаления sendATCommand("AT+CMGR=" + (String)msgIndex, true); // Делаем сообщение прочитанным sendATCommand("\n", true); // Перестраховка - вывод новой строки parseSMS(_response); // Отправляем текст сообщения на обработку break; // Выход из do{} } else { // Если сообщение не заканчивается на OK Serial.println ("Error answer"); // Какая-то ошибка sendATCommand("\n", true); // Отправляем новую строку и повторяем попытку } } while (i < 10); break; } else { lastUpdate = millis(); // Обнуляем таймер if (hasmsg) { sendATCommand("AT+CMGDA=\"DEL READ\"", true); // Удаляем все прочитанные сообщения hasmsg = false; } break; } } while (1); } if (SIM800.available()) { // Если модем, что-то отправил... _response = waitResponse(); // Получаем ответ от модема для анализа _response.trim(); // Убираем лишние пробелы в начале и конце Serial.println(_response); // Если нужно выводим в монитор порта if (_response.indexOf("+CMTI:")>-1) { // Пришло сообщение об отправке SMS lastUpdate = millis() - updatePeriod; // Теперь нет необходимости обрабатываеть SMS здесь, достаточно просто // сбросить счетчик автопроверки и в следующем цикле все будет обработано } } if (Serial.available()) { // Ожидаем команды по Serial... SIM800.write(Serial.read()); // ...и отправляем полученную команду модему }; } void parseSMS(String msg) { // Парсим SMS String msgheader = ""; String msgbody = ""; String msgphone = ""; msg = msg.substring(msg.indexOf("+CMGR: ")); msgheader = msg.substring(0, msg.indexOf("\r")); // Выдергиваем телефон msgbody = msg.substring(msgheader.length() + 2); msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK")); // Выдергиваем текст SMS msgbody.trim(); int firstIndex = msgheader.indexOf("\",\"") + 3; int secondIndex = msgheader.indexOf("\",\"", firstIndex); msgphone = msgheader.substring(firstIndex, secondIndex); Serial.println("Phone: " + msgphone); // Выводим номер телефона Serial.println("Message: " + msgbody); // Выводим текст SMS if (msgphone.length() > 6 && phones.indexOf(msgphone) > -1) { // Если телефон в белом списке, то... setLedState(msgbody, msgphone); // ...выполняем команду } else { Serial.println("Unknown phonenumber"); } } void setLedState (String result, String phone) { bool correct = false; // Для оптимизации кода, переменная корректности команды String msgToSend = ""; if (result.length() == 2) { int ledIndex = ((String)result[0]).toInt(); // Получаем первую цифру команды - адрес устройства (1-3) int ledState = ((String)result[1]).toInt(); // Получаем вторую цифру команды - состояние (0 - выкл, 1 - вкл) if (ledIndex >= 1 && ledIndex <= 3 && (ledState == 0 or ledState == 1)) { // Если все нормально, исполняем команду msgToSend = "LED:" + (String)ledIndex + " set to " + (ledState == 0 ? "OFF" : "ON"); // Статус исполнения digitalWrite(pins[ledIndex - 1], ledState); // Исполняем команду correct = true; // Флаг корректности команды } } if (!correct) { msgToSend = "Incorrect command: " + result; // Статус исполнения } Serial.println(msgToSend); // Выводим результат в терминал } void sendSMS(String phone, String message) { sendATCommand("AT+CMGS=\"" + phone + "\"", true); // Переходим в режим ввода текстового сообщения sendATCommand(message + "\r\n" + (String)((char)26), true); // После текста отправляем перенос строки и Ctrl+Z }
104

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

107

Похожие запросы:

  • GSM модуль SIM800L — самое полное руководство на русском языке
  • Дешевый модуль GSM с поддержкой GPRS , для самоделок и удаленного управления.
  • How to send ctrl+z
  • Sending ctrl+z using serial monitor
  • How to put a CTRL-z character to the serial monitor send-window?
  • Монитор порта . Комбинация ctrl+z
  • How to send CTRL_Z through serial
  • Управление при помощи SMS
  • Как парсить входящие SMS
  • SIM800L как осуществлять звонки
  • Управление реле SMS сообщениями (с заданных номеров)
  • Отправка SMS с 3G/GSM модема
comments powered by HyperComments

Яндекс.Метрика