Самодельный GSM термостат для удаленного контроля системы отопления (котла) через SMS

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

Последня версия прототипа на февраль 2019:

Идея сделать свой GSM термостат появилась не сразу, но после разочарования в готовых решениях. Читайте обзор WiFi термостата POER и доступных в продаже GSM/SMS-термостатов. Wi-Fi термостат оказался бесполезным в плане пресечения проблем с котлом (отключение электричества на долгий период, нежелание котла зажечься в -20 и т.п.), что приводит к заморозке воды в доме и затратам на ремонт, а готовые термостаты кажутся мне весьма неудобными при излишне завышенной цене (в среднем 7-10 тысяч). Создание своего GSM термостата позволяет за разумные деньги (до 3 тысяч рублей) сделать изделие, которое будет заточено под конкретные пожелания создателя и по возможностям будет превосходить готовые решения за 9 тысяч рублей. Первоначальная идея была взята из темы «Управление отоплением в загородном доме (GSM)«. Там же можете скачать готовые скетчи для девайса, однако, я настоятельно рекомендую разобраться в программировании для Arduino и написать свой скетч, отвечающий Вашим личным нуждам. Так Вы получите не только недорогой GSM термостат, но и новое хобби, которое увлечет Вас как минимум на несколько недель.

Итак, для создания термостата была выбрана платформа Arduino, как наиболее дружелюбная к тем, кто очень далек от электроники и программирования микроконтроллеров. Уже на второй неделе после того, как я узнал об Arduino, я смог сам сделать термометр с логгером на SD карту, а на третьей неделе у меня уже был немного работающий прототип GSM-термостата. Почему я назвал его «немного работающим» — да дело в том, что просто создать девайс (соеденить провода) — дело весьма быстрое и незамысловатое, но написать программное обеспечение, которое будет работать правильно при любых нестандартных ситуациях — вот это самое сложное. Ведь если не продумать все возможные варианты развития ситуации, прибор может войти в «вечный цикл» и перестать выполнять даже свои первостепенные задачи, такие как поддержание нужной температуры в доме, не говоря уже об управлении котлом с помощью SMS. Посмотрев несколько ардуино-скетчей по контролю климата в доме, я сначала порадовался, что не придется писать свой код, но потом, познакомившись поближе с ардуино и вникнув в суть имеющихся готовых программ по работе с GSM, ужаснулся. Использование подобных программ может нанести больше вреда, чем пользы.

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

Итак, вот список того, что, по моему мнению, должен делать нормальный термостат. К сожалению, по мере роста кода, я столкнулся с проблемой ограниченности ресурсов Arduino (32 килобайта на код и 2 килобайта на переменные — это очень мало). Не всё, что я могу сделать, может существовать в рамках одного проекта, но, тем не менее, делюсь идеями, а каждый сам решит, какие фичи ему более полезны и интересны.

  • Термостат должен показывать текущую температуру и установленную пользователем температуру, которую будет поддерживать котел.
  • Чтобы котел не включался/выключался каждые 5 секунд, используется гистерезис, который определяет границы допустимой температуры. Например, при желаемой температуре 20 градусов и гистерезисе 1 градус, котел будет включаться при температуре <=19 градусов и выключаться при температуре >=21 градус. В интервале от 21 до 19 градуса котел будет выключен.
  • В случаях, когда термостат обесточен (или не работает по непонятным причинам), котел должен иметь возможность работать на нагрев. Это обеспечивается особым подкючением котла к реле, когда котлу разрешен нагрев тогда, когда на реле не подается ток с термостата. Когда же термостат подает ток на реле, котел прекращает нагрев. Таким образом, если термостат глюконёт и выключится или потеряет питание, котел все еще сможет выполнять свои функции, и вероятность заморозки дома уменьшается. Обратите внимание, существуют модули реле, которые по-разному отрабатывают входной сигнал. Логическая единица может включать реле или выключать его. На некоторых модулях есть джампер, который меняет логику, но на многих его нет (не путать с джампером внешнего питания реле). Таким образом, если Вы скачали чей-то скетч, но он действует «наоборот», значит у Вас релейный модуль с другой логикой (нужно править скетч).
  • Кроме гистерезиса, используется контроль по границам температуры, который позволяет удаленно понять, что с котлом что-то не так. Например, если желаемая температура 20 градусов, падение температуры установлено 3 градуса и превышение температуры 5 градусов, то при выходе реальной температуры за границы между 17 и 25 градусами, термостат высылает SMS, что что-то не так. Границы подбираются экспериментально и с небольшим запасом такими, за пределы которых при нормальной работе котла температура в поещении не выходит. Я специально сделал именно значения относительно установленной желаемой температуры, а не фиксированные значения типа от +17 до +25, чтобы при переключении желаемой температуры с 20 на 10 градусов (например, на время отсуствия в доме), не пришлось бы переставлять границы вручную — они автоматически обновятся до границ от +7 до +15.
  • Поскольку отопление газовое, неплохо было бы контроллировать утечку газа. Для этого используется датчик газа. Сам датчик передает значения от 0 до 1023, но для удобства я использую процентное значение от 0 до 99%. Также устанавливается значение, при превышении которого прибор отправит тревожное SMS. Учитывайте, что газовый датчик потребляет 120 мА и есть смысл отключать его при переходе на резервное питание в случае отключения электроэнергии, иначе он быстро высадит резервный аккумулятор. Также тем, кто экономит на электроэнергии, стоит иметь в виду, что добавление датчика газа в проект увеличит расход электроэнергии на 12.3 квтч в год — много это или мало — решайте сами. По моим наблюдениям, располагать его «где-то на столе» — практически бесполезно (вы задохнетесь или взорвете всё быстрее, чем он среагирует). Чтобы он реально поднял панику вовремя, надо обеспечить активное попадание в него газа, то есть располагать его сверху, над местом потенциальной протечки газа, что существенно усложняет монтаж и уменьшает мобильность термостата. Так что пока у меня есть сомнения на счет необходимости использования данного датчика. MQ-9 был достаточно чувствительным, но я его спалил, а вот MQ-2 весьма вяло реагирует как на природный, так и на сжиженный газ. В добавок, учитывайте, что показания этих датчиков сильно зависят от температуры окружающей среды. При холодном старте показания зашкаливают за 30% при том, что через несколько минут датчик нагревается и показания падают до 5%. Это не позволяет поставить достаточно низкий порог на отсылку тревожных СМС, а чтобы довести значение до 30%, нужно целенаправленно держать датчик над источником газа.
  • Модем не должен слать тревожные SMS каждые 10 секунд, иначе баланс быстро сойдет на ноль. Пока для решения этой проблемы я сделал триггер. После первого тревожного SMS отсылка последующих SMS не производится до сброса триггера специальной командой (перед этим следует вручную устранить проблему, вызвавшую отсылку тревожного SMS).
  • Кроме первого реле, управляющего котлом по температуре, есть еще три реле, которые включаются и выключаются вручную. Я планирую использовать одно следующим образом. Если котел заглючит и температура начнет падать, придет тревожное SMS. Если электричество при этом будет, я удаленно включу второе реле, к которому будет заранее подключен электрический обогреватель. Таким образом, он сможет поддерживать положительную температуру в помещении с водой до того момента, как получится приехать и выяснить, почему котел встал (прецеденты уже были).
  • Все настройки должны храниться в энергонезависимой памяти и восстанавливаться после отключения от сети или сбоев питания.
  • Для экономии денег на SMS при настройке прибора хорошо бы иметь альтернативный метод настройки при нахождении вблизи прибора. Пока я без проблем меняю все настройки через терминал при подключении к компу, но, когда прибор будет готов, это будет неудобно. Поэтому к пинам 0 и 1, когда компьютер уже не используется, можно подцепить Bluetooth модуль HC-05, и проводить все те же настройки с телефона через Bluetooth терминал. Не забываем, что HC-05 работает с уровнем 3.3 вольт, нужно подключать RX блютус модуля через делитель напряжения (10к и 4.7к резисторы — схемы смотрите в инете). Для андройда можно использовать бесплатный Serial Bluetooth Terminal. Связка со смартфоном работает отлично, таким образом, за изменение настроек при нахождении рядом с девайсом платить не придется.
  • Ну а когда настройки приходится менять удаленно, опять же ради экономии, девайс должен принимать несколько команд в одном SMS, но, при этом, комадны должны быть интуитивными и запоминающимися, чтобы не носить с собой справочник. Свой вариант предлагаю ниже.
  • Поскольку модем уже имеет встроенную возможность выходить в интернет через GPRS (без Wi-Fi), есть смысл вести лог ошибок и параметров прибора на удаленном сервере. Таким образом, выяснять статус можно удаленно и без оплаты SMS, если, конечно, на тарифе оплачен доступ в интернет. Реализация данного функционала вполне несложная (описана ниже).
  • Нужно позаботиться об источнике бесперебойного питания для термостата, чтобы при отключенном напряжении 220 вольт термостат мог высылать тревожные SMS о пропадании электричества и о недопустимом падении температуры, когда дом начнет остывать. Чтобы сильно не заморачиваться и не усложнять скетч, я пошел простым путем. Ардуино имеет несколько входов для питания. Если подключить несколько источников питания, она питается от источника с большим напряжением. Так, если использовать блок питания на 9 вольт в круглом разъеме питания и повер-банк в разъеме usb, ардуина будет питаться от блока питания, и переходить на повер-банк в случах отключения электроэнергии. Недостаток конструкции только в том, что повер-банк сам не заряжается после того, как разрядится, что не очень хорошо при регулярных сбоях в электроснабжении. То есть либо надо периодически подзаряжать его вручную, либо пустить на него отдельный внешний блок питания для автоматической подзарядки (зарядка от 5 вольт ардуины может прогнуть всю систему). Как вариант, «раздвоить» шнур блока питания, пустить 9 вольт напрямую на ардуину и 5 вольт через step-down converter на зарядку повербанка. Для проверки на отключения 220 вольт, чтобы выслать смс об отключении и включении, многие рекомендуют использовать реле на 220 вольт, или вообще мудрствуют с оптронами и прочими делами, но это в любом случае лишняя занятая розетка. Я сделал проще. У меня был Voltage Sensor (по сути это модуль из двух аккуратно оформленных резисторов). Суть в том, что на модуль подается от 0 до 25 вольт, а модуль преобразует это напряжение в годное для чтения analogRead(). Можно подпаяться напрямую к штекеру питания, но, точность здесь не принципиальна, и поскольку у меня внешнее питание 9 вольт, а резервное 5 вольт, я могу отследить эту разницу на пине VIN. В моем случае при питании от 220 вольт на VIN считывается 8.11 вольт (8.18 мультиметром), а при питании повер-банком 4.50 вольт (4.34 мультиметром). Так, простое условие if (voltage>6) позволяет понять, что напряжение 220 вольт присутствует, и не надо занимать лишнюю розетку и вообще баловаться с высоким напряжением!

Что хотелось бы, но еще не сделано:

  • Для разбирательств в проблемных случаях хорошо бы вести лог температур и действий термостата на MicroSD карту. С точки зрения железа и программирования проблем тут никаких нет, но проблема в ресурсах Arduino. Подключение одной только библиотеки SD сжирает чуть ли ни половину доступной памяти. Как вариант, библиотека SdFat, которая не столь требовательна к памяти, но и с ней мой код не влез в лимит 32 кб. На Arduino UNO это точно не выйдет, разве что переделывать всё на Arduino Mega. Так что эта задумка пока под вопросом.

Команды (посылаются через терминал или SMS, или со смартфона, подключенного по Bluetooth):

Для фильтрации рекламных SMS и прочих мессаг от чужих людей каждое управляющее SMS должно начинаться с пароля, который невозмжно встретить в случайном SMS. Фильтрация по номеру телефона распростанена в подобных проектах, но она может сыграть в Вами злую шутку (например, если в случае аварии нужно будет срочно получить информацию с места или выслать какие-то управляющие команды, а зарегистрированного в термостате телефона почему-то не будет под рукой (кончатся деньги, сядет батарейка, забудется дома — вариантов куча). Пароль же можно применить с любого телефона, и вероятность того, что его наберут посторонние люди «случайно» практически нулевая (если, конечно, пароль не банален, типа «Привет!». Предположим, для примера, что пароль установлен «#pass#». Дальшейшие команды вводятся одной строкой и разделяются знаком-разделителем. У меня это «,». Команды выполняются в указанной последовательности слева направо, регистр букв не имеет значения.

Основные команды.

«T=x» устанавиливает автоматическое поддержание температуры на x градусов.

«H=x» устанавливает гистерезис для автоматического поддержания температуры.

«A=1/0» включает или выключает автоконтроль температуры на первом реле.

«L=1/0» включает или выключает SMS-предупреждения о выходе температуры за установленные границы.

«LH=x» верхний предел допустимой температуры (=T+x).

«LL=x» нижний предел допустимой температуры (=T-x).

«G=x» верхний предел уровня газа в помещении (в процентах). «G=0» отключает контроль утечки газа.

«R=xxxx» устанавливает значения четырех реле. 1=вкл, 0=выкл, -=оставить как есть. Пример: «R=-1-0» включает второе реле, выключает четвертое реле, остальные остаются без изменений. Допускаются сокращения, например «R=1» — включает первое реле, остальные без изменений.

«W» сбрасывает триггер предупреждений (разрешает отправку тревожных смс). W=1 включает триггер (на всякий случай для первоначальных настроек — обычно это не нужно).

«S» статус (высылает в SMS все то, что обычно видно на экране прибора — все данные). Того же можно добиться без отправки SMS, просто позвонив бесплатно на номер термостата (в этом случае данные высылаются только при звонке с админского телефона). Термостат сбросит вызов и отправит SMS со статусом на номер админа. На чужие звонки модем не ответит.

«I» — срочно выслать данные на сервер в интернете (не дожидаясь положенного по расписанию времени).

Технические и отладочные команды для проведения первоначальной настройки.

«LCD=1/0» включает или выключает подсветку LCD экрана. Так как при отсутствии людей в доме подсветка не нужна, ее можно выключить (потребление электричества в этом случае уменьшается на 0.5 ватт, что составляет примерно 4.4 квтч в год).

«AT=команда» — отправляет на модем указанную команду, например «#pass#AT=AT+CPOWD=1» — выключает модем. Список AT команд смотрите в документации к своему модему. Фича сделана, в основном, чтобы играться с модемом через терминал, но если приспичит, можно отправить команду и по SMS.

«GSMRST» — позволяет выключить и включить модем снова (на случай, если заглючит).

«RST» — перезагрузка ардуины (если заглючит).

«RSTDEF» — перезагрузка со сбросом всех параметров на прошитые в скетче по умолчанию (если дела совсем плохи).

«PW=newpass» — замена пароля.

«PH=+79991234567» — замена админского телефонного номера.

«SEP=&» — замена знака-разделителя команд. Сохраняется только до перезагрузки. Замена разделителя пригодится только если нужно передать значение, имеющее запятые в AT-командах модема.

«GA=1/0» — gsmAllow (разрешает/запрещает работу с GSM — прием/отправка смс, прием входящего звонка).

«SA=1/0» — smsAllow (разрешает/запрещает отправку любого смс — не только тревожных, как W).

«IA=1/0» — iLogAllow (разрешает/запрещает выход в интернет для отправки лога на сервер).

«CR=x» — clock refresh time (количество секунд для обновления часов на экране).

«TR=x» — temp refresh time (количество секунд для обновления значений датчиков).

«GR=x» — gsm refresh time (количество секунд, через которое проверяется модем).

«IR=x» — iLog refresh time (количество секунд, через которое отправляется лог на сервер).

«DT=yyyymmddhhmmss» — установка даты и времени.

«PAR» — показать значения вышеуказанных параметров.

«HELP» — высылает все возможные команды в одном SMS (если память подводит):
S,T,H,A,L,LH,LL,R,G,W,I,LCD,PAR,GA,SA,IA,CR,TR,GR,IR,DT,PW,PH,SEP,GSMRST,RST,RSTDEF

Ну и крутяцкая фича самодельного термостата в том, что все команды можно выслать в одном CMC для экономии, так как тупые «готовые» GSM-термостаты принимают по одной команде за раз, то есть для изменения пяти параметров нужно выслать 5 СМС. Здесь же все просто. Пример: держать температуру в помещении 21 градус, включить второе реле, разрешить прием тревожных СМС, запросить статус:

#pass#t=21,r=-1,w,s

В ответ получаем SMS о состоянии прибора после проведенных действий:

T=22.75; A(1)=21.00,0.10; L(1)=18.00-26.00; R=01; G=10/15; N=8; W=1; 2019.02.10/13:00:40

Ну как-то так все и работает.

Отправка данных на интернет-сервер по GPRS.

Модем, в моем случае на базе SIM900, умеет выходить в интернет без дополнительного оборудования. Это можно использовать, чтобы сохранять данные об ошибках и периодически вести лог параметров устройства (температуры в доме, состояния реле в разное время и т.п.). Эти данные можно использовать как для удаленного контроля без необходимости оплаты SMS, так и для выяснения обстоятельств в случае проблем с котлом (когда именно котел встал, как быстро падала температура в доме и т.п.).

Самая простая реализация следующая:

Если еще нет своего сайта, делаем сайт (можно на бесплатном хостинге, но обязательно с поддержкой PHP).

Делаем какой-нибудь скрипт, который сохраняет параметр из адресной строки в файл или в MySQL базу данных — второй способ более навороченный, но позволит в дальнейшем удобнее обрабатывать данные. Наипростейший пример («log.php»):

<?php

$myFile = «log.txt»;
$fh = fopen($myFile, ‘a’) or die(«Can’t open file»);
if ($_GET[‘t’]==») die(«Invalid request»);
fwrite($fh, strftime(«%Y-%m-%d,%H:%M:%S,»));
fwrite($fh, $_GET[‘t’]);
fclose($fh);

?>

В результате получаете лог типа такого при проветривании помещения:

2019-02-14,09:54:20,23.25
2019-02-14,09:55:20,22.50
2019-02-14,09:56:19,22.00
2019-02-14,09:57:24,20.75
2019-02-14,09:58:25,20.25
2019-02-14,09:59:19,20.25
2019-02-14,10:00:19,20.25
2019-02-14,10:01:20,19.75
2019-02-14,10:02:19,19.50

В скетче периодически по таймеру делаем GET запрос на наш сервер с нужными данными. Например, если сейчас 21.75 градусов, делаем запрос: «http://server.com/log.php?t=21%2E75». Здесь %2E — это hex значение точки — все символы, которые недопустимы в параметрах урла, нужно заменить на hex значения перед отправкой.

Я с GPRS интернетом мучался два дня, так что расскажу, где могут быть затыки. Вообще, эта статья очень помогла мне разобраться с командами, там же узнаете, какие ответы что означают. Шлем следующие AT команды модему, не забывая дожидаться ответа об успешном выполнении после каждой команды.

AT+CGATT=1
AT+CGATT?
AT+SAPBR=3,1,»CONTYPE»,»GPRS»
AT+SAPBR=3,1,»APN»,»internet.beeline.ru» //internet.tele2.ru или internet.mts.ru
AT+SAPBR=3,1,»USER»,»»
AT+SAPBR=3,1,»PWD»,»»
AT+SAPBR=1,1
AT+HTTPINIT
AT+HTTPPARA=»CID»,1″
AT+HTTPPARA=»URL»,»http://server.com/log.php?t=some_data»
AT+HTTPACTION=0
AT+HTTPTERM
AT+SAPBR=0,1

Максимальное время ожидания подбирается экспериментальным путем. Большинству команд хватит 200-300мс, но AT+CGATT=1 может выполняться несколько секунд, AT+SAPBR=1,1 тоже долгая команда, AT+HTTPACTION=0 — это и есть отправка данных в интернет. Я поставил максимальное время ожидания 15 секунд, хотя обычно она проходит за 3-5 секунд (зависит от качества связи). В изначальной версии своего скетча я делал как в скетчах-примерах, которые можно найти в интернете: тупо отсылал команду модему и ждал от 0.3 до 10 секунд после каждой команды, надеясь на положительный результат. Иногда модем не укладывался в отложенные рамки, иногда выдавал ошибку, а скетч слал следующие команды, не дождавшись выполнения предыдущих, и получалась полная фигня. Результат отправки был хаотичным, иногда почти всегда, а иногда один успешный раз за 3-5 попыток, и занимало это по 20-30 секунд на попытку. Делать удлиненные задержки по несколько секунд для 10 команд — это тоже дичь, в таком случае ардуина только и будет, что ждать, особенно, если лог отправляется каждую минуту. В общем, в какой-то момент постоянные глюки начали меня злить, и я пошел искать, как делают нормальные люди. Есть библиотека GPRS_Shield_Arduino. Она жирненькая, так что в моем проекте не прижилась, но я подсмотрел там функцию sim900_wait_for_resp, немного ее изменив под свои нужды. Суть функции в том, что мы посылаем в модем AT-команду и вызываем эту функцию с двумя параметрами — какой должен прийти ответ при благоприятном исходе, и максимальное время, которое можно ожидать ответ. Если ответ приходит быстрее, функция сразу же завершается. Этим мы добиваемся двух целей: команды выполняются без фиксированной задержки максимально быстро, и мы знаем результат выполнения (OK или нет). Только когда я применил этот подход, почти все* попытки отправить данные на сервер стали успешными, и время отправки уменьшилось до 5-10 секунд.

Ну а имея данные на сервере в интернете, с ними уже можно делать что угодно — выводить на экране в удобном виде, строить графики, рассылать тревожные email’ы при выходе параметров за разумные границы и т.п. Я также думал об обратной связи — чтобы из интернета слать команды на модем, но пока не вижу в этом большой необходимости — это излишнее нагромождение в проекте, а у меня уже на это нет памяти — 31.8 кб из 32 занято. Да и если всё исправно работает, то и слать на термостат ничего не нужно. А я все же надеюсь, что термостат будет работать исправно 🙂 Если же кто-то планирует это сделать, у меня в мыслях два варианта:

  1. Каждый раз, когда термостат запрашивает log.php, пусть читает его вывод (команда AT+HTTPREAD). Можно сделать на сайте страницу для задания команд и настроить всё так, чтобы команды выводились файлом log.php. Термостат периодически всё равно запрашивает этот файл, заодно захватит и подготовленные для него команды. Этот подход имеет смысл только если термостат настроен на частую отправку лога в интернет, например, каждую минуту, иначе выполнения команд не дождешься…
  2. Другой способ — если при запросе log.php термостат высылает в параметрах кроме всего прочего и свой IP адрес. На модеме можно развернуть и небольшой HTTP сервер, а его IP будет вычленяться из log.php и выводиться на Вашем сайте (в случае динамического IP это особенно актуально). Далее Вы просто в браузере набираете IP термостата и уже на сгенерированной им HTML странице вводите команды, а ардуина их парсит и выполняет.

Еще один важный момент по отправке данных через GPRS. Поскольку трафик с термостата составит несколько десятков мегабайт в месяц, платить ежемесячно за анлим для термостата — как-то жирно. Вполне хватит пакета на 50-100 мегабайт. Но и тут опсосы схитрили, чтобы увеличить свои прибыли. Предположим, наш термостат делает один GET запрос на 1 килобайт в минуту, 60 запросов в час, 1440 запросов в сутки, 44640 запросов в месяц. Грубо говоря, модему нужно 45 мегабайт в месяц для выполнения своих обязанностей. Опсосы прекрасно понимают, что на этом много с нас не сдерешь, поэтому в предложении интернета мелким шрифтом пишут, что-то вроде «округление трафика в большую сторону до 100 кб». Что это для нас значит? Это значит, что открыв сессию, скачав 1 кб, и закрыв сессию, с нас «учтут» 100 кб трафика. Значит наши 45 мегабайт в месяц мистическим образом превращаются в 4.5 гигабайта. Нехило, да? А мегафон, помнится, в какое-то время округлял трафик до мегабайта! Что с этим можно сделать. Ну во-первых, поискать предложения интернета с наименьим округлением. В идеале, можно поискать предложения с неоплачиваемым минимумом трафика, но не знаю, остались ли еще такие. Если тарифицируемый минимум там больше килобайта, получится, что мы вообще как бы не будем платить за трафик. Если с этим не повезло, то код скетча придется поменять, так, чтобы модем не закрывал сессию после отправки каждого пакета. В нашем случае, AT+SAPBR=1,1 открывает сессию, а AT+SAPBR=0,1 закрывает сессию. Изменим код так, чтобы сессия открывалась один раз и никогда не закрывалась. Правда, нужно учесть, что опсос сам может разорвать сессию, так что перед отправкой данных нужно будет проверять, активна еще сессия или нет, и если ее разорвали, открывать заново. Проверяем командой AT+SAPBR=2,1, получаем ответ +SAPBR: 1,1,»наш IP», если вторая цифра 1, значит соединение установлено. Таким образом, если опсос не будет слишком часто разрывать сессию, мы заплатим примерно за тот трафик, который реально использовали.

* Выявленные проблемы. Во время поступления внешних данных на модем (входящий звонок, пришедшее смс), модем выдает в буфер данные, которые мешают нормальному парсингу ответов, которые в этот момент идут от команд при отправке данных на интернет-сервер. В результате, прибор «захлебывается» левой информацией и отправка не удается. Пока я не понял, как с этим бороться. Вроде бы и мелочь — вероятность, что кто-то позвонит в те самые 5-10 секунд, когда отправляются данные — весьма невелика, но, с другой стороны, именно непродуманность таких мелочей и отличают «быдлокод» от нормального кода. Куда более неприятно, что пока ардуина отправляет данные на сервер и парсит ответы модема, она пропускает звонки, а значит, и входящие смс. Короче, как-то «по-китайски» получилось. Если кто-то обошел эту проблему — пишите, какие есть варианты.

Остальное…

Конечно, прототип еще сырой, и многие вещи хотелось бы улучшить, но тут есть такая проблема: научиться писать кое-как работающий «быдлокод» для ардуино может каждый за неделю, но научиться писать качественные и надежные программы — нужно время и на учебу, и на отладку. Поэтому пока я предлагаю эту статью только в качестве идеи для тех, кто хочет попробовать свои силы в создании подобного прибора, у меня же еще впереди немало времени на тесты, прежде чем я решусь оставить с ним котел наедине без присмотра хотя бы на недельку. В любом случае, работа над этим прибором была интересной и познавательной.

 

Теперь несколько слов о потенциальных проблемах, которые я обнаружил в скетчах, имеющихся в интернете.

Об использовании millis(); в качестве таймера.

Классический пример, который можно видеть чуть ли ни во всех скетчах, которые мне попались:

unsigned long currentTime;
unsigned long loopTime;
currentTime = millis();
if(currentTime >= (loopTime + 10000)){
loopTime = currentTime;
/* какое-то важное действие, которое надо выполнять каждые 10 секунд */
}

Это часто работает при первоначальных проверках, но копнем глубже, прочитаем инструкцию:

millis() — Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней.

Итак, предпложим, наш прибор каким-то чудом проработал 50 дней. Незадолго до этого значение loopTime + 10000 переходит через 4,294,967,295, и получает некоторое значение меньше 10000 в данном примере. А millis все еще находится на каком-то гигантском числе. Так вот, в каждый момент, пока millis не сбросится на 0, будет выполняться «какое-то важное действие» (в данном примере), которое по задумке должно было выполняться только раз в 10 секунд. И количество этих выполнений будет сравнимо с числом миллисекунд, которое прибавляется к loopTime (чем длиннее интервал и реже должно выполняться действие, тем больше раз оно в реальности выполнится). У меня нет желания ждать 50 дней, поэтому я накидал несложный скетч для проверки такой реализации в рамках unsigned char (0-255):

unsigned char ct =0; // типа замена таймера millis(); в рамках 0-255
unsigned char lt = 0; // текущее значение времени цикла

void setup() {
Serial.begin(9600);
}

void loop() {
if (ct>=byte(lt+10)) // выполняем каждые 10 единиц времени ct
{
Serial.println(» WIN!»);
lt=ct;
} else Serial.println();

ct++;
Serial.print(«ct=»+String(ct)+» lt=»+String(lt)+» lt+10=»+String(byte(lt+10)));
delay(30);
}

И что Вы думаете, последний раз прога нормально отработала на ct=250, далее еще 5 раз выполнила наше действие «незапланированно», и только когда таймер, наконец, сбросился на 0, программа начала выполняться как и было задуманно (до следующего переполнения):


ct=238 lt=230 lt+10=240
ct=239 lt=230 lt+10=240
ct=240 lt=230 lt+10=240 WIN!
ct=241 lt=240 lt+10=250
ct=242 lt=240 lt+10=250
ct=243 lt=240 lt+10=250
ct=244 lt=240 lt+10=250
ct=245 lt=240 lt+10=250
ct=246 lt=240 lt+10=250
ct=247 lt=240 lt+10=250
ct=248 lt=240 lt+10=250
ct=249 lt=240 lt+10=250
ct=250 lt=240 lt+10=250 WIN!
ct=251 lt=250 lt+10=4 WIN!
ct=252 lt=251 lt+10=5 WIN!
ct=253 lt=252 lt+10=6 WIN!
ct=254 lt=253 lt+10=7 WIN!
ct=255 lt=254 lt+10=8 WIN!
ct=0 lt=255 lt+10=9
ct=1 lt=255 lt+10=9
ct=2 lt=255 lt+10=9
ct=3 lt=255 lt+10=9
ct=4 lt=255 lt+10=9
ct=5 lt=255 lt+10=9
ct=6 lt=255 lt+10=9
ct=7 lt=255 lt+10=9
ct=8 lt=255 lt+10=9
ct=9 lt=255 lt+10=9 WIN!
ct=10 lt=9 lt+10=19
ct=11 lt=9 lt+10=19

Так что ходящий по рукам код выполнения действий через заданные промежутки, рассчитанные с помощью millis() кажется мне весьма сомнительным. Понятно, что большая часть поделок на arduino не используются 50 дней подряд, а если и используются, автор уже считает, что отладка завершена и может просто не заметить странного поведения, ведь через какое-то время программа снова должна начать выполняться как следует (если только многоразовое выполнение того, что должно выполняться редко, не выведет всю систему из строя.

Варианты решения проблемы (как я их вижу):

  1. Не дожидаясь переполнения, обнулить нужные переменные самому. Например, выполнять «сброс» каждый месяц.
  2. Использовать RTC, ведь в серъезных проектах часы все равно не помешают (не зря же китайцы во всё подряд встраивают часы!). Но тут есть другая проблема — работа с RTC медленная и энергозатратная, поэтому польностью переложить всё на нее не получится. Я думаю, имеет смысл отсчитывать короткие интервалы времени через millis(), а уже остчитав, скажем, 1 секунду, получать точное значение с RTC и на основании этого значения принимать решение, что делать. Но тут еще надо поэкспериментировать. Но зато если повесить важные таймеры на DateTime.unixtime(), то ожидать переполнения в ближайшем будущем не стоит.

 

Об отсутствии проверок на текущее состояние оборудования.

Например типичное включение модема, которое можно видет в скетчах:

digitalWrite(GSM_PIN, HIGH);
delay(800);
digitalWrite(GSM_PIN, LOW);

Но эти же команды и выключают модем. Так что если по какой-то причине модем уже был включен, выполняя эти команды, мы его выключаем. Далее авторы скетчей беззаботно выполняют команды отправки SMS, даже не проверив, работает модем или нет. Имхо, вреда от такого оборудования будет больше, чем пользы — если Вы выключите модем, полагая, что вы его включаете, то вовсе останетесь без удаленного контроля.

Некоторые продвинутые программеры даже проверяют, зарегистрировался ли модем в сети, командой «AT+COPS?», и если модем возвращает «+COPS: 0», считают, что все OK, можно слать SMS. Только вот мой опыт показал, что модем может возвращать эту строку даже тогда, когда он не зарегистрирован в сети! Нужны альтернативные проверки.

Как я это себе представляю (на практике пока работает без проблем):

  • Посылаем AT-команду.
  • Если не приходит OK, считаем, что модем выключен, включаем модем, ждем секунд 5 и повторяем проверку AT-командой.
  • Если OK не приходит, считаем модем временно неисправным и включаем термостат для оффлайновой работы (котел все-таки надо регулировать). Будем повторять попытки включать модем каждые 10 минут.
  • Если приходит OK, считаем, что модем включен, но это не гарантирует наличие связи. Поиск сети может занять 20-30 секунд, после чего проверяем AT+COPS? (в идеале сравнить ответ с названием Вашего опсоса, потому что «+COPS: 0» без названия опсоса все равно оставляет Вас без связи. Как дополнительную проверку я использую «AT+CSQ», которая возвращает качество радиосвязи с опсосом от 0 до 31 (0 или 99 = связи нет). Если все это выдает нормальные значение, считаем, что связь есть.
  • Проводим проверки на работоспособность модема каждые 10 минут, на всякий случай. Мой GSM модуль прожорливый и может выключиться сам при некоторых условиях, например, при низком качестве связи, если в это время отправляется или принимается SMS, а если еще в это время и релехами начать щелкать, то шансы отрубить модем сильно увеличиваются. Конечно, сейчас я отлаживаю его по USB, а в идеале, когда все будет готово, нужно подключить блок питания на 2A, но и с ним никогда не знаешь когда отрубит, так что лучше подстраховаться.

 

О выводе SMS в сериал порт без сохранения.

Идея выводить смс с модема в сериал порт без сохранения на модеме и постоянно читать сериал ардуиной в loop вроде как работает, но только до того момента, как мы попытаемся общаться с модемом для каких-либо целей, помимо отлова «+CMT». Даже добавление простой проверки «модем еще жив?» требует отправлять в модем «АТ» и некоторое время читать вывод, ожидая «ОК». Понятно, что «АТ» выполняется быстро, и вероятность, что в эти пару десятков-сотен миллисекунд придет СМС — крайне мала. Но если мы используем долгие АТ команды, такие как установка GPRS соединения, отправка данных на сервер и чтение данных из интернета, время ожидания ответа от этих команд — уже несколько секунд. Если за это время придет SMS, насколько я понимаю, функция ожидания ответа от АТ команды просто проглотит +CMT, и SMS не будет обработана. Вот я и думаю, надежнее было бы установить в модеме сохнанение СМС на сим карту. Потом, когда модем нам будет не нужен, в loop прочитаем имеющиеся в памяти СМС, пропарсим, выполним указанные действия, удалим СМС. Конечно, это больше геммора в коде, но вероятность пропуска СМС во время взаимодействия с модемом должна сойти в ноль. Я пока что минимизировал общение с модемом и надеюсь на лучшее, но если кто-то активно использует AT-команды — имейте в виду ту ситуацию, которую я описал.

Статья будет дополняться и исправляться со временем. Если кто-то тоже работает над подобным проектом — делитесь своими мыслями в комментах. Также пишите о граблях, на которые уже пришлось наступить при подобных разработках.

Коментарий сюда