Радиоуправляемый выключатель своими руками. Часть 4 — Центр управления

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

Теперь хотелось бы воспользоваться им «на полную катушку».

Пока не хватает следующих возможностей по удаленному управлению:
  • Изменять временные характеристики работы выключателя.
  • Узнать текущее его состояние.
  • Управлять его состоянием.


Отображение текущего состояния и управление выключателем реализуем в виде веб-страницы.

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

Железо


Для дальнейшей работы я буду использовать:
  • Плата iBoard и модуль nrf24l01+ (из этого мы организуем шлюз LAN<=>RF24)
  • Сетевое хранилище Synology (как веб-сервер с базой mySQL)



Естественно, вы можете использовать то, что есть под рукой (к примеру, ардуинку с Ethernet-шилдом на Wiznet — для создания шлюза) и любой компьютер, на котором у вас работает php и есть mySQL (ничего суперспецифичного делать не будем).

Посмотрим, какие коммуникации между модулями получаются.

Распределение ролей


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

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

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

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

Роли «железкам» распределены — теперь надо придумать, как же они между собой будут «общаться».

Считаю, что будет правильно если:
  • Какой-то модуль сообщает в эфире свое состояние, то шлюз отправляет данные на веб-сервер (метод POST). Сервер делает какую-нибудь проверку на валидность данных и «укладывает» эти данные в БД.
  • Если от веб-сервера пришел запрос (метод GET) к шлюзу, то шлюз разбирает этот запрос на параметры и транслирует запрос в эфир. После этого iBoard получает ответ (или не получает) и формирует ответ (JSON).

Можно было бы в качестве ответа серверу отдавать XML, но пришлось бы оперировать более длинными строками, что не слишком просто реализовать на скромных ресурсах atmega328 (на этом МК построен iBoard).

Принцип описан, дело за конкретикой.

Шлюз LAN<=>RF24


Сначала определяемся с GET-запросом к шлюзу (пусть его IP 192.168.1.2). Буду использовать запросы вида:
http://192.168.1.2/?sid=701&cmd=2&pid=1&val=0

В запросе передается информация о том, к какому модулю идет обращение (параметр sid), команда (cmd: 1 — читать, 2 — установить), параметр модуля (pid) и требуемое значение (val).
«Человекопонятно» эта команда звучит следующим образом: «Выключатель в санузле, выключи свет» (см. предыдущий пост).

Минимальное количество параметров, которые необходим для работы — три (sid, cmd, pid), если параметров пришло меньше — модуль должен отчитаться и сообщить:
{ "message": "Bad request" }

Если модуль «отчитался» в ответ на запрос, шлюз должен выдать ответ:
{"message": "OK","sensor": 701,"parameter": {"pid": 1,"pval": 0.000,"st": 1,"note": "Ch.1 (Light)"}}

В ответе присутствует полная информация о соответствующем параметре (и ответ максимально близок к той структуре, что используется при радиообмене).

Если же ответ не получен:
{ "message": "No answer" }

Теперь уже кажется, что шлюзу как-то мало задач досталось, поэтому чуть-чуть «догрузим» его функцией получения точного времени через NTP.

Т.е. наш шлюз становится еще и «датчиком» с двумя параметрами — «дата» и «время». Как это делать я уже описывал ранее (см. Беспроводные коммуникации «умного дома». Часть вторая, практическая).

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

Когда все функции системы описаны, уже остается делом техники написать соответствующий скетч.

Прошиваем iBoard, подключаем LAN-кабель, подаем питание и если все в порядке, уже можно через GET-запросы получать состояние и управлять беспроводным модулем, т.е. уже частично решили часть задач, но пока еще не слишком удобно (скоро это исправим).

Шлюз лучше расположить в центре (квартиры, дома), чтобы расстояние до всех беспроводных модулей было бы минимальным.

Можно приступать серверной части.

Считаем, что ваш сервер уже подготовлен: веб-сервер с PHP и mySQL — работают.

Disclaimer: код очень далек от идеала и приводится исключительно в целях демонстрации основных принципов.
Proof of concept — не более того. Если будете использовать его в этом виде — только на свой страх и риск.

Сервер


Для определенности сервер имеет ip-адрес 192.168.1.10. Этот адрес фигурирует в скетче выше, там же есть адрес страницы, которая должна принимать данные от шлюза: 192.168.1.10/sensors — с этой страницы и начнем.

База данных

Но перед тем, как начинать разрабатывать страницу — подготовим базу данных. Самый простой способ — сделать это через phpMyAdmin.

У меня база данных называется db_sensors и содержит всего две таблицы: sensor_list и sensor_value (назначение таблиц отлично угадываются из их названий).
Скрипт для создания таблиц
--
-- Структура таблицы `sensor_list`
--

CREATE TABLE IF NOT EXISTS `sensor_list` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `unit` varchar(16) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `comment` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `SensorID` int(11) NOT NULL,
  `ParamID` int(11) NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `value` float NOT NULL COMMENT 'Последнее значение',
  `last` datetime NOT NULL COMMENT 'Время последнего коннекта',
  PRIMARY KEY (`id`)
) ;

-- --------------------------------------------------------

--
-- Структура таблицы `sensor_value`
--

CREATE TABLE IF NOT EXISTS `sensor_value` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `sensor_id` int(11) NOT NULL,
  `value` float NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ;


База готова, создаем страницу, которая бы принимала данные и сохраняла бы их в базу.

Логирование данных

Чтобы было проще вести отладку — сделаем эту страницу в виде формы, которая постит данные сама на себя и проверяет их на валидность.

Дополнительно введем в список обязательных данных запроса правильный ключ (который в явном виде задан в скетче шлюза) для того, чтобы принимать запросы только от «своих». Понятно, что все не слишком безопасно, но исходя из disclaimer, вполне допустимо.

Логика работы скрипта следующая:
  • Первичная проверка на наличие всех необходимых параметров и проверяем валидность ключа.
  • Проверяем, есть ли в таблице sensor_list запись о таком «сенсоре». Если такой записи нет — создаем ее и получаем идентификатор записи, иначе — получаем идентификатор уже имеющейся.
  • Записываем в таблицу sensor_value соответствующее значение «сенсора».
  • Дополнительно обновляем в таблице sensor_list последнее значение соответствующего сенсора и фиксируем время последнего контакта.

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

Кстати, выборку из этой таблицы с актуальными данными удобно использовать в качестве данных для Universal Widget (жаль, что проект очень давно не обновлялся).

Скрипт подготовили, файл назвали index.php, разместили его в папке \sensors\ (относительно корня сайта).

Архив со всеми файлами веб-сервера доступен по ссылке.

Теперь можно к нему обратиться по адресу: 192.168.1.10/sensors

Если все сделано правильно, то по этому адресу откроется форма, в которую можно руками вставить тестовые значения и проверить (через phpMyAdmin, например), что данные проверяются и правильно помещаются в БД.

Если у вас уже работает радиовыключатель и шлюз — в базу будут поступать и эти данные.


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

Чтение и изменение параметров модулей

Создадим другой скрипт (по адресу 192.168.1.10/tests), который из себя тоже будет представлять форму, с помощью которой сервер будет формировать запросы к нашему шлюзу, получать ответы и отображать их в этой же форме.


На изображении выше уже результат отработки формы: два верхних поля и селектор — формируют данные для запроса, «ОК» — это значение параметра message из ответа, остальные параметры — соответствующие части ответа.

Парсинг ответа делаем с помощью JavaScript.

В работе этого скрипта есть маленькая хитрость. Данные формы отправляются не сразу на шлюз, а проходят через файл json-proxy.php

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

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

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

Главная страница центра управления

Теперь уже можно объединить наши наработки, чуть-чуть прочитать про AJAX и сделать вот такую страницу (по адресу 192.168.1.10/tests/switch.php):

Для обновления данных конкретного параметра вызывается функция chekIt(701, 1, 'ch1'); (собственно, три аргумента: номер модуля, номер параметра и идентификатор div, содержимое которого надо обновить).

Страница отображает временные параметры работы модуля (чтобы сильно не напрягать наш слабенький по ресурсам шлюз) — эти данные запрашиваем раз в 5 минут.

Данные о состоянии каналов освещения и вентиляции обновляем чаще — один раз в 5 секунд (достаточно часто чтобы не приходилось долго ждать изменения состояния после нажатия соответствующей кнопки включения или выключения).

На кнопки «повешены» вызовы функции doIt():
<input type="button" value="освещение" onclick="doIt(701,1,1);">

Параметры функции: номер модуля, номер параметра и новое значение.

В самом низу страницы отображается ответ шлюза.

Данные на странице отображаются без перезагрузки, все происходит «само».

Результат


  • Теперь радиоуправляемый выключатель работает не только сам по себе, но и удобно управляется с веб-страницы.
  • Создан универсальный шлюз LAN<=>RF24, который позволяет работать с различными домашними беспроводными устройствами. Достаточно просто добавить новое устройство с таким же протоколом радиообмена — никаких изменений в коде шлюза производить не требуется.
  • Настроено постоянное логирование значений сенсоров для пост-обработки (например, для анализа или построения красивых графиков).

Дальнейшее развитие проекта


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

К примеру: длинное нажатие на кнопку включения/выключения освещения нашего радиоуправляемого выключателя (напомню, при этом один из параметров модуля устанавливается в «1») будет приводить к запуску скрипта, который выключит все включенные источники света (естественно, они тоже должны быть оборудованы радиоуправляемыми выключателями).



На этом мой «обучающий материал» по данной теме завершен. Спасибо за внимание и извините, что «курс» так затянулся.

Спасибо Nikita_Rogatnev за помощь в подготовке материала к публикации.
Support the author
Share post

Comments 17

    0
    Очень не дурно, однако веб сервера NASа очень скоро начнет не хватать, особенно для графиков и аналитики.
      0
      про шифрованный обмен не думали?
        0
        Думаю, но пока в «вялотекущем режиме». Добавить это не слишком сложно (лишь бы хватило ресурсов МК) — можно просто шифровать перед отправкой структуру, а на принимающей стороне делать обратную процедуру.
        –1
        бренд ethernet-трансформатора доставляет
          +1
          Почему бы не сделать сразу usb nrf24l01+ устройство для сервера на базе какой-нибудь avr'ки?
            0
            +1
            У меня на USB проводе к серверу подключена связка Atmega32U4 (китайская arduino pro micro) + nrf24l01, первая имеет аппаратный USB, хотя можно было воспользоваться мостом usb-com, их в китае по полтора бакса за штуку вагонами отдают, и взять еще более простую AVR-ку.
            Залил модифицированый пример usb-cdc от Atmel, минимум правок для обмена с радио частью, платка выплевывает принятые пакеты в json в виртуальный порт.

            На серваке python скрипт выгребает строки из /dev/ttyUSB0, логгирует, запускает соотв. скрипты, в зависимости от принятых данных, (управлению вентиляцией и влажностью).

            Но видимо сейчас это тренд — wifi в каждый выключатель, linux в каждое реле )
              0
              >Но видимо сейчас это тренд — wifi в каждый выключатель, linux в каждое реле
              пару месяцев назад меня за это чуть не линчевали
                0
                Отличное решение, только бы я наверно сырые данные в виртуальный порт слал. Еще на ум приходит Raspberry Pi в качестве главного устройства, тогда вообще nrf24l01+ можно к нему прямо через GPIO прицепить, но тут уже зависит от поставленной задачи, кому-то нужны мощности посерьезней.
                  0
                  Сначала я думал в приемнике на AVR делать какой то парсинг по типам датчиков, наприме «давление», «температура», «выключатель», обрабатывать их посылки, а в порт выкидывать уже готовые ascii строки, типа

                  temprerature: +22C

                  но быстро понял что в случае добавления нового типа, скажем датчик движения, мне придется модифицировать прошивку AVR-ки.

                  Тогда я решил гнать сырые данные но в json, его легко сформировать на стороне AVR, а так же можно всегда прочитать из порта
                  cat /dev/ttyUSB0
                  
                  и убедиться что все верно, и его легко парсить на серваке. Пример с DHT22:

                  {"type":1,"uid":2,"data":[1,211,0,246,0,0]}

                  DHT22 возвращает 4 байта данных, из которых очень легко получить значения температуры и влажности на Python:

                  Humidity = (value[0]*255 + value[1])*0.1
                  if (value[2]&0x80):
                  	Temperature = (value[2]&0x7F)*255 + value[3]
                  else:
                  	Temperature = (value[2]*255 + value[3])*0.1
                  print "Climat is ", Temperature, "C, ", Humidity, "%"
                  


                  Таким образом, код на AVR не нужно менять при добавлении в систему новых датчиков, и на серваке легко работать с этими данными на том же Python.
                    0
                    В моем случае примерно то же самое: шлюз ничего не знает о сути модулей и их параметров — главное, чтобы приходила «утвержденная» структура, а что с ней будет делать тот, кто запросил — его не волнует.
                0
                В планах есть задачка сделать такой шлюз RF24<=>USB, но руки пока не дошли.
                  +1
                  Посмотрите в сторону nRF24LU1+, там как раз всё в одном. На ебее платки продаются.
                    0
                    Спасибо! Интересный чип — надо попробовать.

                    Тогда моя система может существенно упроститься: в «розетки-выключатели» поставить nRF24LЕ1, а к «большому брату» прикрутить nRF24LU1.

                    Надо будет только с «новым» МК (в составе чипов) разобраться.
                      +1
                      Заодно и простенькое шифрование сделаете вместе с «hardware AES co-processor» :) Жаль, что LE1 и LU1 пока сильно дороже «обычных» L01.
                    0
                    Обратите внимание на вариант переходника из USBasp. Там можно поправить клиентский код и реализовать сырые данные, описанные выше в комментариях. Все же 3 доллара это дешевле, чем nRF24LU1+ за 10$, где надо ещё разобраться с USB-шной частью…
                    Про nRF24LЕ1 тут уже я постарался «разжевать» и переход с ардуино+nRF24L01 на его получается не такой уж сложный.
                  0
                  Любопытно.

                  Возможно, я отстал от жизни или слишком профессионально зашоренный, но мне кажется, что такие устройства надо собирать в сеть не на Ethernet, а на чём-то типа RS-485. А вот шлюзом между ними и «гражданской сетью» будет контроллер, который ими управляет.
                    +1
                    Тут не сеть на Ethernet, он тут для подключения к «большому брату» нужен.

                    Хотя подключение к «большому брату» nrf24l01+ реализовывал через usb используя готовую плату USBasp -тоже неплохо выходит…

                  Only users with full accounts can post comments. Log in, please.