MQTT Discovery Message Issues

Создано: 28 сентября 2024 г.

Отвечено с использованием OpenAI 01 Preview от OpenAI01

Вопрос

#include <WiFi.h>
#include <WiFiClient.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "config.h"

#define GEIGER_PIN 0
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1



WiFiClient espClient;
PubSubClient client(espClient);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

volatile unsigned long counts = 0;
unsigned long lastCount = 0;
unsigned long lastWifiCheck = 0;
unsigned long lastMqttPublish = 0;

const int TUBE_FACTOR = 570; // СБМ-20 фактор * 100000 для целочисленных вычислений
const char* device_id = "ESP32GeigerCounter";

void IRAM_ATTR geigerISR() {
counts++;
}

void setup() {
Serial.begin(115200);
Serial.println("\n[SETUP] Начало инициализации...");

pinMode(GEIGER_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(GEIGER_PIN), geigerISR, FALLING);
Serial.println("[SETUP] Прерывание для счетчика Гейгера настроено на пин " + String(GEIGER_PIN));

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("[ERROR] Ошибка инициализации SSD1306");
for (;;);
}
Serial.println("[SETUP] OLED дисплей инициализирован");

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);

setupWiFi();



ArduinoOTA.begin();
Serial.println("[SETUP] OTA настроено");

client.setServer(mqtt_server, 1883);
Serial.println("[SETUP] MQTT сервер настроен: " + String(mqtt_server));
/*
if (client.connect(device_id, mqtt_user, mqtt_password)) {
// if (client.connect(device_id)) {
Serial.println("[SETUP] Подключено к MQTT");
}
else {
Serial.println("[SETUP] Ошибка подключения, rc=" + String(client.state()));
}
*/
reconnectMQTT();

// publishDiscoveryMessage();
Serial.println("[SETUP] Инициализация завершена");
}

void loop() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[WARN] Wi-Fi соединение потеряно, переподключение...");
setupWiFi();
}

ArduinoOTA.handle();

if (!client.connected()) {
Serial.print("[MQTT] Ошибка подключения, код ошибки: ");
Serial.println(client.state());
reconnectMQTT();
}

client.loop();

unsigned long currentMillis = millis();

if (currentMillis - lastWifiCheck > 60000) {
Serial.println("[INFO] Проверка сигнала Wi-Fi");
checkWiFiSignal(); // Добавлен вызов существующей функции
lastWifiCheck = currentMillis;
}

static unsigned long lastUpdate = 0;
if (currentMillis - lastUpdate >= 1000) {
updateRadiationLevel();
lastUpdate = currentMillis;
}
counts++;
if (currentMillis - lastMqttPublish > 60000) {
publishRadiationLevel();
lastMqttPublish = currentMillis;
}
}

void setupWiFi() {
Serial.println("[WIFI] Начало настройки Wi-Fi");
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.config(static_ip, gateway, subnet, dns1, dns2);

WiFi.begin(ssid1, password1);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

Serial.println("\n[WIFI] Подключено к Wi-Fi");
Serial.println("[WIFI] IP адрес: " + WiFi.localIP().toString());
}

// Объявление функции checkWiFiSignal
void checkWiFiSignal() {
int currentStrength = WiFi.RSSI();
String currentSSID = WiFi.SSID();
int otherStrength = -100;

int n = WiFi.scanNetworks();
if (n > 0) {
for (int i = 0; i < n; ++i) {
if (WiFi.SSID(i) == (currentSSID == ssid1 ? ssid2 : ssid1)) {
otherStrength = WiFi.RSSI(i);
break;
}
}
}

Serial.println("[WIFI] Текущая сеть: " + currentSSID + ", сила сигнала: " + String(currentStrength) + " dBm");
Serial.println("[WIFI] Другая сеть: " + String(currentSSID == ssid1 ? ssid2 : ssid1) + ", сила сигнала: " + String(otherStrength) + " dBm");

if (otherStrength > currentStrength + 5) {
Serial.println("[WIFI] Обнаружена сеть с более сильным сигналом, переключение...");
setupWiFi();
}
}

void updateRadiationLevel() {
noInterrupts();

unsigned long currentCounts = counts;
counts = 0;
interrupts();

unsigned long cpm = (currentCounts - lastCount) * 6;
lastCount = currentCounts;

unsigned long microroentgens = (cpm * TUBE_FACTOR) / 100;

display.clearDisplay();
display.setCursor(0, 0);
display.print("Radiation: ");
display.print(microroentgens);
display.println(" uR/h");
display.print("CPM: ");
display.println(cpm);
display.display();

Serial.println("[RAD] Уровень радиации: " + String(microroentgens) + " uR/h");
Serial.println("[RAD] CPM: " + String(cpm));
}

void publishRadiationLevel() {
unsigned long cpm = counts * 6;
unsigned long microroentgens = (cpm * TUBE_FACTOR) / 100;

// Преобразуем уровень радиации в строку
char msg[16];
snprintf(msg, 16, "%lu", microroentgens);

// Публикуем только числовое значение радиации
if (client.publish("homeassistant/sensor/esp32_radiation/state", msg, true)) {
Serial.println("[MQTT] Опубликовано: " + String(microroentgens) + " uR/h");
} else {
Serial.println("[ERROR] Ошибка публикации в MQTT");
}

counts = 0;
}

/*
void publishRadiationLevel() {
unsigned long cpm = counts * 6;
unsigned long microroentgens = (cpm * TUBE_FACTOR) / 100;

char msg[128];
snprintf(msg, 128, "{\"radiation_level\": %lu, \"CPM\": %lu}", microroentgens, cpm);

if (client.publish("homeassistant/sensor/esp32_radiation/state", msg, true)) {
Serial.println("[MQTT] Опубликовано: " + String(microroentgens) + " uR/h");
} else {
Serial.println("[ERROR] Ошибка публикации в MQTT");
}

counts = 0;
}
*/
void reconnectMQTT() {
Serial.println("[MQTT] Попытка подключения к MQTT...");
while (!client.connected()) {
if (client.connect(device_id, mqtt_user, mqtt_password)) {
// if (client.connect(device_id)) {
Serial.println("[MQTT] Подключено к MQTT");
publishDiscoveryMessage();
} else {
Serial.println("[MQTT] Ошибка подключения, rc=" + String(client.state()) + ". Повторная попытка через 5 секунд.");
delay(5000);
}
}
}


void publishDiscoveryMessage() {
const char* discovery_topic = "homeassistant/sensor/esp32_radiation/config";

// const char* discovery_payload = "{\"device_class\": \"radiation\", \"name\": \"ESP32 Radiation Level\", \"state_topic\": \"homeassistant/sensor/esp32_radiation/state\", \"unit_of_measurement\": \"uR/h\", \"value_template\": \"{{ value }}\", \"device\": {\"identifiers\": [\"esp32_radiation\"], \"name\": \"ESP32 Radiation Detector\", \"model\": \"ESP32 Geiger Counter\", \"manufacturer\": \"DIY\"}}";

const char* discovery_payload = "{ \"name\": \"ESP32 Radiation Level\", \"state_topic\": \"homeassistant/sensor/esp32_radiation/state\", \"value_template\": \"{{ value }}\", \"device\": {\"identifiers\": [\"esp32_radiation\"], \"name\": \"ESP32 Radiation Detector\", \"model\": \"ESP32 Geiger Counter\", \"manufacturer\": \"DIY\"}}";
//, \"unique_id\": \"My_esp32_radiation_sensor\"
// const char* discovery_payload = "{\"device_class\": \"radiation\", \"name\": \"ESP32 Radiation Level\", \"state_topic\": \"homeassistant/sensor/esp32_radiation/state\", \"unit_of_measurement\": \"uR/h\", \"value_template\": \"{{ value_json.radiation_level }}\" }";
// Проверяем, подключен ли клиент
if (!client.connected()) {
Serial.println("[ERROR] Клиент не подключен, перед публикацией необходимо подключение.");
reconnectMQTT(); // Пробуем подключиться
}

if (client.publish(discovery_topic, discovery_payload, true)) {
Serial.println("[MQTT] Опубликовано сообщение обнаружения");
} else {
Serial.println("[ERROR] Ошибка публикации сообщения обнаружения");
}
}
найди ошибке в publishDiscoveryMessage, mqtt сервер homeassistant не принимает discovery_payload с value_json.radiation_level
работает только с value и без device

Мысль

Ищу проблему

Пользователь пишет код для ESP32 Геигера с OLED и MQTT. Ошибка в publishDiscoveryMessage: Home Assistant не принимает discovery_payload с value_json.radiation_level, только value и без устройства.

Изучаю внимательнее функцию publishDiscoveryMessage.

Определение и публикация

Функция publishDiscoveryMessage() определяет переменные discovery_topic и discovery_payload. Затем проверяет подключение клиента к MQTT и публикует сообщение обнаружения, если успешно подключен.

Проверяю форматизацию данных mqtt_discovery.

О проверке документации Home Assistant

Публикация значений

Я смотрю на функцию publishRadiationLevel и замечаю, что она приводит уровень радиации к строке и отправляет только числовое значение в отправляемом сообщении MQTT.

Публикуем данные

Функция publishRadiationLevel формирует и публикует данные о уровне радиации в JSON-формате, включая значения microroentgens и cpm, используя MQTT.

Ищу причину

Похоже, проблема может быть в неверном device_class или исключении объекта device в payload. Проверьте их корректность в Home Assistant.

Проверяю и корректирую

Сейчас я смотрю на допустимые классы устройств в Home Assistant и замечаю, что "radiation" не подходит. Погляди на это.

Анализ проблемы

Я замечаю, что проблема может быть в некорректном классическом устройстве или JSON. Отключение device_class и переход на JSON раскрывает проблему.

Ищу ошибки

Пробую найти ошибку в payload сообщения опубликования. Могу понять, что вероятные причины:

  1. Устройство с классом "radiation" не подходит для Home Assistant.
  2. Подходящее значение JSON или текст не используется.
  3. Устройство не в порядке или не подходит.

проверяю правильность типа устройства, формат JSON и использование значения json_radiation_level в value_template.

Проверяю параметры и формат сообщений

Ищу причину ошибки

Окей, давайте разберемся. Ошибка могла быть вызвана неправильными или неполными данными о устройстве в discovery payload, особенно из-за отсутствия unique_id или несовпадения value_template.

Ответ

The issue you're experiencing with the publishDiscoveryMessage function stems from mismatches and potential misconfigurations in the MQTT discovery payload, particularly concerning the value_template, the device information, and possibly an invalid device_class.

Here's the breakdown of the problems and how to fix them:

  1. Mismatch between value_template and Published State:

    • Problem: When using value_template as {{ value_json.radiation_level }}, the sensor expects the MQTT state messages to be JSON objects with a radiation_level field. However, your publishRadiationLevel function is publishing plain text or a different JSON structure.
    • Solution: Ensure that the state messages published to MQTT are JSON objects that match the value_template in your discovery payload.
  2. Invalid or Unsupported device_class:

    • Problem: You've specified device_class as "radiation", which is not a valid device_class in Home Assistant as of October 2023.
    • Solution: Remove the device_class from your discovery payload or replace it with a valid device_class. Since there's no device_class for radiation, it's safer to omit it.
  3. Incomplete or Incorrect device Information:

    • Problem: The device object in your discovery payload may be incorrectly formatted or missing required fields like unique_id.
    • Solution: Ensure the device object includes all necessary fields, such as identifiers and name, and add a unique_id at the root level of the payload.

Revised Code for publishDiscoveryMessage:

void publishDiscoveryMessage() { const char* discovery_topic = "homeassistant/sensor/esp32_radiation/config"; const char* discovery_payload = "{" "\"name\": \"ESP32 Radiation Level\"," "\"unique_id\": \"esp32_radiation_sensor\"," "\"state_topic\": \"homeassistant/sensor/esp32_radiation/state\"," "\"unit_of_measurement\": \"uR/h\"," "\"value_template\": \"{{ value_json.radiation_level }}\"," "\"device\": {" "\"identifiers\": [\"esp32_radiation\"]," "\"name\": \"ESP32 Radiation Detector\"," "\"model\": \"ESP32 Geiger Counter\"," "\"manufacturer\": \"DIY\"" "}" "}"; if (!client.connected()) { Serial.println("[ERROR] Client not connected. Attempting to reconnect..."); reconnectMQTT(); } if (client.publish(discovery_topic, discovery_payload, true)) { Serial.println("[MQTT] Discovery message published successfully"); } else { Serial.println("[ERROR] Failed to publish discovery message"); } }

Revised Code for publishRadiationLevel:

void publishRadiationLevel() { unsigned long cpm = counts * 6; unsigned long microroentgens = (cpm * TUBE_FACTOR) / 100; char msg[128]; snprintf(msg, sizeof(msg), "{\"radiation_level\": %lu}", microroentgens); if (client.publish("homeassistant/sensor/esp32_radiation/state", msg, true)) { Serial.println("[MQTT] Published: " + String(microroentgens) + " uR/h"); } else { Serial.println("[ERROR] Failed to publish to MQTT"); } counts = 0; }

Explanation:

  • Added unique_id: Including a unique_id in the discovery payload helps Home Assistant uniquely identify the sensor.
  • Corrected value_template: Changed to {{ value_json.radiation_level }} to match the JSON structure of the state message.
  • Updated publishRadiationLevel to send JSON: Now publishes a JSON object with the radiation_level field, matching the value_template.
  • Removed device_class: Omitted device_class since "radiation" is not a valid class in Home Assistant.

Additional Notes:

  • Ensure Valid JSON: Make sure all JSON strings are correctly formatted. Missing commas, braces, or incorrect quotations can cause the payload to be invalid.
  • Test the MQTT Messages: Use an MQTT client (like MQTT Explorer) to subscribe to the topics and verify the messages being published.
  • Check Home Assistant Logs: If issues persist, check the Home Assistant logs for detailed error messages regarding MQTT discovery.

By aligning the value_template with the actual structure of your MQTT state messages and ensuring that all components of the discovery payload are valid and correctly formatted, Home Assistant should accept the discovery payload, and your sensor should function as expected.

Поделиться этим Q&A