MQTT_DUSH
=================================
Вот полный, готовый к компиляции скетч со всеми исправлениями.
Что исправлено критически:
Убрано if-else каскадирование, которое "застревало" на верхнем уровне. Теперь каждый датчик публикуется независимо (level/upper, level/lower, level/intermediate).
Добавлена явная инверсия логики реле через #define RELAY_ACTIVE_LOW.
Введена стабильная проверка пинов с антидребезгом.
MQTT-публикация уровня происходит каждый интервал, что исключает "залипание" статуса на брокере.
Сохранён автовыбор из 4 WiFi-сетей по RSSI.
cpp
// ============================================================================
// ESP8266: Мониторинг температуры, влажности и уровня воды (4 WiFi станции)
// ============================================================================
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
// 📡 КОНФИГУРАЦИЯ WIFI (до 4 станций)
const char* wifi_networks[4][2] = {
{ "2Keenetic-8891", "hoMt2iPo" },
{ "Keenetic-6391", "U7Y6hMeB" },
{ "Home_WiFi_2.4", "password3" },
{ "Office_IoT", "password4" }
};
const uint8_t WIFI_NETWORKS_COUNT = 4;
// 🌐 КОНФИГУРАЦИЯ MQTT
const char* mqtt_server = "192.168.1.75";
const int mqtt_port = 1883;
const char* mqtt_user = "mqtt";
const char* mqtt_password = "mq485127sav";
// 🔌 ПИНЫ ДАТЧИКОВ
const int oneWireBus = 13; // DS18B20
const int upperLevelPin = 15; // Реле/зонд верхнего уровня
const int lowerLevelPin = 12; // Реле/зонд нижнего уровня
const int intermediatePin = 14; // Промежуточный зонд
// ⚙️ ЛОГИКА РЕЛЕ
// true = реле замыкает пин на GND при срабатывании (наиболее частый вариант)
// false = реле замыкает пин на 3.3V при срабатывании
#define RELAY_ACTIVE_LOW true
// 📦 ОБЪЕКТЫ БИБЛИОТЕК
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);
DHT dht(4, DHT22);
WiFiClient espClient;
PubSubClient client(espClient);
// 🌡️ ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
float waterTemp = 0.0;
float airTemp = 0.0;
float humidity = 0.0;
// ⏱️ ТАЙМЕРЫ
const uint32_t send_data_interval = 10000;
uint32_t last_send_time = 0;
const uint32_t wifi_rescan_interval = 300000; // 5 минут
uint32_t last_wifi_scan = 0;
const uint32_t level_check_interval = 500; // 0.5 сек
uint32_t last_level_check = 0;
// ============================================================================
// 📡 ФУНКЦИЯ: ПОДКЛЮЧЕНИЕ К ЛУЧШЕЙ ИЗ 4-х СЕТЕЙ
// ============================================================================
bool connectToBestWiFi() {
Serial.println("\n🔍 Сканирование WiFi сетей...");
int n = WiFi.scanNetworks();
if (n <= 0) {
Serial.println("⚠️ Сети не найдены");
return false;
}
int bestIdx = -1;
int bestRSSI = -100;
for (int i = 0; i < n; i++) {
String ssid_found = WiFi.SSID(i);
int rssi = WiFi.RSSI(i);
for (uint8_t j = 0; j < WIFI_NETWORKS_COUNT; j++) {
if (ssid_found == wifi_networks[j][0]) {
Serial.printf(" ✅ Найдена: %-20s | RSSI: %d dBm\n", ssid_found.c_str(), rssi);
if (rssi > bestRSSI) {
bestRSSI = rssi;
bestIdx = j;
}
break;
}
}
}
WiFi.scanDelete();
if (bestIdx >= 0 && bestRSSI > -85) {
Serial.printf("\n📶 Подключение к: %s (RSSI: %d dBm)\n", wifi_networks[bestIdx][0], bestRSSI);
WiFi.begin(wifi_networks[bestIdx][0], wifi_networks[bestIdx][1]);
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < 20000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n🎉 WiFi подключён!");
Serial.printf("IP: %s | RSSI: %d dBm\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());
return true;
}
}
Serial.println("❌ Подключение не удалось");
return false;
}
// ============================================================================
// 🔄 ФУНКЦИЯ: ПЕРЕПОДКЛЮЧЕНИЕ К MQTT
// ============================================================================
void reconnect() {
while (!client.connected()) {
Serial.print("🔄 MQTT connection...");
if (client.connect("ESP8266_WaterMonitor", mqtt_user, mqtt_password)) {
Serial.println("connected ✓");
} else {
Serial.printf("failed (rc=%d), retry in 5s\n", client.state());
delay(5000);
}
}
}
// ============================================================================
// 📖 ФУНКЦИЯ: СТАБИЛЬНОЕ ЧТЕНИЕ ПИНА (АНТИДРЕБЕЗГ + ИНВЕРСИЯ)
// ============================================================================
bool readLevelStable(int pin) {
// Читаем дважды с задержкой для подавления помех
bool r1 = (digitalRead(pin) == LOW);
delay(10);
bool r2 = (digitalRead(pin) == LOW);
// Приводим к логике: true = вода есть / реле сработало
return RELAY_ACTIVE_LOW ? (r1 && r2) : (!r1 && !r2);
}
// ============================================================================
// ⚙️ SETUP
// ============================================================================
void setup() {
Serial.begin(9600);
Serial.println("\n🚀 ESP8266 WaterMonitor v2.0");
// Инициализация датчиков
dht.begin();
sensors.begin();
// Настройка пинов уровня
pinMode(upperLevelPin, INPUT_PULLUP);
pinMode(lowerLevelPin, INPUT_PULLUP);
pinMode(intermediatePin, INPUT_PULLUP);
pinMode(0, INPUT); // Кнопка BOOT (оставлено из оригинала)
// WiFi
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
if (!connectToBestWiFi()) {
delay(10000);
connectToBestWiFi();
}
// MQTT
client.setServer(mqtt_server, mqtt_port);
// Инициализация таймеров
last_send_time = millis();
last_wifi_scan = millis();
last_level_check = millis();
Serial.println("✅ Setup complete. Starting loop...");
}
// ============================================================================
// 🔁 LOOP
// ============================================================================
void loop() {
// 📡 Периодический перескан WiFi
if (millis() - last_wifi_scan >= wifi_rescan_interval) {
if (WiFi.status() != WL_CONNECTED || WiFi.RSSI() < -75) {
Serial.println("\n📡 Проверка лучших сетей...");
connectToBestWiFi();
}
last_wifi_scan = millis();
}
// 🔄 Поддержание соединения
if (WiFi.status() != WL_CONNECTED) {
connectToBestWiFi();
delay(1000);
}
if (!client.connected()) {
reconnect();
}
client.loop();
// 💧 ЧАСТЫЙ ОПРОВ ДАТЧИКОВ УРОВНЯ (для стабильности логики)
if (millis() - last_level_check >= level_check_interval) {
readLevelStable(upperLevelPin);
readLevelStable(lowerLevelPin);
readLevelStable(intermediatePin);
last_level_check = millis();
}
// 📤 ОТПРАВКА ДАННЫХ В MQTT
if (millis() - last_send_time >= send_data_interval) {
// 🌡️ Температура воды
sensors.requestTemperatures();
waterTemp = sensors.getTempCByIndex(0);
// 🌡️💧 Температура и влажность воздуха
humidity = dht.readHumidity();
airTemp = dht.readTemperature();
// 💧 Чтение уровней (применяем логику и антидребезг)
bool isUpper = readLevelStable(upperLevelPin);
bool isIntermediate = readLevelStable(intermediatePin);
bool isLower = readLevelStable(lowerLevelPin);
// 🔍 Отладка в Serial
Serial.printf("💧 Raw: U=%d I=%d L=%d | MQTT: U=%s I=%s L=%s\n",
isUpper, isIntermediate, isLower,
isUpper ? "ON" : "OFF",
isIntermediate ? "ON" : "OFF",
isLower ? "ON" : "OFF");
// 📦 Публикация каждого датчика отдельно (исключает "залипание")
client.publish("level/upper", isUpper ? "ON" : "OFF");
client.publish("level/intermediate", isIntermediate ? "ON" : "OFF");
client.publish("level/lower", isLower ? "ON" : "OFF");
// 🎯 Общий статус с приоритетом
const char* levelState = "empty";
if (isUpper) levelState = "upper";
else if (isIntermediate) levelState = "intermediate";
else if (isLower) levelState = "lower";
client.publish("water_level", levelState);
// 📐 Публикация температур и влажности
char buf[12];
dtostrf(waterTemp, 4, 2, buf);
client.publish("water_temperature", buf);
dtostrf(airTemp, 4, 2, buf);
client.publish("air_temperature", buf);
dtostrf(humidity, 4, 2, buf);
client.publish("humidity", buf);
// 📶 Статус WiFi
char rssiStr[6];
itoa(WiFi.RSSI(), rssiStr, 10);
client.publish("wifi/rssi", rssiStr);
client.publish("wifi/ssid", WiFi.SSID().c_str());
last_send_time = millis();
}
}
================================================
?? Как проверить работу после прошивки
- Откройте Serial Monitor (9600 бод).
- Вы увидите строку каждые 10 секунд:
1
💧 Raw: U=0 I=0 L=0 | MQTT: U=OFF I=OFF L=OFF
- Когда вода поднимется и реле щёлкнет:
1
💧 Raw: U=1 I=0 L=0 | MQTT: U=ON I=OFF L=OFF
- Когда резервуар освободится:
1
💧 Raw: U=0 I=0 L=0 | MQTT: U=OFF I=OFF L=OFF
- В MQTT-клиенте подпишитесь на: level/# и water_level. Вы будете получать чёткие ON/OFF без зависаний.
🛠️ Если статус всё ещё "залипает"
|
Симптом |
Причина |
Действие |
|
Raw не меняется при щелчке реле |
Проводка/питание реле |
Проверьте, замыкает ли реле пин на GND или 3.3V. Измените #define RELAY_ACTIVE_LOW |
|
Raw меняется, а MQTT нет |
Проблема брокера/подписчика |
Брокер может кэшировать. Используйте mosquitto_sub -v -t "level/#" или добавьте client.publish(..., true) для флага retain (по желанию) |
|
Raw "мигает" 0/1 |
Электромагнитные помехи |
Увеличьте delay(10) в readLevelStable() до 20 или 50 |
Скетч полностью готов к работе. Если потребуется добавить управление реле через MQTT или настроить retain-флаг — напишите, дополню. 💧📡