Перейти к публикации
iT4iT.CLUB

Kitsum

Пользователи
  • Публикации

    424
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    234

Все публикации пользователя Kitsum

  1. Доброе время суток. Сегодня пойдет речь о том, как переправлять данные с MQTT брокера в базу данных MySQL. Транспортировать будем как сами адреса, так и значения всех топиков, на которые оформлена подписка. Данную задачу нельзя назвать распространенной, но все же, она имеет место быть и может пригодиться в том случае, если данные востребованы в системах не способных работать с MQTT протоколом самостоятельно или брокер находится в изолированной системе, а данные востребованы, например, в GUI за её приделами. Для осуществления задуманного нам потребуется самостоятельный процесс, который сыграет роль транспортного узла между MQTT брокером и базой данный MySQL. А значит, его придется где-то держать. В моем случае, это сервер под управлением операционной системой Linux Ubuntu 16.04.3 и дальнейшее описание будет под неё, но для других ОС действия аналогичные. Сам демон будет написан на Python и для его работы нам потребуется: python3 python-pip python-dev libmysqlclient-dev библиотека paho-mqtt для python https://pypi.python.org/pypi/paho-mqtt библиотека mysqlclient для python https://github.com/PyMySQL/mysqlclient-python Но начнем мы, в первую очередь, с подготовки базы данных. На плечи MySQL ляжет не только хранение, но и частичная обработка данных. Для этих целей нам потребуется отдельная база, хранимая процедура и функция с реализацией небольшой логики, и пользователь с ограниченными правами под чьим именем мы будем обращаться к ним. В конце поста, кроме самого демона, будет опубликован .sql файл, который достаточно просто импортировать, например с помощью стандартный средств базы данных mysql -uroot -p Вводим пароль администратора MySQL и импортируем наш .sql файл, но прежде, дочитайте статью до конца, возможно, Вы захотите внести свои изменения. mysql> source /media/mqttMySqlClient.sql После этого будет получен следующий результат: Создана база (схема) с именем mqtt Пользователь с именем mqtt-agent и паролем p@$$w0rd имеющий возможность подключаться с внешних адресов Пользователю будут назначены ограниченные права (только EXECUTE) в этой схеме Будет добавлена процедура update_topic, на которую ляжет задача добавления и обновления данных Будет добавлена функция get_topic для упрощения поиска данных На тот случай, если Вы захотите внести изменения или создать все ручками, рассмотрим содержимое sql файла. Если схема mqtt не существует, она будет создана CREATE DATABASE IF NOT EXISTS `mqtt`; Аналогичным образом будет создан пользователь mqtt-agent. Если необходимо конкретизировать, с какого адреса будет производиться подключение под этим пользователем, то замените % на доменное имя или ip адрес хоста. Если планируется использовать демона на том же сервере где установлен MySQL, замените % на localhost. Также разрешено не более 2 активных подключений, измените это значение на необходимое Вам. CREATE USER IF NOT EXISTS 'mqtt-agent'@'%' IDENTIFIED BY 'p@$$w0rd' WITH MAX_USER_CONNECTIONS 2; Пользователю будут выставлены ограниченные права. Ему будет разрешено пользоваться только хранимыми процедурами и функциями. Никакие самостоятельные запросы выполнять нельзя. GRANT EXECUTE ON `mqtt`.* TO 'mqtt-agent'@'%'; Переходим к работе с самой схемой USE `mqtt`; Будет создана таблица topics со следующей структурой. md5 - содержит уникальный одноименный хеш полученный из полного адреса топика. Именно по этому ключу и будет производиться поиск данных. Почему именно по нему, а не по самому имени? Дело в том, что md5 хеш имеет фиксированную, заранее известную, длину, что нельзя сказать о имени топика. Именно это ограничение не позволит сделать имя топика первичным ключом и явно идентифицировать данные в таблице. time - содержит UNIX время добавления/обновления данных по конкретному топику (по умолчанию GMT+0) topic - адрес топика. В контексте, упомянутого ранее, поля md5, не несет для нас никакой смысловой нагрузки. value - данные опубликованные в топике. DROP TABLE IF EXISTS `topics`; CREATE TABLE `topics` ( `md5` varchar(32) NOT NULL, `time` bigint(20) DEFAULT NULL, `topic` text, `value` text, PRIMARY KEY (`md5`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Теперь необходимо создать хранимые процедуры и функции, но сделать это будет невозможно из-за присутствие в их синтаксисе разделителя совпадающего с концом данных в sql запросе - ";" Чтобы избежать этот неловкий момент, изменяем разделить на произвольный. DELIMITER $$ Создает процедуру update_topic. Она принимает в качестве входных параметров два значения, адрес топика и опубликованные данные. Оба параметра являются текстовыми. Процедура, вычисляет md5 хеш из адреса топика и уже по нему производит поиск записи в таблице. Если запись не будет найдена, она будет создана, в противном случае данные в поле value будут обновлены. Данная процедура должна ускорить работу демона и избавить его от задержек, которые были бы неминуемы при выполнении этих же запросов на стороне клиента. DROP PROCEDURE IF EXISTS `update_topic`$$ CREATE DEFINER=CURRENT_USER() PROCEDURE `update_topic`(topic text, value text) BEGIN declare nMD5 varchar(32) default md5(topic); declare NUM bit; declare uTime bigint(20) default UNIX_TIMESTAMP(); SELECT COUNT(t.md5) INTO NUM FROM topics t WHERE t.md5 = nMD5; if NUM <> 1 then INSERT INTO topics VALUES(nMD5, uTime, topic, value); else UPDATE topics t SET t.time = uTime, t.value = value WHERE t.md5 = nMD5; end if; END$$ Также будет добавлена функция get_topic. Она необходима для запроса данных от имени созданного ранее пользователя и ограничений, наложенных на него. Функция принимает адрес топика в текстовом виде, производит вычисление md5 хеша и основываясь на его совпадении с имеющимися записями выводит значение поля value искомого топика. DROP FUNCTION IF EXISTS `get_topic`$$ CREATE DEFINER=CURRENT_USER() FUNCTION `get_topic` (topic text) RETURNS text BEGIN declare hMD5 varchar(32) default md5(topic); declare topicValue text; SELECT t.value INTO topicValue FROM topics t WHERE t.md5 = hMD5; RETURN topicValue; END$$ И в завершении всего, будет восстановлено стандартное значение разделителя. DELIMITER ; На этом разбор sql файла можно считать законченным. Он не содержит каких-либо сложным манипуляций и должен быть понятен. Все эти операции можно выполнить руками, но я совету воспользоваться импортом, как и было описано ранее. Переходим к демону В первую очередь устанавливаем необходимые пакеты. sudo apt-get install python3 python-pip python-dev libmysqlclient-dev Устанавливаем недостающие библиотеки для Python pip install paho-mqtt mysqlclient Добавим пользователя из-под которого будет запускаться демон sudo useradd --shell /usr/sbin/nologin --system mqtt-agent Выставляем все необходимые права (каталог /media как пример) sudo chown mqtt-agent:mqtt-agent /media/mqttMySqlClient.py sudo chmod 0700 /media/mqttMySqlClient.py Добавляем демона в автозагрузку через планировщик задач и от имени созданного пользователя sudo crontab -u mqtt-agent -e Добавляем в конец следующую запись @reboot /media/mqttMySqlClient.py start Запускаем демона от имени все того же пользователя sudo -u mqtt-agent /media/mqttMySqlClient.py start Это основное, что требуется сделать на сервере для организации работы демона. Переходим к разбору настроек программы Т.к изначально за основу была взята концепция другого демона из ветки Zabbix, то конфигурация перекочевала оттуда и аналогично разбита на несколько секций. """ Настройки MQTT """ mqtt_server = "mqtt.it4it.club" mqtt_port = 1883 mqtt_login = "" mqtt_password = "" mqtt_client_id = "mqttMySqlClient" Настройки подключения к брокеру. Все должно быть интуитивно понятным. Помните, что при совпадении идентификаторов клиентов, они начнут конкурировать за подключение и по очереди терять связь. Не допускайте их совпадений. """ Список топиков для подписки """ subscribe = { '$SYS/#', '#', } Список топиков для подписки указывается через запятую и в кавычках. """ Настройки MySQL """ mysql_host = "127.0.0.1" mysql_port = 3306 mysql_user = "mqtt-agent" mysql_passwd = "p@$$w0rd" mysql_schema = "mqtt" mysql_log_file = "/var/log/mqttMySqlClient.log" Настройки подключения с MySQL серверу также не должны вызывать вопросов. """ Настройки общие """ pid_file = "/tmp/mqttMySqlClient.pid" Последний параметр указывает на размещение .pid файла демона. Команды управления классические start - запуск в режиме демона stop - остановка в режиме демона restart - перезапуск в режиме демона window - запуск в оконном режиме, также позволяет запускать процесс в операционных системах Windows После запуска, демон пытается установить связь с MQTT брокером и пока это не произойдет, связь с MySQL сервером устанавливаться не будет. Если во время работы, связь с брокером будет потеряна то в принудительном порядке, будет разорвано соединение с базой данных. Таким образом, по активным сессиям MySQL сервера можно судить о наличии связи у демона с брокером. Во время простоя, а в нашем случае, это отсутствие потока данных от брокера, для проверки связи с сервером базы данных будет использована процедура эмуляции ping для MySQL сервера. Она представляет из себя простую арифметическую задачу на сложение не приводящей к работе с данными в базе. Операция выполняется крайне быстро и её удачное выполнение сигнализирует клиенту о наличии связи с базой, а базе об активности клиента. В связи с этим, периодическая активность клиента при отсутствие данных от брокера, является показателем нормальной работы. И на последок Если вы хотите произвести выборку из таблицы под именем пользователя используемым по умолчанию и с его ограничениями. Необходимо воспользоваться функцией get_topic с указанием полного адреса, интересующего топика. select mqtt.get_topic('$SYS/broker/version'); В ответ мы получим +---------------------------------------+ | mqtt.get_topic('$SYS/broker/version') | +---------------------------------------+ | mosquitto version 1.4.8 | +---------------------------------------+ 1 row in set (0,00 sec) На этом пока все. Файлы проекта: PS: Это тестовая версия демона и возможно она будет претерпевать некоторые изменения.
  2. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @zloydimo4ka доброе время суток. Осмелюсь предложить Вам отказаться от связки BMP + DHT и заказать BME280. Данный датчик позволит получить информацию как о давление с температурой, так и о влажности. Я уже упоминал о нем ранее, а на днях получил модуль с таким сенсором и обязательно поделюсь информацией о нем.
  3. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Alex_DIY горизонтально, датчиком вниз. Такая установка не обусловлена ничем кроме моих собственных предпочтений и мыслей. Не знаю на сколько это правильно. Сама будка, если память не подводит, выполнена тоже из АБС пластика белого цвета, но, как и ожидалось, со временем он пожелтел. Никаких конструктивных особенностей я не вносил, хоть и были мысли окрасить её серебрянкой, как предлагал исполнитель заказа на печать этой самой будки. Возможно сделаю это весной, ведь по сути, это тестовый вариант метеостанции и можно смело экспериментировать.
  4. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Alex_DIY это очень интересное наблюдение. В библиотеке от LowPowerLab имеется функция setHeater для включения обогрева датчика. void SI7021::setHeater(bool on) { byte userbyte; if (on) { userbyte = 0x3E; } else { userbyte = 0x3A; } byte userwrite[] = {USER1_WRITE, userbyte}; _writeReg(userwrite, sizeof userwrite); } Если имеется возможность провести эксперимент, было бы великолепно узнать о результатах, но это не при автономном питании, как Вы и указали. Возможно есть еще что-то, что осталось не замеченным.
  5. @Slava хорошо, давайте отталкиваться от этой информации. Но хотелось бы уточнить, скорость Serial монитор совпадала со скоростью выставленной в контроллере? По умолчанию 115200. Что бросилось в глаза: а также то, что Т.к Вы не увидели никакой информации, в том числе и приветствие, я делаю вывод, что функция Setup не была завершена, а значит, до функции loop дело не дошло. Это объясняет, почему не отрабатывает кнопка, как и все остальные функции замка. Также, учитывая, что во втором варианте, при старте с сетью и последующем отключении от неё, кнопка продолжала работать, можно сделать вывод, что программа вышла из функции Setup и выполняет loop. Могу предположить, что при старте контроллер пытается инициализировать сетевое соединение и т.к ожидаются настройки от DHCP сервера, которых мы не получим из-за отсутствия связи, то на ожидание уходит какое-то время. И вероятнее всего, оно превышает установленный придел времени для сторожевого таймера, что приводит к перезапуску контроллера. И данный цикл повторяется до появления сети. Думаю, это можно проверить, запустив контроллер без сети, и открыв Serial монитор, подключить сеть обратно. Предлагаю попробовать уйти от DHCP сервера и прописать всю необходимую информацию самостоятельно. Но перед этим добавим тестовый вывод после инициализации Serial. Найдите Serial.begin(115200); while (!Serial); Добавьте тестовый вывод Serial.begin(115200); while (!Serial); Serial.println(F("Start")); Далее пропишем все данные для сети самостоятельно. Найдите в функции Setup строку Ethernet.begin(mac); И замените её на следующий блок IPAddress ip(192, 168, 0, 100); IPAddress subnet(255, 255, 255, 0); IPAddress gateway(192, 168, 0, 1); IPAddress dns(192, 168, 0, 1); Ethernet.begin(mac, ip, dns, gateway, subnet); Укажите настройки, соответствующие Вашей сети.
  6. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Alex_DIY доброе время суток. Датчиком SI7021 я пользовался с момента тестовой обкатки, примерно с середины декабря 2016, и вплоть до июля 2017. Вот лог с Народного мониторинга. На имя датчика не обращайте внимание, данные до первых чисел июля, фактически принадлежат SI7021, а все, что дальше по времени уже HDC1080. Максимальное значение 99.64% относительной влажности были зафиксированы 22 февраля 2017. Сравнил с архивом погоды на это число в моем регионе, это 90%-95% влажности, что примерно совпадает с реальностью. Естественно не учитываем погрешности в связи с расположением самой будки с датчиками. Также хочу отметить, что сам датчик очень хорош, но имеет полимерную пленку над входным отверстием. Я эту пленку повредил и данным с датчика нельзя было доверять т.к калибровка на заводе изготовителя учитывает её наличие. После этого инцидента, датчик был заменен на аналогичный по характеристикам (возможность использования датчика на улице) - HDC1080. Об этом было упоминание в этой ветке Возможно Вы имеете схожую проблему. Что касаемо HDC1080. Датчик также очень хорошо себя показал, начиная с июля 2017 и по сей день. На момент написания поста - 14 ноября 2017, уже около недели стоит очень сырая погода и идут дожди. Относительная влажность в максимуме 99%, вот график с самой метеостанции за последние 24 часа. Вывод, который я могу сделать - оба датчика очень хороши, но SI7021 требует бережного обращения при транспортировке и установке. Крайне зависим от состояния полимерной пленке на лицевой стороне сенсора. Оба датчика были установлены на одной высоте и в одном положении в будке (на середине и горизонтально). Но опять обращаю внимание, что SI7021 я повредил сам, что и привело к искажению показаний.
  7. @Slava Изначально не предусматривалось использование локальных ключей, только внешняя база. Но и под внешней базой подразумевается MySQL сервер расположенный в пределах локальной сети. Использование внешнего сервера в интернете не безопасно. Но об этом будет другой проект с замком. Что по поводу кнопки. Я буду рассуждать в контексте оригинальной программы. Реализация механизма открытия с кнопки очень проста и работает независимо от сети. // Открытие двери с кнопки key_open.update(); if(!key_open.read() and openTimer == 0 and !mode) { if(modeLock or (!modeLock and digitalRead(PIN_RELAY))) { openTimer = millis()/1000; digitalWrite(PIN_RELAY, LOW); Serial.println(F("The door opened from the inside\n")); squeaker(5, 3200, 100, 300); } delay(2000); } На работоспособность кнопки влияет смена режима работы замка - режим программирования. В этом состоянии замок открыт и ожидает новые ключи для записи в eeprom. Но я не могу утверждать, что не происходит никакой нештатной ситуации т.к все мы люди и все ошибаемся. В данный момент у меня нет возможности собрать схему замка и проверить все самостоятельно. Но попробовать разобраться в ситуации можно. В каком режиме работает закрытие замка (авто/ручной)? Используете ли Вы сторожевой таймер? Внесите следующие изменение в работу кнопки Воссоздайте ситуацию и опишите полный порядок Ваших действий. Что прилетает в Serial монитор?
  8. Kitsum

    Обучаем Zabbix работать с MQTT протоколом

    Маленькое дополнение к последнему посту с мониторингом MQTT брокера Mosquitto в реальном времени. В шаблоне не было отображено как определить отсутствие данных. В принципе, это не такое серьезное упущение т.к целью было получение самих данных, но все же. Т.к мы использовали тип элемента данных "Zabbix траппер", то самым явным индикатором будет отсутствие входящих данных по одному из элементов в течении некоторого промежутка времени. При этом, мы должны быть уверены, что данные отправляются нам систематически с фиксированным интервалом времени. Для этих целей можно взять, как опорный, топик из раздела $SYS, например, время работы сервера $SYS/broker/uptime По умолчанию раздел $SYS обновляется каждые 10 секунд, но это значение может быть иным или обновления могут быть отключены полностью. Проверьте Ваше значение в конфигурационном файле /etc/mosquitto/mosquitto.conf sys_interval 10 Далее переходим в Zabbix и создаем новый триггер. Сам Zabbix предоставляет функция nodata(), работающую совместно с траппами и возвращающую: 1 - если не было получено данных за указанный промежуток времени в секундах. Период не может быть меньше 30 секунд. 0 - наоборот Таким образом, наша проверка будет выглядеть следующим образом {broker:topic[uptime].nodata(60)}=1 или, если используются полные имена, так {broker:topic[$SYS/broker/uptime].nodata(60)}=1 где broker - имя узла сети в Zabbix. В итоге, мы получаем взведенный триггер, если данные о времени работе брокера не будут обновлены в течении одной минуты.
  9. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @LogOFF хорошо, с гаражом мы разберемся. Но в любом случае, вся силовая часть ляжет на Ваши плечи и именно Вам решать, как управлять вентиляторами и нагревателями. Что касаемо метеостанции на улице, то в принципе все уже готово, плюс ко всему будет описание, как перевести все на единый датчик BME280. А значит уменьшится бюджет всего проекта. Пока ждем датчики. Теплицы тоже подпадают под этот пункт. А вот общение с центральным сервером это уже интересный вопрос связанный непосредственно с коммуникацией. Могу предположить, что имеются три разных объекта: Квартира с центральным сервером Удаленный участок с домиком и теплицей Гараж Таким образом на всех трех объектах должна быть инфраструктура, позволяющая выходить в интернет и если он есть, то не вижу никаких трудностей с организацией передачи данных. В противном случае, придется запланировать дополнительные расходы.
  10. Kitsum

    Обучаем Zabbix работать с MQTT протоколом

    Друзья, в очередной раз приветствую Вас. Сегодня мы доведем до ума нашу идею с дружбой Zabbix и MQTT (Message Queue Telemetry Transport) протокола. Все описанные ранее варианты также жизнеспособны, но хотелось чего-то большего и в первую очередь, избавиться от задержки, из-за которой клиентам приходилось рассылать сообщения с параметром "-r, --retain". То есть была такая ситуация, что Zabbix сервер получал сообщения не тогда, когда они по факту приходили брокеру, а через какое-то время, равное интервалу между запросами к брокеру через вызов /usr/bin/mosquitto_sub. И именно из-за такой логики работы, клиентам приходилось выставлять флаг "сохранить" при отправке сообщения. Ну и пусть, скажете Вы, подождем, но как всегда есть нюансы: Сервер получает последнее сообщение в топике, а значит, в промежуток между их сбором, данные могут обновиться несколько раз, и мы потеряем часть информации. Это может быть актуально в некоторых случаях, например, если требуется контролировать состояние двери в серверной комнате (открыта/закрыта) ну или в других более серьезных задачах. Если требуется оперативно поднять соответствующий триггер, особенно если на него опирается логика других триггеров. Просто хочется поддержки MQTT протокола в реальном времени. Это действительно важный пункт и если предыдущие можно проигнорировать, то этот ни в коем случае. Долой слоупока, обучаем Zabbix работать с MQTT протоколом в реальном времени! Стоит понимать, что сам по себе Zabbix сервер не способен работать с данным протоколом и именно из-за этого нам требуются различные посредники. Раньше мы обращались к ним самостоятельно через Zabbix агента или внешнюю проверку (скрипт) и сообщали, что и как мы хотим получить. Теперь мы пополним арсенал сервера внешним демоном/сервисом/службой, нужное подчеркнуть. Именно на этот самостоятельный процесс ляжет задача по поддержанию постоянного соединения с MQTT брокером, и при появлении нового сообщения, он будет передавать его серверу через Zabbix траппер. Ну, что же, на словах все просто, как это будет на деле. При написании демона я использовал язык Python в силу его популярности, простоты и доступности некоторого набора готовых библиотек. Но это моя первая в жизни программа на Python и, возможно, Вы захотите её доработать. Также на моем сервере установлена операционная система Linux Ubuntu 18.04 и дальнейшее описание будет именно под неё, но я уверен, что Вам не составит труда установить следующие пакеты под любой ОС: python3 python3-pip библиотека paho-mqtt для python https://pypi.python.org/pypi/paho-mqtt zabbix-sender Получить список установленных пакетов, связанных с python и Zabbix, можно так dpkg -l | grep -E "python|zabbix" Устанавливаем пакеты (уже установленные можно выкинуть из списка) sudo apt install python3 python3-pip zabbix-sender Подгружаем библиотеку pip3 install paho-mqtt Далее нам понадобится сама программа, скачать её можно в конце этого поста Копируем её в любое удобное Вам место, но, чтобы пользователь, от которого будет запускаться программа, имел туда доступ, например, /media/zabbixMqttClient.py и выставляем права ограниченного пользователя, пусть это будет пользователь Zabbix sudo chown zabbix:zabbix /media/zabbixMqttClient.py sudo chmod 0700 /media/zabbixMqttClient.py Далее необходимо добавить программу в автозагрузку. Сделаем это через планировщика задач crontab и опять же от имени пользователя zabbix sudo crontab -u zabbix -e Добавляем следующую запись @reboot /media/zabbixMqttClient.py start Запускаем демона от имени пользователя zabbix sudo -u zabbix /media/zabbixMqttClient.py start Далее переходим к разбору настроек программы Они разбиты на несколько секций """ Настройки MQTT """ mqtt_server = "mqtt.it4it.club" mqtt_port = 1883 mqtt_login = "" mqtt_password = "" mqtt_client_id = "zabbixServer" mqtt_short_names = True Все параметры должны быть интуитивно понятны, кроме mqtt_short_name. Данный параметр заставит программу производить разбор топика с целью поиска в нем имени хоста, на который будут отсылаться сообщения Zabbix серверу. Если параметр будет выставлен в False, то в качестве параметра ключа будет использовано полное имя топика. Мы рассмотрим этот механизм подробнее ниже, при разборе механизма подписки на интересующие нас топики. Также по умолчанию мы используем TCP соединение т.к поддержка webSocket не предусматривалась, но программу можно легко доработать. """ Настройки Zabbix """ zabbix_server = "127.0.0.1" zabbix_port = 10051 zabbix_sender = "/usr/bin/zabbix_sender" #zabbix_sender = "C:\\ZabbixAgent\\bin\\win64\\zabbix_sender.exe" С настройками подключения к Zabbix серверу также все просто. Подключаться мы будем с помощью утилиты zabbix-sender, поэтому необходимо указать полный путь до неё. Скрипт также можно запускать из-под Windows, но только в оконном режиме и с параметром window. При использовании параметра start, получите шлак ошибок т.к она отвечает за запуск программы в качестве демона в Unix подобных системах. """ Настройки общие """ pid_file = "/tmp/zabbixMqttClient.pid" В общих настройках описано только расположении pid файла. Можно оставить без изменений. """ Список топиков для подписки и идентификаторы Zabbix хостов на которые требуется их переслать """ subscribe = { '$SYS/#': 'broker', 'kitsum/espWeatherStation/#': 2, 'log/+/#': 2, } Ну вот мы и добрались до самого главного - оформление подписки на топики. Я долго ломал голову над тем, как угодить всем и организовать переправку любых сообщений на те Zabbix хосты, для которых они предназначены. И сделать это без внесения транспортной информации в само тело сообщение, лично я считаю это плохой практикой, но судя по сообщениям, за которыми я наблюдал на некоторых популярных брокерах, люди считают это хорошей идеей. Просто какое-то безумие... Думаю, что Арлен Ниппер и Станфорд-Кларком, разработчики протокола, это не одобрили, хотя сам протокол не несет никаких ограничений на передаваемую информацию. Но давайте отталкиваться от идеологии протокола. Сам по себе топик представляет: стандартизированный, упорядоченный и интуитивно понятный набор информации, а также является адресом получателя. То есть, если адрес топика будет соответствовать адресу проживания реального человека, то вид его будет похож на что-то подобное <страна>/<штат или область>/<город>/<район или улица>/<дом>/<квартира>/<имя получателя> Думаю, что Вы согласитесь со мной, ведь нет смысла писать только половину адреса, а оставшуюся часть вкладывать в сообщение. Проще, после имени получателя, дописать адрес, но уже в контексте той нагрузки, которую несет сообщение. <имя получателя>/<помещение>/<источник информации> <имя получателя>/<группа>/<подгруппа>/<источник информации> ... building/serverRoom/door/1/state ... building/serverRoom/zone1/smokeDetectors/1 building/serverRoom/zone2/smokeDetectors/1 building/serverRoom/zone3/smokeDetectors/5 ... building/serverRoom/+/smokeDetectors/# Все понятно и даже задумываться не нужно, о чем идет речь. Также стоит обратиться к принципу работы zabbix-sender, той самой утилиты, которая и будет финальной в цепочке передачи. Вот еще немного подробностей про неё. Как видим, кроме параметров подключения мы обязаны передать: Имя узла сети, которому адресовано сообщение Ключ элемента данных Сами данные И если с последним параметром все понятно, то два предыдущих я предлагаю брать из самого топика, а также не брезговать принудительным указанием имени узла сети для конкретного топика. Синтаксис правил следующий 'адрес топика в формате mqtt': 'имя узла сети в zabbix', В таком случае, все сообщения, подпадающие под установленное правило будут адресованы заранее указанному узлу сети. Но что делать, если имя узла неизвестно, и мы ожидаем данные от любых источников с адресом топика подпадающим под установленную маску? В таком случае мы можем указать порядковый номер субтопика, который содержит это самое имя. Указывать его нужно в виде целого числа, как положительного, так и отрицательного. В зависимости от знака, отсчет будет начинаться с начала конца адреса топика. Saint Petersburg/office1/temperature Saint Petersburg/office213/Wi-Fi/signalStrength Эти два топика могут подпадать под правило 'Saint Petersburg/+/#': 2, Таким образом, второй элемент с начала топика выпадает на субтопик "+" и, следовательно, имя узла сети будет соответствовать номеру офиса: office1 и officce213. Тоже самое можно делать и в обратном порядке, то есть с отрицательными значениями номера субтопика, но при этом, Вы должны быть уверены в длине адреса топика, а точнее, в количестве его субтопиков. Moscow/Odintsovo/+/temperature Этот шаблон может подпадать под два разных правила 'Moscow/Odintsovo/+/temperature': 3, 'Moscow/Odintsovo/+/temperature': -2, Что в итоге будут опять ссылаться на субтопик "+" и соответствовать его любому значению. И последнее правило 'NewYorkCity/+/gasSensors/+/#': 4, Которое соответствует второму значению "+" (четвертый субтопик), что совпадет с разными топиками и может не соответствовать нашим ожидания т.к мы получим два разных значения ссылающиеся на один узел сети "sensor 3". NewYorkCity/office16/gasSensors/sensor3/value/current NewYorkCity/office85/gasSensors/sensor3/value/current В таком случае мы должны быть уверены, что нумерация датчиков уникальна для всего набора зданий или на стороне Zabbix сервера мы ожидаем увидеть полный адрес топика, а значит, для второго варианта развития событий переменная mqtt_short_names должна быть выставлена в Flase. Таким образом мы можем кардинально поменять логику работы при построении ключей и выглядеть они будут следующим образом. Но отправлены на один узел - "sensor3" mqtt_short_names = True # отправлено хосту sensor3 (значения перезаписывают друг друга) topic[value/current] topic[value/current] mqtt_short_names = False # отправлено хосту sensor3 (ключи уникальны) topic[NewYorkCity/office16/gasSensors/sensor3/value/current] topic[NewYorkCity/office85/gasSensors/sensor3/value/current] Или исправить правило на следующее 'New York City/+/gas sensors/+/#': 2, И при mqtt_short_names = True, данные будут переданы на разные узлы сети с именами office16 и office85 соответственно, но при этом ключи будут выглядеть одинаково и не будут пересекаться. topic[gasSensors/sensor3/value/current] # отправлено хосту office16 topic[gasSensors/sensor3/value/current] # отправлено хосту office85 Возможно в теории все не так явно, но на практике это очень удобно. И важный момент - все описанные Вами правила будут рассматриваться аналогично правилам любого межсетевого экрана (firewall). Вышестоящее правило в списке имеет приоритет над нижестоящим. И если топик попал под действие одного из шаблонов, то все нижестоящие правила будут проигнорированы. Поэтому будьте внимательны. Вот пример того явной ошибки построения правил. # ошибочная конфигурация правил subscribe = { '#': 'rubbish', '$SYS/#': 'broker', 'log/+/#': 2, } Как мы видим, под первое (жадное) правило будут подпадать абсолютно все топики и данные будет получать только узел с именем rubbidh. Два оставшихся правила никогда не будут выполнены т.к уже имеют более низкий логический приоритет. Чтобы исправить данную ситуацию, опустим самое "жадное" правило в конец списка. # правильная конфигурация правил subscribe = { '$SYS/#': 'broker', 'log/+/#': 2, '#': 'rubbish' } Что мы получим теперь. Правила не пересекаются Все данные с системного топика $SYS (данные по работе брокера) будут переданы Zabbix и адресованы узлу broker Все данные с топика log (условный раздел для логов) будут переданы узлам c именами соответствующими второму субтопику Все остальное (вообще все) будет передано узлу rubbish Также хочу отметить, что если, при использовании коротких имен топиков (mqtt_short_names = True), имя имя узла сети не будет найдено в адресе топика, то в ключе будет использован полный адрес топика, как будто бы короткие имена топика не используются вовсе (mqtt_short_names = False). mqtt_short_name = True subscribe = { 'serverRoom/rack2/zone4/temperature': 'zabbixServer', 'serverRoom/rack3/zone4/temperature': 'rack3', } В первом правиле будет использован полный путь топика т.к имя узла не будет найдено, а вот во втором правиле совпадение найдено будет и имя топика будет преобразовано в его короткий вариант. topic[serverRoom/rack2/zone4/temperature] # отправлено узлу zabbixServer topic[zone4/temperature] # отправлено узлу rack3 Короткие имена очень удобны в том случае, если требуется избавиться от лишних элементов в ключе. Например, если данные явно адресованы и сам узел сети несет информацию, связанную непосредственно только с ним. Это может быть, как сам брокер, так и набор различных сенсоров, определенных на Zabbix сервере как самостоятельные единицы. Рассмотрим это на примере шаблона для мониторинга MQTT брокера Mosquitto. Саму процедуру установки и настройки брокера я рассматривать не буду т.к предполагается, что брокер у Вас уже имеется. Мониторинг MQTT брокера Mosquitto В первую очередь нам понадобится сам шаблон, найти его можно в конце статьи. Импортируем его на Zabbix сервере, создаем узел сети, описывающий наш брокер и подключаем шаблон. Переходим в раздел "Мониторинг -> Последние" данные, выбираем наш узел и смотрим, что прилетает нам. Если Вы все сделали правильно, то увидите все данные по работе брокера. И как мы можем наблюдать, все ключи имеют короткие имена. Чтобы создать собственные элементы данных необходимо: Хость на, который отправляются данные, существовал на Zabbix сервере. Обращение идет по полю "Имя узла сети" Элемент данных был с типом "Zabbix траппер" Адрес обязан быть заключен в ключ topic[], например, topic[bytes/received] На этом все. Файлы проекта PS: Приятного использования и обязательно делитесь своими наработками и идеями.
  11. Kitsum

    Метеостанция на ESP8266 от it4it.club

    Доброе время суток @LogOFF Думаю, что никаких трудностей быть не должно. Я бы взял за основу два датчика BME280 т.к судя по общедоступной информации он имеет вывод SDO, что позволяет изменять его адрес на I2C шине (0x76/0x77) и работать в паре с аналогичным сенсором. Данный датчик я себе уже заказал, к сожалению, только один, но раз есть потребность, значит закажу еще несколько. https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf Далее остается дело за малым - по очереди опрашивать датчики, производить необходимые вычисления и при достижении установленных Вами границ подать на порт 1 или 0, в зависимости от того, как и чем, Вы будите управлять нагрузкой. Возможно будут сложности в компоновке данных на графике, если он вообще востребован, но все это решаемо.
  12. Kitsum

    Обучаем Zabbix работать с MQTT протоколом

    Как повторить устройство, используемое для мониторинга температуры и передающее показания на zabbix через mqtt протокол, можно прочитать тут
  13. Привет друзья. В данной теме пойдет речь о конфигурации микроконтроллера через UART (Universal Asynchronous Receiver-Transmitter) интерфейс. А рассмотрим мы это на примере MQTT логгера. В данном случае, это будет логгер температуры. Мне это устройство потребовалось на работе, даже не мне, а моим коллегам, и оно действительно работает и приносит огромную пользу т.к контроль температуры производится совместно с отличной, на мой взгляд, системой мониторинга Zabbix с оперативными оповещениями, построением графиков, блэк-джеком и... Подробнее о дружбе Arduino и Zabbix можно почитать тут Но как всегда, есть нюансы. А заключаются они в том, что в будущем, обслуживать армию мелких контроллеров придется людям, которые заняты своими задачами и им попросту некогда изучать Arduino, не говоря уже о серьезных альтернативах, разбираться в том, как прописать нужные значения переменных в программу и загрузить её в микроконтроллер. Все настройки необходимо производить быстро, с явным указанием изменяемого параметра и его значения. Ровно также, как это делается с любым промышленным оборудованием. И тут на помощь приходит UART Микросхема UART to USB имеется в большинстве плат семейства Arduino, а там, где её нет, обычно выведены соответствующие "пины". И все это очень облегчает жизнь т.к позволяет общаться с контроллером, просто подключив его к компьютеру напрямую или через переходник, благо их везде навалом, да и стоят они как пачка семечек. Остается только запустить любой терминал, который умеет доставлять в конец строки символ "перевод строки", что известен в народе как "\n", а в ASCII таблице имеет номер 0A. Кстати, в Serial мониторе Arduino IDE выставить символ конца строки можно так Ну а дальше только, что и остается, как общаться с устройством на той стороне. И тут мы переходим к основному алгоритму программы. Но перед этим хочу отметить, и это ВАЖНО, что за любое упрощение жизни, всякие красивости и прочее, приходиться платить, и цена довольно высока! В данном случае, это ОЗУ микроконтроллера. Поэтому не раскатываем губы, а если очень хочется, то берем следующий по характеристикам микроконтроллер. А начинать мы будем с ATmega328P, что известен в народе как Arduino UNO, Arduino Nano, IBoard v1.1 и т.д по списку. Заканчивать Вы можете чем угодно, хоть ATmega2560, ESP8266 или ESP32. В противном случае, производим оптимизацию кода, отказываемся от громоздких библиотек, или вообще, от Arduino IDE. Что мы хотим получить Вся конфигурация микроконтроллера должна храниться в энергонезависимой памяти (ПЗУ) известной нам как EEPROMM. Если в ПЗУ конфигурация отсутствует, необходимо иметь резервный план. И им станет сброс конфигурации на настройки по умолчанию. Это поведение знакомо всем, особенно по домашним дешевым маршрутизаторам, а значит, интуитивно понятно. Выводить справку при начале общения пользователя и устройства, на мой взгляд, как манеры высшего общества. Контроллер должен представляться и сообщать всю необходимую информацию о себе и о том, как с ним вести диалог. Все команды должны быть просты и иметь не двусмысленное значение. И конечно, мы должны иметь возможность просмотра текущего состояния датчиков или процессов, которыми занимается устройство в свободное от общения с нами время. Как сохранять конфигурацию в EEPROM Пожалуй, стоит начать с того, как сохранить конфигурацию микроконтроллера в энергонезависимую память. Для этих целей, в стандартный набор инструментов Arduino IDE входит библиотека для работы с EEPROM. #include <EEPROM.h> На данный момент нас интересуют две функции, это чтение и запись EEPROM.get(address, variable); EEPROM.put(address, variable); Обе принимают два параметра: Адрес, начиная с которого будет произведено чтение или запись данных в память Переменная чье содержимое надо сохранить или в которую нужно из памяти прочитать Особенность работы этих функция заключается в том, что в зависимости от типа переданной им переменной во втором параметре, будет произведено чтение или запись ровно того количества данных которое соответствует размеру типа этой самой переменной. На простом языке это означает, что если переменная variable будет иметь типа byte, то и работать мы будем с объемом памяти в 1 байт. И тоже самое произойдет с абсолютно любым типом данных пока мы не упремся в размеры самого EEPROM или ОЗУ микроконтролера. Из этого всего следует, что мы можем создать свой собственный тип данных, разместить в нем необходимую нам информацию и всего лишь двумя функциями помещать его в память и извлекать обратно. И в этом нам поможет пользовательский составной тип - структура (struct). Данный тип позволяет объединить в себе различные типы данных, упорядочить их и присвоить им понятные имена. Это общий пример для большего понимания, как объединить несколько типов данных в одной структуре, получить к ним доступ, записать и прочитать их из EEPROM. Наша структура будет немного сложнее, но суть остается той же самой. // Дополнительная структура описывающая IPv4 адреса struct addres { byte a; byte b; byte c; byte d; }; // Структура объекта конфига для хранения в EEPROM struct configObj { addres ip; addres subnet; addres gateway; addres dns; byte mac[6]; byte hex; char server[40]; char topic[40]; } config; Данная структура хранит сетевые настройки для работы с Ethernet модулем (w5100 и выше) Arduino, базовые настройки для связи с MQTT брокером. Сразу при описании структуры мы объявили новую переменную с именем config с типом нашей структуры. ВАЖНО: кроме наших данных в структуре имеется дополнительная переменная с именем hex. Её задача, это контроль наличия наших данных в EEPROM. Она всегда должна содержать одно и тоже значение. Представьте ситуацию, что вы взяли контроллер в EEPROM которого находится какая-либо информация (может там чисто, но мы этого не знаем наверняка) и мы прочитаем данные и поместим их в нашу переменную. В итоге мы получим данные которым нельзя доверять, а что еще хуже, это если эти самые данные нарушат работу внешнего оборудования. Более правильным, на мой взгляд, будет проверка значений по конкретно определенным адресам. Например, мы знаем, что в 16 байте должно быть значение 0xAA и если оно действительно там, то мы убеждаемся, что это наша информация. Естественно, что контрольных точек может быть несколько и разумеется с разными значениями, это увеличит гарантию того, что данные являются нашими, но 100% гарантии не даст. Для более серьезных проектов есть более серьезные методы, например, подсчет контрольной суммы всего набора данных. Также структура может иметь вложенные структуры, у нас ими являются: ip, subnet, gateway, dns. Вы можете отказаться от такого варианта и записывать данные просто в массив байт, как это было сделано с MAC адресом. Естественно, что обращаться к этим полям нужно по-разному. Запись данных в поле subnet config.subnet = {255, 255, 255, 0}; Запись данных в поле mac byte mac[] = {0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02}; memcpy(config.mac, mac, 6); С записью данных в поле server все еще проще config.server = "mqtt.it4it.club"; Функция, которая возвращает нашу структуру данных с полностью заполненными полями. // Начальный конфиг configObj defaultConfig() { configObj config = { {192, 168, 0, 200}, {255, 255, 255, 0}, {192, 168, 0, 1}, {192, 168, 0, 1}, {0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02}, 0xAA, // Не трогать! Используется для проверки конфигурации в EEPROM F("mqtt.it4it.club"), F("arduino/serial/config") }; return config; } К примеру, два последних значения записывать не обязательно, тогда эти поля останутся пусты если использовать данную функцию для возврата к "заводским" настройкам. Вот пример того, как используя описанную нами структуру, мы проверяем целостность настроек в EEPROM и в случае не совпадения hex значений, загружаем настройки по умолчанию. const byte startingAddress = 9; bool configured = false; void loadConfig() { EEPROM.get(startingAddress, config); if (config.hex == 170) configured = true; else config = defaultConfig(); configEthernet(); // Функция производящая настройку сети } Как контроллеру начать понимать, что от него хотят В Arduino имеется функция, вызываемая каждый раз, когда в передаваемый буфер данных попадает знакомый нам символ перевода строки. void serialEvent() { // Вызывается каждый раз, когда что-то прилетает по UART // Данные передаются посимвольно. Если в строке 100 символов, то функция будет вызвана 100 раз } И в контексте обсуждаемой нами программы, мы можем представить ее в следующем виде void serialEvent() { serialEventTime = millis(); if (console.available()) { char c = (char)console.read(); if (inputCommands.length() < inputCommandsLength) { if (c != '\n') inputCommands += c; else if (inputCommands.length()) inputCommandsComplete = true; } } } Её задача, символ за символом, собрать в кучу все переданные нами данные и при получении заветного символа перевода строки (именно он даст нам понять, что передача сообщения завершена) сообщить, что команда получена и передать накопленный буфер данных своей напарнице по цеху. Но перед тем как это сделать стоит рассмотреть альтернативную ветку развития событий, а именно тот факт, что нам попросту могут прислать огромную, кучу шлака без волшебного символа, а раз могут, значит рано или поздно пришлют. И мы бесполезно потратим ценные ресурсы микроконтроллера, что может привести к непредсказуемым результатам в дальнейшем. Поэтому, в логику функции, мы добавим дополнительное ограничение на количество переданных символов, если оно достигнуто, то попросту перестаем воспринимать последующие данные. Останется только избавиться от них, и самым удобным моментом будет, когда этот поток шлака прекратиться. Чтобы об этом узнать мы будем запоминать время, когда пришел каждый из символов переданной строки перезаписывая соответствующую временную переменную данными о следующем символе и т.д пока поток не иссякнет. И как только расхождение текущего времени CPU и времени, когда поступил последний символ превысит некоторое значение, пусть это будет 1 секунда, мы очистим нашу память. Этот простой механизм напоминающий амнезию позволит избавить нас от лишних проблем. Переменная отвечающая за размер принимаемого буфера const byte inputCommandsLength = 60; Теперь можно переходить к напарнице предыдущей героини и, по совместительству, основной рабочей лошадки - функции обрабатывающей адекватно полученные данные. void serialEventHandler() { // вызывается в loop и проверяет взведена ли переменная inputCommandsComplete // в полученных данных пытается распознать команды } По началу я хотел описать данную функцию в упрощенном варианте, но в процессе понял, что ничего хорошего из этого не выйдет, и я решил описать только ключевые моменты, но их будет достаточно, на мой взгляд. Разбор serialEventHandler Полученные данные будут переданы нам в переменной inputCommands с типом String В первую очередь стоит почистить ее от лишних пробельных символов. Они часто встречаются в начале и в конце строки если пользователь копирует текст, а не набирает его самостоятельно. Это распространенная ситуация, приводящая к отказу принятия команды и бороться с ней очень просто. inputCommands.trim(); Далее стоит отсеять команды, не несущие никакой динамической информации, например, help, restart, reset и т.п это предписывающие команды которые заставляют контроллер выполнять строго описанные функции без вмешательства в их работу. if (inputCommands == F("help")) { consoleHelp(); } else if (inputCommands == F("restart")) { resetFunc(); } else { // Все сложные команды обрабатываются в этом блоке } Как Вы видите, все очень просто и скучно. Но не в том случае если команда динамическая, то есть содержит не только саму команду (заголовок) но и полезную нагрузку (параметр) которая может меняться раз от раза. Простой пример это команда изменения ip адреса и её варианты: ip 37.140.198.90 ip 192.168.0.244 ip 10.10.10.88 В данном случае, нам стоит понять, относится ли данная команда именно к ip адресу. Для этого в наборе String имеется отличный метод, позволяющий производить сравнение переданного ему параметра с началом строки. if (inputCommands.startsWith(F("ip"))) { // Строка inputCommands начинается с пары символов "ip" } Если все идет так, как мы задумали, то нам стоит отделить динамическую часть - наш параметр, от заголовка и получить полезную нагрузку. В этом нам поможет, опять же из набора String, метод substring позволяющий получать часть строки с указанием начального и конечного символа подстроки. Последний параметр указывать не обязательно и в таком случае мы получим всю строку начиная с указанного символа. inputCommands.substring(4) В данном случае начиная с 4-его и заканчивая последним. И как Вы успели заметить, отсчет мы начинаем не с третьего символа, что соответствует нашей строке без вступительного "ip", а на один больше т.к между заголовком и параметром имеется разделяющий символ в виде пробела. Далее, полученную строку мы передадим в функцию, занимающуюся разбором на компоненты и принимающую следующие параметры: Указатель на переменную с типом char, для этого нам потребуется преобразовать наш тип String Символ разделителя, что для IPv4 является точка "." Указатель на массив типа byte, которому будет присвоен результат разбора Количество искомых элементов в строке И система счисления, подразумеваемая в качестве исходной для записи элементов подстроки /* Парсинг https://stackoverflow.com/questions/35227449/convert-ip-or-mac-address-from-string-to-byte-array-arduino-or-c */ void parseBytes(const char* str, char sep, byte* bytes, int maxBytes, int base) { for (int i = 0; i < maxBytes; i++) { bytes[i] = strtoul(str, NULL, base); str = strchr(str, sep); if (str == NULL || *str == '\0') break; str++; } } В нашем случае выглядеть это будет следующим образом byte ip[4]; parseBytes(inputCommands.substring(4).c_str(), '.', ip, 4, 10); А дале все становится еще проще, попросту проверить попадает ли наш ip адрес, в список правильных адресов. И самой простой проверкой послужит проверка первого байта адреса на несоответствие не угодным нам сетям (0, 127, 255) if (ip[0] != 127 and ip[0] != 255 and ip[0] != 0) { // Производим необходимые нам действия с ip адресом, например, запись в конфиг config.ip = {ip[0], ip[1], ip[2], ip[3]}; } Вы в праве реализовать собственные проверки, какие только душе угодны. Также хотелось бы отметить, что обрабатывать некоторые параметры проще и быстрее через их короткие записи. К таким можно отнести маску подсети устройства. Например, привычный дня нас адрес 192.168.0.1 с маской подсети 255.255.255.0 можно записать в виде 192.168.0.1/24, где цифра 24 указывает нашу подсеть в краткой форме. А, следовательно, мы можем записать несколько кратких форм масок подсети в следующем виде: subnet 255.255.255.0 или subnet 24 subnet 255.255.0.0 или subnet 16 subnet 255.0.0.0 или subnet 8 Это основные маски, и я не описывал все существующие т.к в этом нет нужды, но если Вам интересно, то почитать про них можно в wikipedia. if (inputCommands.startsWith(F("subnet"))) { String input = inputCommands.substring(8); if (input == F("24")) config.subnet = {255, 255, 255, 0}; else if (input == F("16")) config.subnet = {255, 255, 0, 0}; else if (input == F("8")) config.subnet = {255, 0, 0, 0}; else { // Все остальные маски попадают в этот блок byte subnet[4]; parseBytes(input.c_str(), '.', subnet, 4, 10); config.subnet = {subnet[0], subnet[1], subnet[2], subnet[3]}; } } MAC адрес хранится у нас в виде массива байт. Его перезапись другим массивом производится с помощью функции memcpy if (inputCommands.startsWith(F("mac"))) { byte mac[6]; parseBytes(inputCommands.substring(4).c_str(), ':', mac, 6, 16); memcpy(config.mac, mac, 6); } Изменение адреса MQTT сервера if (inputCommands.startsWith(F("server"))) { String server = inputCommands.substring(8); server.trim(); if (server.length() < 40) server.toCharArray(config.server, 40); } В принципе теперь понятно, как производить получение, разбор и сохранение конфигурации в EEPROM микроконтроллера. Как это выглядит на практике Заливаем программу в микроконтроллер и подключаемся к Arduino по usb или через переходник. Открываем терминал и нас приветствуют краткой справкой с описанием доступных команд. - --------------------------------------------------------------------------------------- # Sensor with data sending to mqtt server (c) it4it.club # Use the "config" command to view the current configuration # To change the configuration, specify the parameter name and its new value with a space, # for example "ip 192.168.0.200", "subnet 255.255.255.0" or "mac AA:BB:CC:DD:EE:FF" # You can also specify a subnet using the mask 24, 16 or 8 # Additional commands: # sensors - view current data from sensors # config - view current configuration # save - saves the current configuration # reset - resets all settings # restart - restarts the device # eeprom clear - removes all contents of eeprom # help - view this help - --------------------------------------------------------------------------------------- Т.к. в EEPROM микроконтроллера не была обнаружена конфигурация (волшебный hex байт нам подсказал), то были задействованы стандартные настройки. Просмотреть текущую конфигурацию можно командой config config # ip: 192.168.0.200 # subnet: 255.255.255.0 # gateway: 192.168.0.1 # dns: 192.168.0.1 # mac: 00:AA:BB:CC:DE:02 # server: mqtt.it4it.club # topic: arduino/serial/config чтобы изменить значение любого из этих параметров необходимо передать имя параметра и его новое значение, разделенные между собой пробелом. ip 10.10.10.99 # ok gateway 10.10.10.1 # ok dns 10.10.10.1 # ok После окончания конфигурации необходимо сохранить настройки и если были затронуты критические параметры, например, сеть, то перезапустить Arduino соответствующей командой. save # ok restart # ok # restarting device... Если параметр был успешно принят, то контроллер ответит нам "ok", а в противном случае ругнется. ip 127.0.0.1 # bad ip Также мы получим негативный ответ если команда не была распознана. qwerqwer1243 # bad command С остальными командами Вы разберетесь самостоятельно. Исходник: MQTT_CLIENT_328_SERIAL_CONFIG.zip PS: в общем то это статья родилась только для того, чтобы в соседнем форуме с системой мониторинга Zabbix появилась ссылка на устройство, но я надеюсь, что она также станет полезна любителям домашней автоматизации и не только.
  14. Kitsum

    Обучаем Zabbix работать с MQTT протоколом

    Привет друзья. Обкатка прошла успешно и пришло время расширить функционал и обзавестись дополнительными плюшками. А именно: Добавлять устройства по реальными ip адресами, а не эмитировать их через петлю сервера 127.0.0.1 Реализовать возможность подключения как к приватному брокеру с системой авторизации, так и оставить поддержку открытых брокеров Перенести весь функционал в шаблон Предусмотреть возможность использования одного шаблона с различными брокерами. А также разделить адрес топика на две части - корневой путь, который можно выстраивать динамически у каждого узла и статическую часть, несущую как смысловую нагрузку, так и сами данные. Вам понадобится mosquitto-clients. У меня сервер под Linux Ubuntu поэтому от этого и будем отталкиваться. Если у Вас что-то иное, то действуйте по аналогии. sudo apt-get install mosquitto-clients Далее создаем дополнительный внешний скрипт для Zabbix. По умолчанию, все используемые сервером скрипты располагаются по адресу /usr/lib/zabbix/externalscripts Быстро узнать какой каталог использует Ваш сервер можно так cat /etc/zabbix/zabbix_server.conf | grep ExternalScripts Добавляем новый скрипт и заодно выставляем на него права. В моем случае, сервер работает от имени пользователя zabbix cd /usr/lib/zabbix/externalscripts touch ./mqtt chown zabbix:zabbix ./mqtt chmod 555 ./mqtt Скрипт станет посредником между Zabbix сервером и брокером. Его основная задача, это прием входных параметров, их подсчет и принятие решения о том чем эти самые параметры являются. А вот и сам скрипт Скрипт способен принимать до 4 входных параметров и от их количества будет зависеть то, какое место займет каждый из перечисленных параметров. Теперь в самом zabbix сервере, при создании нового элемента данных, нам доступна внешняя проверка mqtt принимающая до 4 параметров mqtt[<топик>] mqtt[<адрес сервера>, <топик>] mqtt[<топик>, <имя пользователя>, <пароль>] mqtt[<адрес сервера>, <топик>, <имя пользователя>, <пароль>] А вот и сам тестовый шаблон: mqtt-temperature.zip Очень удобным решением будет использование пользовательских макросов в создаваемых шаблонах и в самих узлах. Создаем новый узел сети, теперь можно указывать его реальный ip адрес, и переходим в раздел "макросы". Макросы узла имеют приоритет над макросами прикрепленного к узлу шаблона. Таким макросом мы можем указать корневой топик для всей веки собираемой информации. Если переключиться на "макросы узла сети и унаследованные", то мы получим весь список доступных макросов. Таким образом мы можем указать основные настройки на уровне шаблона и вносить мелкие корректировки в эти самые настройки уже на уровне самого узла сети. Также Вы можете добавить собственные макросы и предусмотреть для них место в логике скрипта. Например, добавить возможность выбора порта брокера или иного параметра для mosquitto_sub Использовать макросы очень просто, достаточно указать их в качестве параметра или его части. Далее работаем с ними также, как и с обычными параметрами Для проверки работоспособности удобно использовать программу mqtt-spy ЕЩЕ РАЗ НАПОМИНАЮ: Клиенты рассылающие сообщения должны использовать параметр "-r, --retain" для сохранения сообщения у брокера. Без этого параметра Zabbix не сможет получить данные т.к не поддерживает постоянную связь с брокером, а лишь забирает последние данные по установленному интервалу времени.
  15. Kitsum

    Метеостанция на ESP8266 от it4it.club

    Друзья, всем доброго времени суток. Сообщение будет немного не по теме. Мы запустили в тестовом режиме MQTT сервер Адрес: mqtt.it4it.club Порт: 1883 На данный момент сервер полностью прозрачен и практически не имеет никаких ограничений. По окончанию теста будут добавлены частные топики с авторизованным доступом. Брокер абсолютно бесплатный и таковым и останется. Можете использовать его в собственных разработках.
  16. Kitsum

    Отключаем рекламу в Viber

    Вот и настала очередь популярного мессенджера Viber. Лично у меня терпение лопнуло, когда список контактов был уменьшен в пользу окна с рекламой. Может кому-то это и не доставляло бы много дискомфорта, но не для меня. Спам валит именно когда ты разворачиваешь интерфейс программы, именно тогда, когда требуется больше возможностей для взаимодействия с ней... И главное - закрыть этот поток шлака, а иначе назвать распространяемую контент в рекламе, невозможно. Ну а раз Вы читаете эту заметку, значит у Вас тоже полыхнуло, и срочно требуется лечение данной проблемы. Все крайне просто и быстро. Необходимо изолировать наш клиент от той помойной ямы с которой он тянет эту рекламу. А расположена она по адресу ads.viber.com и самым простым способом изоляции является подмена адреса сервера с помощью файла hosts для Windows Запускаем блокнот с правами администратора (ПКМ -> запуск от имени администратора) и открываем hosts файл, расположенный по следующему пути %SystemRoot%\system32\drivers\etc\hosts Если запускать программу без повышения привилегий в системе, Вы не сможете сохранить наши изменения. А требуется добавить в конце файла новую строку со следующим содержимым 127.0.0.1 ads.viber.com Сохраняем файл и перезапускаем Viber для Linux Делаем все тоже самое, только файл hosts располагается по следующему пути /etc/hosts PS: также смотрите как избавиться от рекламы в uTorrent
  17. Kitsum

    Метеостанция на ESP8266 от it4it.club

    Друзья, в очередной раз приветствую Вас. Появилась возможность вернуться к метеостанции, а значит, сделаем маленькое обновление по Вашим замечаниям. В первую очередь были исправлены опечатки и орфографические ошибки. К сожалению, без этого никуда. Небольшие изменения в html и css разметке чтобы убрать редкие, но все равно присутствующие, смещения слоев. Исправлен баг с отправкой данных MQTT брокеру И главное нововведение, это проверка контрольной суммы, заливаемой через web интерфейс прошивки. Огромное спасибо @Alex_DIY за то, что указал на отсутствие данного функционала в предыдущих версиях. Также, если во время обновления, на стороне контроллера произойдет сбой, в web интерфейс будет возвращен код ошибки и выведено его описание. Немного о контроле md5 хэша. Для выполнения этой процедуры Ваш браузер должен поддерживать работу с FileAPI, что позволит получить доступ к загружаемой прошивке еще до того, как она будет передана микроконтроллеру. Подробнее можно почитать тут https://developer.mozilla.org/ru/docs/Web/API/FileReader А вот реализация самого md5 алгоритма была позаимствована у SparkMD5, ссылка как всегда прилагается https://github.com/satazor/js-spark-md5 К сожалению штатных средств расчета контрольной суммы с помощью данного алгоритма мне найти не удалось, возможно плохо искал. Вся поддержка md5 описана в отдельном файле md5.js.gz в корне web сервера, поместить его непосредственно в index.htm не представляется возможным из-за веса библиотеки в не сжатом виде. Алгоритм загрузки прошивки через web интерфейс на esp8266 немного изменился, но по-прежнему прост. В момент перехода в раздел обновления программы происходит проверка, поддерживает ли Ваш браузер необходимый функционал и загрузилась ли библиотека. Если эти условия НЕ выполнены, то будет отображено соответствующее предупреждение, но возможность загрузить программу по-прежнему имеется, но уже без проверки хэш-суммы. Если же все прошло успешно, то при передаче файла прошивки место его имени будет передан наш md5 хэш. Это связано с особенностями работы метода arg библиотеки web сервера. Досконально разбираться не вижу смысла т.к использование имени файла в качестве транспорта мне кажется вполне жизнеспособным решением учитывая, что само имя файла не несет никакой смысловой нагрузки и практической цели. Если у Вас появятся еще замечания, предложения или свои наработки, обязательно делитесь ими! PS: Все исходники опубликованы в первом сообщении данной темы.
  18. @Shaman59rus Пока такую задачу никто не ставил, но всегда можно договориться. Напишите в приват, что конкретно вы хотите получить, поподробнее про 4 электромагнита и в какую сумму необходимо уложиться. Попробуем реализовать Вашу задумку.
  19. Kitsum

    esp8266 и парсинг погоды с OpenWeatherMap

    @Alexander Воспользуйтесь библиотекой LiquidCrystal_I2C Чтобы вывести что-то на дисплей имеются стандартные функции lcd.setCursor(0,2); // Выставить позицию lcd.print(weather.temp); // Вывести температуру
  20. Думаю что все и так понятно из названия. Да, он есть! Да, уже давно! А, что самое интересно, так простота решения задачи. Все, что требуется - создать файл grep.cmd в корневом каталоге Windows со следующим содержимым. findstr %1 %2 %3 %4 %5 Или запустить консоль с правами администратора и выполнить следующую команду echo findstr %1 %2 %3 %4 %5 > %systemroot%\grep.cmd Утилита findstr, которая попытается заменить нам grep, достаточный набор параметров и поддержку регулярных выражений. Подробности можно узнать на официальном сайте https://technet.microsoft.com/ru-ru/library/bb490907.aspx
  21. Заметка о создании само подписанного сертификата для Apache под Linux Ubuntu сроком на 100 лет. Само подписанный сертификат "не есть плохо", учитывая, что со своими задачами он справляется также хорошо, как и его платные братья. Конечно, если использовать его в публичных проектах, то доверие пользователей к ресурсу начинает катастрофически падать, но если речь идет о внутренних ресурсах, организованных для небольшой группы лиц, то это решение выглядит очень аппетитно. Выбираем самый короткий путь, логинимся на сервере под пользователем root Создаем сертификат сроком на 100 лет. Почему на такой долгий? Да потому, что через год никто не вспомнит о том, что его надо пересоздать, да и важность ресурса далеко не банковского уровня. openssl req -x509 -nodes -days 36500 -newkey rsa:1024 -keyout /etc/ssl/private/apache.key -out /etc/ssl/certs/apache.pem Таким образом, сертификат стандарта X509 и сроком на 100 лет будет записан в файл /etc/ssl/private/apache.key, а его 1024 битный ключ в файл /etc/ssl/certs/apache.pem В процессе создания сертификата будут сыпаться различные вопросы от утилиты, можно пропускать все (прожимаем <ENTER>), кроме имени домена для которого этот сертификат и предназначается. Отметим, что пароль к сертификату запрашиваться не будет, об этом мы позаботились с помощью параметра -nodes. Да и пароль нам и не нужен, в противном случае его придется вводить при каждом запуске Apache. Теперь необходимо поправить ssl профиль для Apache. nano /etc/apache2/sites-available/default-ssl.conf Нам необходимо отключить поддержку SSLv2 и SSLv3 из-за проблем с безопасностью. Добавляем, а если он уже есть, то правим следующий параметр SSLProtocol all -SSLv2 -SSLv3 Указываем откуда подтянуть сертификат с ключом и сохраняем профиль SSLCertificateFile /etc/ssl/certs/apache.pem SSLCertificateKeyFile /etc/ssl/private/apache.key Выбираемся обратно в консоль и подключаем модуль SSL к Apache, а заодно подключаем наш обновленный профиль a2enmod ssl a2ensite default-ssl.conf Финалом торжества станет перезапуск сервера /etc/init.d/apache2 restart Если понадобится принудительно перенаправлять потребителей трафика с http на https протокол, то самым простым способом будет использование mod_rewrite a2enmod rewrite /etc/init.d/apache2 restart После идем в нужный каталог Apache и создаем там конфигурационный файл .htaccess со следующим содержимым RewriteEngine On RewriteCond %{SERVER_PORT} !^443$ RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R,L] Если не отрабатывает ни одно из правил, описанных в .htaccess, то вероятнее всего, в конфигурации Apache стоит запрет на его чтение. Идем в файл конфигурации nano /etc/apache2/apache2.conf Ищем описание настроек безопасности для корневого каталога Вашего web сервера <Directory /var/www/> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> И разрешаем всем описанным в фале .htaccess директивам отмену ранее установленных правил доступа AllowOverride All И снова перезапускаем Apache. Не забудьте обновить сертификат после окончания срока его действия, если здоровье еще будет позволять.
  22. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Anykey дело в том, что снимаемые показания будут напрямую зависеть от качества проработки и исполнения устройства. Чем они хуже, тем больше мы будем получать устройство измеряющее частоту собственного вращения, никак не связанную с реальными показаниями скорости ветра, да и про линейность можно забыть. В итоге, на выходе может получиться устройство, которое дает только моральное удовлетворение от его же наличия и немного каких-то показаний которым, возможно, можно доверять только для фактического определения наличия ветра или других факторов заставляющих эти самые показания изменяться. На мой взгляд, необходимо учитывать все, начиная от радиуса окружности описывающей траекторию движения "чашек", заканчивая сопротивлением подшипника или другого устройства скольжения и силой которую необходимо приложить для того, чтобы эти самые чашки заставить сдвинуться с места. Опять же под вопросом, сохраниться ли линейность при разбросе температуры эксплуатации ±30 градусов Цельсия. И многое другое. PS: это мое личное мнение и оно ни в коем случае не претендует на истину в последней инстанции.
  23. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Alex_DIY Вы правы, стоит добавить проверку контрольной суммы прошивки. Я не сталкивался с этой проблемой т.к использую контроллеры с flash памятью объемом 4Mb. Сам принцип загрузки и обновления прошивки заимствован из стандартного примера WebUpdate для контроллера esp8266, но все необходимое для реализации проверки контрольной суммы на стороне контроллера уже реализовано "из коробки" и описано в файлах %homepath%\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\Updater.h %homepath%\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\Updater.cpp bool UpdaterClass::setMD5(const char * expected_md5){ if(strlen(expected_md5) != 32) { return false; } _target_md5 = expected_md5; return true; } Останется только правильно вычислить md5-хеш при выборе файла прошивки в web интерфейсе и передать его контроллеру в описанную выше функцию. Возможно, Вам будет интересно содержимое файла Updater.ccp как и других файлов из его каталога. Что касаемо расчета места под прошивку, то возможно, самый простой вариант является и самым эффективным, и правильнее просто указать конкретное значение в зависимости от используемой памяти. На данный момент web интерфейс отслеживает только передачу файла и подсчитывает количество переданных байт, отсюда и сообщение об удачном завершении операции, поскольку фактическая передача файла завершилась успешно. @alexd6969 По моему мнению, в измерении скорости ветра нет ничего сложного с программной точки зрения. Принцип снятия показаний обсуждался в начале темы. Самой большой трудностью является физическое исполнение сенсора. Даже если это классический анемометр, конструкция должна быть легко повторяемой и иметь высокую стойкость к погодным причудам, будь то банальная коррозия металлических элементов или обмерзание в зимнее время суток. Модели механических анемометров https://www.thingiverse.com/thing:41367 https://www.thingiverse.com/thing:144665 https://www.thingiverse.com/thing:364660 Ультразвуковой анемометр http://blog.regimov.net/ультразвуковой-анемометр-прототип/ http://blog.regimov.net/ультразвуковой-анемометр-на-двух-hc-sr04/ В общем, все упирается в надежность конструкции.
  24. Kitsum

    Метеостанция на ESP8266 от it4it.club

    Обновление от 12.08.2017 Доброго всем времени суток друзья. Проект метеостанции на контроллере ESP8266 близится к своему завершению. Я хотел опубликовать это обновление раньше, но жизненные обстоятельства связывали руки. Все последующие обновления будут выходить только по просьбе трудящихся на благо домашней автоматизации, то есть Вас. Поэтому предлагайте свои идеи, высказывайте пожелания и делитель своими наработками. ВАЖНО: Друзья, перед тем как приступить к обзору обновления, я хочу обратить Ваше внимание на крайне важный момент. Бывают случаи, когда контролер зависает, например при выполнении функций esp.reset() и esp.restart(). Это не связано с реализацией программы метеостанции, проблема идет от разработчиков микроконтроллера. Чтобы не расписывать возможные причины происхождения данной проблемы, я оставлю ссылку на тему в которой эта ситуация обсуждается https://github.com/esp8266/Arduino/issues/1017 Чтобы избавиться от проблемы Вам необходимо, перед тем как устанавливать метеостанцию на постоянное место жительства, подтянуть GPIO-0 (пин D3 на плате NodeMCU) к питанию 3.3V. Во время данной процедуры, питание на контроллере должно отсутствовать, это на тот случай если вы захотите сделать это на "горячую". Суть в том, чтобы при запуске, микроконтроллер увидел логическую единицу на GPIO-0. И так, что нас ждет в этом обновлении: Все, что связано с логикой работы функций конфигурации вынесено в файл config.h Ранее, кусок отвечающий за обновление конфигурации через web интерфейс еще маячила в .ino файле, но теперь переехал на свое законное место. Добавлена возможность полного удаления файла конфигурации Hard Reset. Найти соответствующую кнопку можно в web интерфейсе, в разделе "Система". Добавлена система логирования показаний датчиков. В логе хранятся показания за последние 24 часа, интервал снятия данные составляет 10 минут. Иконка для вызова графика находится на главной странице и доступна всем устройствам, ширина разрешающей способности дисплея которых превышает 600 пикселей. ВАЖНО: алгоритм построен таким образом, что лог не привязан ко времени, а сам контроллер следит только за временем, прошедшим с момента последней записи в лог. При передаче журнала в web интерфейс также передается это значение в виде параметра timeAdjustment, именно относительно него начнется построение графика в обратную сторону с использования времени на устройстве, которое Вы используете (пк, планшет, телефон). "Зачем это нужно?", спросите Вы, а все на самом деле очень просто. Для логирования показаний с одного датчика с описанным выше интервалом времени требуется 144 элемента типа Float в массиве. Если добавить к каждой точке еще переменную для хранения времени снятия показаний, то объем занимаемой памяти вырастит минимум в 2 раза, в зависимости от типа этой самой переменной. Умножим все это на количество используемых датчиков, и затраты ресурсов будут слишком высоки. Плюс ко всему необходимо поднимать собственный NTP клиент, хоть это и не проблема, но гарантировать точность времени будет проблематично т.к я не знаю где вы можете использовать метеостанцию, а как показала практика, некоторые желают её применять в теплице баз связи с внешним миром. А большинство устройств, используемых для работы с метеостанцией уже имеют на борту синхронизированное время и от него отталкиваться значительно проще. Оси графика общие для всех датчиков, но шкалы автоматически масштабируются в зависимости от диапазона тех данных, которые они описывают. Сам график можно зуммировать, достаточно просто выделить интересующий нас временной интервал. За возможность реализовать данный функционал отвечает программный комплекс от highcharts.com, там же Вы можете найти описание API модуля и все, что с ним связано. Исправлен баг с авторизацией на MQTT сервере. Исправлен баг с пропаданием индикации состояния микроконтроллера через штатный светодиод. Теперь, при программной "перезагрузки" микроконтроллера штатный светодиод горит постоянно. Теперь данные с датчиков хранящиеся в структуре sensors имеют тип Float. Это убирает ряд неудобств, с которыми мы с Вами сталкивались ранее и упрощает, для понимания, часть кода. В связи с этим на MQTT сервер теперь улетают данные с плавающей запятой, примите это во внимание если используете данный функционал. Внутренняя начинка web интерфейса претерпела ряд изменений, не связанных с визуальной составляющей, но они были необходимы для более корректной, на мой взгляд, работы java. По Вашим замечаниям были внесены поправки в паттерны отвечающие за проверку корректности вводимых данных в формы web интерфейса. Например, в SSID точки доступа разрешена точка, а в именах хостов разрешено использовать домены "ниже" второго уровня. Раздел "Система" в web интерфейсе немного поправился и отображает дополнительную информацию. Раздел "Устройства на I2C шине" теперь не сообщает о модели датчика на конкретном адресе, т.к это определить не возможно (если только сам датчик не располагает этой информацией), а просто отображает предполагаемый тип устройства на шине. В разделе "Народный мониторинг" появилась кнопка, позволяющая устанавливать MAC адрес устройства в качестве идентификатора для данного устройства в одноименной системе. Доработан внешний вид интерфейса, внесены мелкие корректировки, для более насыщенной картинки добавлены тени, некоторые элементы теперь плавно появляются и исчезают. Изменена система оповещений, стандартная java функция Alert покинула нас, а её место по праву заняла отдельная система оповещения использующая собственный html шаблон. Также из исходников удалены почти все не запакованные файлы web интерфейса. Как показала практика, в них нет нужды. На этом все. Надеюсь было интересно и данные наработки пригодятся Вам в будущем. Если у Вас имеется желание помочь нам финансово, то сделать это возможно следующими способами: Yandex.Money PayPal.me PS: По традиции, все исходники опубликованы в первом сообщении данной темы.
  25. Kitsum

    Метеостанция на ESP8266 от it4it.club

    @Alex_DIY доброе время суток. IE 11 считается устаревшим браузером и не имеет необходимой поддержки для корректной работы web интерфейса. Ему на смену пришел Microsoft Edge, большую часть функционала он уже должен поддерживать. Под продукты Microsoft необходимо подстраиваться отдельно. Я, по большей части, использую Google Chrome и Safari, но все должно также корректно работать в FireFox и во всех браузерах на движке Blink. Процессы прошивки микроконтроллера напрямую и по "воздуху" различаются. Если память не подводит то, в последнем случае, прошивка заливается в специальную область на flash и после начинается процесс прошивки. Возможно Вам будет интересна эта тема https://github.com/esp8266/Arduino/issues/2438 но я все же склоняюсь к недостаточному объему flash для нормальной поддержки OTA. Осмелюсь предложить произвести замену flash памяти на W25Q32BVSSIG (25Q32BVSIG) SO-8, 32Мбит, 4МБ. Это копеечная память, да и замена не займет много времени. Datasheet: DOC001081512.pdf
×
×
  • Создать...