- Цена: $1.98
Коротенький обзор модуля gsm связи для ардуино и устройство блока автозапуска на его основе.
При относительно низкой цене (всего 2$) данный модуль имеет большой функционал, достаточный для большинства бытовых самоделок.
Модуль
Функционал модуля довольно широк — взаимодействие с устройствами с помощью АТ — команд, подключение микрофона и колонок, совершение звонков, прием и отправка SMS и USSD-запросов, спящий режим модуля — погружение и пробуждение, распознавание DTMF, определение приблизительных координат, FM-радио, прием и отправка данных по GPRS и т. д.
На плате расположены необходимые выводы
Питание модуля рассчитано на литиевые аккумуляторы и лежит в пределах 3.4-4.5 В, рекомендованное 4 В. При питании от другого источника требуется обеспечить силу тока в 2А без просадок, иначе он просто не сможет зарегистрироваться в сети.
Взаимодействие с модулем осуществляется по интерфейсу UART (Serial) при помощи специальных AT-команд. Линии данных рассчитаны на напряжение от 2,1 до 3,1В, при работе с ардуино входящую линию необходимо пропустить через делитель напряжения, чтобы привести к требуемому диапазону, исходящую с напряжением 2,8В ардуино воспринимает нормально и делитель не требуется.
При первом включении светодиод на плате начинает часто, равномерно мигать, что означает что сеть еще не найдена. При успешной регистрации в сети, светодиод начинает мигать редко, примерно раз в секунду или две.
Полный список АТ команд можно найти в мануале по командам
Более подробную информацию по модулю можно получить из даташита
Есть еще сайт с подробнейшим описанием модуля и примерами работы с ним, с которого я и взял всю информацию и код в основной своей массе взят оттуда.
Блок
От парковки до места работы расстояние такое, что брелок от сигнализации не берет. Зимой чтобы сесть в теплую машину надо выйти, дойти ближе к парковке, завести, вернуться, подождать. О комфорте это плохо напоминает.
Выхода 2:
заводить по таймеру — не удобно, потому что выхожу в разное время.
поменять сигнализацию на ту, которая имеет возможность GSM запуска — дорого.
Но нашелся и третий. Как то при случайном чтении инструкции обратил внимание на возможность запуска с сигнала внешнего устройства, есть специальный вход на сигнализации, просто подаем на этот вход + или — бортовой сети и машина заводится точно так же как и с брелока, то есть все проверки на возможность запуска проводит сама сигнализация, остается только подать сигнал на контакт.
Посмотрел бегло инструкции других моделей охранных систем SCHER-KHAN — на всех что мне попались такая возможность есть.
Потребуется модуль SIM800L, ардуино (в моем случае про мини с питанием 3,3В), и STEP_DOWN преобразователь для получения 4В из 12В. Собрал я это все давно, в начале прошлой зимы на макетной плате, показывать её не буду — там всё страшно)) Отработало всё практически год и за это время недостатков я не заметил, ни разу не зависло и других отказов не было. Теперь, перед наступающей зимой решил привести это всё в красивый вид.
Вытравил плату
файлы для DipTrace
Дополнительная информация
Собрал всё на плате
Дополнительная информация
Схема
Все резисторы на 10 кОм, только R8 на 220 Ом.
Дополнительная информация
Не показан конденсатор напаянный на модуль sim800l, как оказалось он очень нужен, без него не хватало питания и он не мог зарегистрироваться в сети. Уровень сигнала высокий, а регистрироваться не хочет, в таких случаях рекомендую сразу смотреть цепи питания.
Код для Ардуины
Сразу хочу предупредить — в основном все скопировано/вставлено из чужих примеров, я совсем не программист, для меня главное работает безотказно, а как там всё внутри не так важно.
От себя попытался добавить спящий режим как для ардуины, так и для модема. Перезагрузку при зависании и раз в сутки для надежности. Несколько дополнительных команд.
cкетч в облаке
Дополнительная информация
#include <SoftwareSerial.h>// Библиотека программной реализации обмена по UART-протоколу
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
SoftwareSerial SIM800(8, 9); // RX, TX
#define RING 2 //пин входящего звонка
#define RESET_GSM 12 //пин перезагрузки sim800l
#define SLEEP_GSM 6 //пин спящего режима sim800l
#define START_ENGINE 3 //пин сигнала автозапуска для сигнализации
#define R1 20 // сопротивление резистора R1
#define R2 5 // сопротивление резистора R2
#define LED 13 // светодиод на плате ардуино
unsigned long sleep_sim; //таймер сна модема
boolean callback = false; //нужно ли перезвонить по последнему входящему номеру
volatile int nbr_remaining; // volatile to be modified in interrupt function
unsigned long timing = 0; //переменная для задержки
unsigned long reset_if_gsm_hover = 30000;
String LastNumber = ""; //переменная для хранения последнего входящего номера
String _response = ""; // Переменная для хранения ответа модуля
int resetCounter = 1; // количество циклов сна после последней перезагрузки, цикл сна = 8сек
unsigned long led_standby = 0; // счетчик для мигания светодиодом
void setup() {
pinMode(RING, INPUT_PULLUP); // к пину RING
pinMode(RESET_GSM, OUTPUT); // к пину RESET модема для его перезагрузки
digitalWrite(RESET_GSM, HIGH); // режим LOW — перезагрузка
pinMode(SLEEP_GSM, OUTPUT); // к DTR пину GSM модуля
digitalWrite(SLEEP_GSM, LOW); // пробуждаем GSM модуль
pinMode(START_ENGINE, OUTPUT); // к сигнализации
digitalWrite(START_ENGINE, LOW); // режим HIGH — автозапуск
pinMode(LED, OUTPUT); //светодиод на ардуино
digitalWrite(LED, LOW); // HIGH — горит
wdt_enable(WDTO_8S); //сторожевой таймер на 8 сек
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+CSCLK=1», true); // Включаем возможность перевода sim800l в спящий режим
_response = sendATCommand(«AT+CMGF=1;&W», true); // Включаем текстовый режим SMS (Text mode) и сразу сохраняем значение (AT&W)!
}
String sendATCommand(String cmd, bool waiting) {
String _resp = ""; // Переменная для хранения результата
Serial.println(cmd); // Дублируем команду в монитор порта
digitalWrite(SLEEP_GSM, LOW); // пробуждаем GSM модуль
delay(300);
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) {
wdt_reset();
} // Ждем ответа 10 секунд, если пришел ответ или наступил таймаут, то…
if (SIM800.available()) { // Если есть, что считывать…
_resp = SIM800.readString(); //… считываем и запоминаем
}
else { // Если пришел таймаут, то…
Serial.println(«Timeout…»); //… оповещаем об этом и…
}
return _resp; //… возвращаем результат. Пусто, если проблема
}
void loop() {
wdt_reset();
if (millis() > led_standby) { //мигание светодиодом в дежурном режиме
digitalWrite(LED, HIGH);
led_standby = millis() + 3000;
delay(10);
//Serial.println(readVoltage(100));
} else {
digitalWrite(LED, LOW);
}
if ((millis() — sleep_sim) > 120000) { //через 2 мин бездействия модем отправляем спать
digitalWrite(SLEEP_GSM, HIGH);
callback = false;
sleep_sim = millis();
Serial.println(«sleep_gsm»);
}
if (millis() > 86000000) { //каждые сутки перезагрузка модема и ардуины
Serial.println(«reset_timeout»);
digitalWrite(12, LOW);
delay(100);
digitalWrite(12, HIGH);
delay(20000);
}
if (millis() > reset_if_gsm_hover) { //если модем завис и не отвечает перезагружаем его и ардуино, проверка каждые полчаса
Serial.println(«reset_timeout_gsm»);
reset_if_gsm_hover = millis() + 1800000;
_response = sendATCommand(«AT+SAPBR=0,1», true);
if (_response.length() < 1) {
Serial.println(«reset_gsm»);
digitalWrite(12, LOW);
delay(100);
digitalWrite(12, HIGH);
delay(20000);
}
}
if (SIM800.available()) { // Если модем, что-то отправил…
reset_if_gsm_hover = millis() + 600000;
sleep_sim = millis();
digitalWrite(SLEEP_GSM, LOW);
_response = waitResponse(); // Получаем ответ от модема для анализа
_response.trim(); // Убираем лишние пробелы в начале и конце
Serial.println(_response); // Если нужно выводим в монитор порта
String whiteListPhones = "+79500000000, +79990000000, +79530000000, +79220000000"; // Белый список телефонов
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)); //… получаем номер
LastNumber = innerPhone;
Serial.println(«Number: » + innerPhone); // Выводим номер в монитор порта
}
// Проверяем, чтобы длина номера была больше 6 цифр, и номер должен быть в списке
if (innerPhone.length() >= 7 && whiteListPhones.indexOf(innerPhone) >= 0) {
digitalWrite(3, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100); // wait for a second
digitalWrite(3, LOW); // turn the LED off by making the voltage LOW
sendATCommand(«ATA», true); // Если да, то отвечаем на вызов
sendATCommand(«AT+CREC=4,»C:\User\6.amr",0,99", true); //проигрываем аудиофайл
sendATCommand(«ATH», true); //сбрасываем
timing = millis(); //сбрасываем счетчик до засыпания модема
callback = true; //разрешаем перезвонить через заданное время если завелась
} else {
sendATCommand(«ATH», true); // Если нет, то отклоняем вызов
}
}
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 на элементы
}
if (_response.indexOf("+CIPGSMLOC: 0,") > -1) { //поиск, формирование ответа и ответ с координатами
String LAT;
String LNG;
String Answer;
LAT = _response.substring(_response.indexOf("+CIPGSMLOC: 0,") + 24, _response.indexOf("+CIPGSMLOC: 0,") + 33);
LNG = _response.substring(_response.indexOf("+CIPGSMLOC: 0,") + 14, _response.indexOf("+CIPGSMLOC: 0,") + 23);
Answer = «yandex.ru/search/?text=E» + LNG + "%20N" + LAT + "n" + readVoltage(200) + «V»;
Serial.println(«GSM»);
sendSMS(LastNumber, Answer); //отправляем смс на последний номер
sendATCommand(«AT», true);
sendATCommand(«AT+CMGDA=»DEL ALL"", true); // Удалить все сообщения, чтобы не забивали память модуля
sendATCommand(«AT+SAPBR=0,1», true); // отключаем интернет
}
}
if (Serial.available()) { // Ожидаем команды по Serial…
SIM800.write(Serial.read()); //… и отправляем полученную команду модему
}
//если прошла минута после звонка и напряжение больше 13,2 вольта и разрешено перезвонить звоним, значит завелась
if (millis() — timing > 45000 && callback == true) {
if (readVoltage(100) > 13.2){
sendATCommand(«ATD» + LastNumber + ";", true); //
delay(1000); //
callback = false;
}else{
callback = false;
}
}
}
// узнаем напряжение бортовой сети
float readVoltage(int samples) { // samples — сколько раз нужно прочитать сенсор
unsigned long avg_sum = 0;
float u1;
for (int i = 0; i < samples; i++) {
avg_sum += analogRead(A6);
delay(1); // небольшая пауза между замерами
}
u1 = (avg_sum / samples * 3.3 / 1024. / R2 * (R1 + R2));
Serial.println(u1);
return u1;
}
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);
LastNumber = msgphone;
Serial.println(«Phone: » + msgphone);
Serial.println(«Message: » + msgbody);
if (msgbody.startsWith(«GPS_SEND»)) {
sendATCommand(«AT+SAPBR=3,1,»CONTYPE",«GPRS»", true);
sendATCommand(«AT+SAPBR=3,1,»APN",«internet»", true);
sendATCommand(«AT+SAPBR=1,1», true);
sendATCommand(«AT+CIPGSMLOC=1,1», true);
}
if (msgbody.startsWith(«V_SEND»)) {
String Answer;
String LNG = «Voltage = »;
Answer = "" + LNG + readVoltage(100) + «V»;
sendSMS(LastNumber, Answer);
}
if (msgbody.startsWith(«E_START»)) {
digitalWrite(3, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100); // wait for a second
digitalWrite(3, LOW); // turn the LED off by making the voltage LOW
timing = millis();
callback = true;
}
if (msgbody.startsWith(«CALL_BACK»)) {
sendATCommand(«ATD» + LastNumber + ";", true);
}
}
void sendSMS(String phone, String message)
{
sendATCommand(«AT+CMGS=»" + phone + """, true); // Переходим в режим ввода текстового сообщения
sendATCommand(message + "rn" + (String)((char)26), true); // После текста отправляем перенос строки и Ctrl+Z
}
Логика работы и смс команды
Белый список номеров телефонов находится в самом скетче. При звонке с незнакомого номера вызов просто сбрасывается.
Если номер известен осуществляется попытка запуска и вызов сбрасывается в течении 2 сек. Через 45 сек если напряжение борт сети равно или более 13,2В значит двигатель работает и модуль перезванивает на последний входящий номер, его можно сбросить, но раз в месяц можно и ответить на звонок, чтобы были какие-то списания с сим карты установленной в модеме и оператор сот связи был доволен))
Смс команды.
отправляются только с номеров со списка.
GPS_SEND отправляет в ответ приблизительные координаты. Сделал на случай угона и если поставят в глухие дворы отстаиваться, так можно попытаться найти.
V_SEND отправляет в ответ напряжение борт сети.
E_START запуск через смс, тоже что и со звонком.
CALL_BACKв ответ перезванивает и через подключенный микрофон можно слушать что происходит в салоне, тоже на случай угона.
В итоге получилось надежное устройство автозапуска, с минимумом вложений. Сим карта без абонентской платы, но нужно периодически чтобы отправлялось смс или совершался звонок, так как операторы связи этого требуют.
Для тех кто решится повторить, отвечу на вопросы с удовольствием.
Upd. Забыл добавить, сначала была проблема, машина не заводилась, сигнализация пищала при попытке запуска. Как оказалось помехи от блока влияли на выносной датчик вибрации сигнализации, получалось в момент звонка сигнализация «думала» что машину трясут и не позволяла завести двигатель. Решилось всё перемещением датчика на 30 — 40 см от блока запуска.