Pull to refresh

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

Reading time 7 min
Views 55K
Собственно, выключатель спроектирован, произведен, протестирован, запрограммирован, установлен и вполне себе самостоятельно работает.

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

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


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

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

Железо


Для дальнейшей работы я буду использовать:
  • Плата 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 за помощь в подготовке материала к публикации.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+13
Comments 17
Comments Comments 17

Articles