esp8266 & Zabbix
- Войдите на сайт для отправки комментариев
В общем, добил я эту тему, правда пока всё в наколенном состоянии, просто, наверное, сообщить, что это работает. Действующие лица:
USB to ESP-01 adapter: https://ru.aliexpress.com/item/SMART-USB-ESP8266-ESP-01-Wi-Fi-Bluetooth-CH340G/32915870451.html
ESP8266-01: https://ru.aliexpress.com/item/ESP-01-ESP8266-wifi/32900849637.html
ESP-01S DHT11 module: https://ru.aliexpress.com/item/ESP8266-ESP-01-ESP-01S-DHT11-esp8266/32887522806.html
Как этим добром пользоваться: http://robotchip.ru/obzor-modulya-dht11-dlya-esp-01/
Zabbix server 4.0 (последний актуальный).
Берём библиотеку ESP8266ZabbixSender https://github.com/DTherHtun/ESP8266ZabbixSender Пример из нее замечательно работает с минимальными изменениями (я лишь добавил подъём статики).
Далее, имплантируем код опроса датчика DHT11 (знаю, что говно, но с чего-то начинать надо).
Получается что-то вроде:
#include <ESP8266ZabbixSender.h>
#include "DHT.h" // Подключаем библиотеку DHT
#define DHTPIN 2 // Пин к которому подключен датчик
#define DHTTYPE DHT11 // Используемый датчик DHT 11
// #define DHTTYPE DHT21
// #define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE); // Инициализируем датчик
float temperature = 0; // запрос на считывание температуры
// float humidity = 0; // влажность не читаем, датчик врёт
ESP8266ZabbixSender zSender;
/* WiFi settings */
String ssid = "zopukh@home";
String pass = "zn3GXAj41T";
/* Zabbix host setting */
IPAddress ip(192, 168, 12, 13); // host IP
IPAddress gateway(192, 168, 12, 1); // gateway IP
IPAddress subnet(255, 255, 255, 0); // network mask
/* Zabbix server setting */
#define SERVERADDR 192, 168, 12, 3 // Zabbix server Address
#define ZABBIXPORT 10051 // Zabbix erver Port
#define ZABBIXAGHOST "IOTBOARD_00" // Zabbix item's host name
boolean checkConnection();
// ----------------------------------------------------------------------------
// SETUP
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
WiFi.begin(ssid.c_str(), pass.c_str());
WiFi.config(ip, gateway, subnet);
while (!checkConnection()) {
}
zSender.Init(IPAddress(SERVERADDR), ZABBIXPORT, ZABBIXAGHOST); // Init zabbix server information
dht.begin(); // Инициализация DHT
}
void loop() {
temperature = dht.readTemperature(); // Запрос на считывание температуры
// humidity = dht.readHumidity(); // влажность не читаем, датчик врёт
checkConnection(); // Check wifi connection
zSender.ClearItem(); // Clear ZabbixSender's item list
zSender.AddItem("temperature", (float)temperature); // Exmaple value of zabbix trapper item
// zSender.AddItem("humidity", (float)humidity); // Exmaple value of zabbix trapper item
if (zSender.Send() == EXIT_SUCCESS) { // Send zabbix items
Serial.println("ZABBIX SEND: OK");
} else {
Serial.println("ZABBIX SEND: NG");
}
delay(10000); // конский делай в 10 секунд
}
boolean checkConnection() {
int count = 0;
Serial.print("Waiting for Wi-Fi connection");
while (count < 300) {
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.println("Connected!");
return (true);
}
delay(500);
Serial.print(".");
count++;
}
Serial.println("Timed out.");
return false;
}
В Zabbix создаем узел сети (в общем-то стандартный). В нем элемент данных с ключём temperature и типом Zabbix trapper, тип данных -- числовой с плавающей точкой. Остальное по вкусу.
Запускаем, наслаждаемся.
Более причёсанный код:
zabbixsender_20190215_02.ino:
// ---------------------------------------------------------------------------- // Таймер потока 01 uint8_t avrTthread01(uint16_t span) { static uint32_t future = 0; if (millis() < future) return 0; future += span; return 1; } // ---------------------------------------------------------------------------- // Таймер потока 02 uint8_t avrTthread02(uint16_t span) { static uint32_t future = 0; if (millis() < future) return 0; future += span; return 1; } // ---------------------------------------------------------------------------- // SETUP void setup() { Serial.begin(115200); // инициализация последовательного порта esp8266_setup(); // инициализация ESP8266 dht_setup(); // инициализация DHT return; } // ---------------------------------------------------------------------------- // LOOP void loop() { // опрос датчика dht-11 if (avrTthread01(15000)) { // Serial.println("thread 1 "); dht_read(); // Запрос на считывание температуры } // отправка данных в Zabbix if (avrTthread02(60000)) { // Serial.println("thread 2 "); esp8266_sendMessage(); } return; }dht_sensor.ino:
#include "DHT.h" // Подключаем библиотеку DHT #define DHTPIN 2 // Пин к которому подключен датчик #define DHTTYPE DHT11 // Используемый датчик DHT 11 // #define DHTTYPE DHT21 // #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); // Инициализируем датчик /* DHT variable*/ float temperature = 0; // запрос на считывание температуры // float humidity = 0; // влажность не читаем, датчик врёт const uint8_t averageFactor = 5; // коэффициент сглаживания показаний (0 = не сглаживать) // ---------------------------------------------------------------------------- // Первоначальная инициализация сенсора void dht_setup() { dht.begin(); // инициализация DHT temperature = dht.readTemperature(); // считывание температуры // humidity = dht.readHumidity(); // влажность не читаем, датчик врёт return; } // ---------------------------------------------------------------------------- // Чтение показаний сенсора void dht_read() { if (temperature != dht.readTemperature()) { // усреднение показаний для устранения "скачков" if (averageFactor > 0) { // <новое среднее> = (<старое среднее>*4 + <текущее значение>) / фактор сглаживания temperature = (temperature * (averageFactor - 1) + dht.readTemperature()) / averageFactor; } else { temperature = dht.readTemperature(); // не делаем усреднений, что прочитали то и считаем выводом } } /* // влажность не читаем, датчик безбожно врёт if (humidity != dht.readHumidity()) { // усреднение показаний для устранения "скачков" if (averageFactor > 0) { // <новое среднее> = (<старое среднее>*4 + <текущее значение>) / фактор сглаживания humidity = (humidity * (averageFactor - 1) + dht.readHumidity()) / averageFactor; } else { humidity = dht.readHumidity(); // не делаем усреднений, что прочитали то и считаем выводом } } */ return; }esp8266_sender.ino:
#include <ESP8266ZabbixSender.h> ESP8266ZabbixSender zSender; /* WiFi settings */ String ssid = "zopukh@home"; String pass = "zn3GXAj41T"; /* Zabbix host setting */ IPAddress ip(192, 168, 12, 14); // host IP IPAddress gateway(192, 168, 12, 1); // gateway IP IPAddress subnet(255, 255, 255, 0); // network mask /* Zabbix server setting */ #define SERVERADDR 192, 168, 12, 3 // Zabbix server Address #define ZABBIXPORT 10051 // Zabbix server Port #define ZABBIXAGHOST "IOTBOARD_01" // Zabbix item's host name // ---------------------------------------------------------------------------- // Инициализация esp8266 void esp8266_setup() { WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); WiFi.begin(ssid.c_str(), pass.c_str()); WiFi.config(ip, gateway, subnet); while (!checkConnection()) { } zSender.Init(IPAddress(SERVERADDR), ZABBIXPORT, ZABBIXAGHOST); // Init zabbix server information return; } // ---------------------------------------------------------------------------- // Передача данных в Zabbix void esp8266_sendMessage () { checkConnection(); // Check wifi connection zSender.ClearItem(); // Clear ZabbixSender's item list zSender.AddItem("temperature", (float)temperature); // Exmaple value of zabbix trapper item // zSender.AddItem("humidity", (float)humidity); // Exmaple value of zabbix trapper item if (zSender.Send() == EXIT_SUCCESS) { // Send zabbix items Serial.println("ZABBIX SEND: OK"); } else { Serial.println("ZABBIX SEND: NG"); } return; } // ---------------------------------------------------------------------------- // Проверка соединения boolean checkConnection() { int count = 0; Serial.print("Waiting for Wi-Fi connection"); while (count < 300) { if (WiFi.status() == WL_CONNECTED) { Serial.println(); Serial.println("Connected!"); return (true); } delay(500); Serial.print("."); count++; } Serial.println("Timed out."); return false; }Настройки хоста:
Настройки элементов данных:
Графичек:
Немного дёгтя от модулей ESP-01S DHT11 https://ru.aliexpress.com/item/A21-ESP8266-ESP-01-ESP-01S-DHT11/32840839415.html
Дело в том, что на плате расположен линейный стабилизатор 5-3.3V. Радиатором ему служит традиционно фольга печатки. При работе этот стабилизатор даёт тепловую помеху примерно +10°C. После сборки и упаковки в корпус у меня получилось вообще +14°C. То есть, если кто будет собирать, на этом барахле, после сборки необходимо провести "юстировку" и загнать в скетч полученную поправку.
Привет! Всё классно, всё работает. Но есть нюанс, у меня в esp8266 крутятся и другие клиенты http для забора данных с openweather.org, так вот походу твой zclent не закрывает сессию и другой клиент не может коннектится. Хотя может причина в другом, я не большой специалист) можно ли как-то пофиксить?
Похоже, что ZabbixSender не закрывает соединение сам, сваливает эту работу на сервер ;)
Похоже, я пробовал там ставить zclient.stop(); но особо не помогло)
В обычном эзернет-варианте помогало жестко привязываться к сокету, но в ESP я не шарю.
Подскажи хоть как в эзернете делается?
Типа
EthernetClient client(1); client.connect(...);
При этом функция коннекта принудительно делает закрытие соединения, если сокет находится в Established или типа того.
Ок, завтра попробуем, спасибо.
Должен заметить что sadman использует свой собственный проект zabbuino, завязанный на ethernet. Я же способен от силы криво скопипастить пару кусков кода, потому и получилось это поделие с zabbixsender. Правда, пока оно до воплощения в натуре доросло, оно мутировало в mqtt-клиента. Если интересует, выложу код, хотя откровений там ни каких.
ratman
Ну я примерно как и ты, хотя благодаря твоему проекту, я понял как соединить zabbix и esp, а значит есть переспективы для творческого росат в этом направлении. Так что спасибо большое!
Пока про mqtt только слышал и ролики смотрел, но сам не дорос. Но думаю, что на будущее будет интересно узнать, так что выкладывай)
У MQTT есть одна проблема - он не оперирует таймштампами. Т.е. никогда не знаешь к какому моменту времени относится то или иное значение метрики.
Установка MQTT брокера Mosquitto
Оригинал статьи: https://www.8host.com/blog/ustanovka-brokera-soobshhenij-mosquitto-v-centos-7
Пакет предоставляет простые базовые настройки. Запустите его:
Включите сервис mosquitto, чтобы автоматизировать запуск программы:
Теперь нужно проверить настройки по умолчанию. Пакет mosquitto поставляется с MQTT-клиентами командной строки. Используйте один из них, чтобы подписаться на тему.
Флаг –h указывает имя хоста сервера MQTT, -t – тему. После запуска команды на экране не появится вывода, поскольку команда mosquitto_sub ждет получения сообщений. Вернитесь в первый терминал и опубликуйте сообщение:
Команда mosquitto_pub использует те же опции, что и mosquitto_sub, однако в этот раз используется дополнительный флаг –m (он позволяет ввести текст сообщения). Нажмите Enter, и вы увидите в другом терминале MQTT-сообщение hello world.
Добавляем пользовательский параметр MQTT
Сброс ошибок в /dev/null необходим из-за возможности их появления в случае запуска клиента от пользователя, у которого нет собственного домашнего каталога. В противном случае можно получить в довесок к выводу сопроводительное письмо со следующим текстом.
Переходим в панель управления Zabbix.
Каждый ключ соответствует адресу топика с которого нужно взять ту или иную информацию. И сама структура топика просто идеальна для иерархии оборудования или ветвления расположений объектов. Но Вы и так это видите.
Теперь, ради чего это затевалось. Набор железа тот же, что и в стартовом сообщении.
Скетчи:
mqtt_esp8266_20190316_013.ino
/* ************************************************************************************************ ************************************************************************************************ */ long lastMsg = 0; char msg[50]; // int value = 0; uint32_t taskPeriod_01 = 30000; uint32_t taskPeriod_02 = 60000; // ############################################################################ // SETUP void setup() { Serial.begin(115200); setup_wifi(); // client.setServer(mqtt_server, mqtt_port); // client.setCallback(callback); setup_sensor(); return; } // ############################################################################ // LOOP void loop() { // опрос сенсора if (avrTask01(taskPeriod_01)) { read_sensor(); } // if (avrTask02(taskPeriod_02)) { send_message(); } } // ------------------------------------------------------------------------------------------------ // Таймер потока 01 uint8_t avrTask01(uint32_t span) { static uint32_t future = 0; if (millis() < future) return 0; future += span; return 1; } // ------------------------------------------------------------------------------------------------ // Таймер потока 02 uint8_t avrTask02(uint32_t span) { static uint32_t future = 0; if (millis() < future) return 0; future += span; return 1; }dht_sensor.ino
mqtt_sender.ino
В Zabbix создаём элемент данных с ключём "mqtt[/espboard_01/temperature]" и типом "Zabbix агент" и наслаждаемся.
У MQTT есть одна проблема - он не оперирует таймштампами. Т.е. никогда не знаешь к какому моменту времени относится то или иное значение метрики.
P.S. Надо, кстати, запилить, а то неуютно без вачдога.
Да тут и MQTT не нужен, но я же молчу )) Пока этот велосипед используется в пределах LAN для пары неважных метрик - ничего необычного, в целом, замечать не будете. Но при масштабировании системы можете получить неиллюзорную головную боль.
Для меня ценность мониторинга - в синхронизированных по времени значениях метрик. А если температура сегодняшняя, а влажность (или миллис тот же) позавчерашняя - никакого смысла в таком мониторинге нет, считаю.
Когда запихиваете метрику через траппер - получаете актуальное значение. Когда через посредника, который может отдать метрику через час - получаете просто какие-то числа.
А откуда берётся уверенность, что данные полученные траппером актуальны, а не "пролежали на полке" месяц? Если они вообще имеют осмысленное значение? =)
На которой полке? Вы датчик прочитали, сендером в траппер пихнули. Zabbix поставил на них таймштамп получения. Т.е. данные актуальны в пределах времени, затраченного на отправку. Если траппер не ответил или не принял значения метрики - данные не попали в мониторинг вообще. В следующую отправку попадут уже новые, актуализированные значения.
В случае с MQTT Zabbix при подключении, осуществленном в роли клиента, получит значения метрики, попавшие на MQTT неведомо когда и хранимые неизвестно сколько. Ведь данные на MQTT хранятся (при определенном QOS) ровно до того момента, когда их заберут. Т.е., при проблемах в канале, вероятность получить от MQTT числа, которые ESP положила туда позавчера, стремится к 100%. А при попадании в Zabbix они будут иметь таймштамп времени забора с MQTT (удачного коннекта). И что в итоге ? На графике видим вчерашнюю жару, которая как будто началась три минуты назад. А коли еще триггеров и экшнов навесите на метрики, то получите вовсе веселые последствия.
Так! Устройство сбора информации по своему графику датчик опросило, данные получило, приклеило к ним свой таймштамп, отправило брокеру. Zabbix по своему графику брокера опросил, получил данные и таймштамп, сверил таймштамп с предыдущим, если не сошлось, то данные свежие, если сошлось, то источник данных мёртв. Да, получаем задержку между публикацией и чтением, но опять же всего лиш. На "последнем метре" (от монитора до стула) задержка будет на порядки больше.
И, раз уж на то пошло, то тогда по SNMP что-то вообще не имеет смысла опрашивать, там-то точно записи обновляются как производителю захотелось.
Покажите-ка мне в коде где к humidity цепляется таймштамп и как заббикс его оттуда получает... Может я что-то пропустил.
Ну, как вариант:
void send_message() { if (!client.connected()) { reconnect(); } client.loop(); snprintf (tpcBuffer, 50, "/%s/temperature", BOARD_NAME); snprintf (msgBuffer, 50, "%f", temperature + temperatureCorrection); Serial.print("Publish message: "); Serial.println(msgBuffer); client.publish(tpcBuffer, msgBuffer, true); snprintf (tpcBuffer, 50, "/%s/wdt", BOARD_NAME); snprintf (msgBuffer, 50, "%ld", millis()); Serial.print("Publish message: "); Serial.println(msgBuffer); client.publish(tpcBuffer, msgBuffer, true); return; }На брокере получаем два топика:
Разбор в Zabbikse ключами:
То, что таймштампы Zabbix'а и источника не совпадают, так они в принципе совпадать не могут, каждый живет по своему времени.
В данном случае вы послали две независимые метрики, которые могут придти в мониторинг вразнобой, с разницей в пару периодов опроса и не всегда в одном и том же порядке (вы же знаете, что такое очередь проверок в Zabbix?). Причем - одна из них на вполне законных основаниях может вообще не уйти на MQTT, а туда попадет значение метрики из следующей итерации.
Вы не думайте, что у меня есть цель переубедить в чем-то. Просто вспомните все вышенаписанное когда Zabbix повыключает сервера, получив вчерашнюю температуру в серверной.
ratman, я тут взялся модифицировать своего агента с целью добавления параллельной (но, наврядли, полной) поддержки ESP. Есть интерес поучаствовать в тестинге?
ratman, я тут взялся модифицировать своего агента с целью добавления параллельной (но, наврядли, полной) поддержки ESP. Есть интерес поучаствовать в тестинге?
а я просто в базу пишу без всяких Zabbix и MQTT, в задумках написать скрипт отправляющий СМС, если что-то идёт не так
Любое тестирование помогает. Мой email - zbx.sadman@gmail.com