По фото: у нас два одинаковых 2-канальных релейных модуля (не один) — второй закрывает fan-1 по-настоящему (GPIO18), больше не "пока не куплено". Добавлен soil-1: компараторный модуль почвенной влажности, читаем A0 через ADC (GPIO34, input-only), публикуем sensor_type=soil_moisture в процентах. Raw->% калибруется через SOIL_RAW_DRY/SOIL_RAW_WET в config.h — заводские значения приблизительные, точную калибровку раньше сделать было нельзя (не было физического доступа к датчику), нужно измерить на месте. soil-1 в Postgres/Laravel платформы ещё не заведён (не трогаем home_automatization) — телеметрия будет идти в ClickHouse, но в Laravel UI устройство не появится до отдельной регистрации device_type/devices-записи. Веб-интерфейс и /api/state дополнены полем "soil".
93 lines
2.8 KiB
C++
93 lines
2.8 KiB
C++
#include "telemetry.h"
|
|
|
|
#include <DHT.h>
|
|
|
|
#include "config.h"
|
|
#include "mqtt_link.h"
|
|
#include "time_sync.h"
|
|
|
|
static DHT dht(PIN_DHT22, DHT22);
|
|
static unsigned long lastPublish = 0;
|
|
|
|
static bool hasReading = false;
|
|
static float lastTemperature = 0;
|
|
static float lastHumidity = 0;
|
|
|
|
static bool hasSoilReading = false;
|
|
static float lastSoilPercent = 0;
|
|
|
|
void telemetryBegin() {
|
|
dht.begin();
|
|
}
|
|
|
|
// Компараторный модуль почвенной влажности отдаёт на A0 сырое ADC-значение,
|
|
// которое УМЕНЬШАЕТСЯ с ростом влажности (суше — выше сопротивление датчика
|
|
// — выше напряжение на A0). SOIL_RAW_DRY/SOIL_RAW_WET в config.h — заводские
|
|
// значения-плейсхолдеры, откалибровать по месту (замерить raw на воздухе и
|
|
// в воде) через Serial-лог этой функции.
|
|
static float rawToSoilPercent(int raw) {
|
|
long percent = map(raw, SOIL_RAW_DRY, SOIL_RAW_WET, 0, 100);
|
|
if (percent < 0) percent = 0;
|
|
if (percent > 100) percent = 100;
|
|
return (float)percent;
|
|
}
|
|
|
|
void telemetryTick() {
|
|
unsigned long now = millis();
|
|
if (now - lastPublish < TELEMETRY_INTERVAL_MS && lastPublish != 0) {
|
|
return;
|
|
}
|
|
// Без синхронизированного времени ingest-service отклонит timestamp
|
|
// (или он будет ложным), поэтому просто пропускаем цикл и ждём.
|
|
if (!timeSyncIsReady()) {
|
|
return;
|
|
}
|
|
lastPublish = now;
|
|
|
|
float humidity = dht.readHumidity();
|
|
float temperature = dht.readTemperature();
|
|
if (isnan(humidity) || isnan(temperature)) {
|
|
Serial.println("[telemetry] DHT22 read failed, skipping this sensor this cycle");
|
|
} else {
|
|
lastTemperature = temperature;
|
|
lastHumidity = humidity;
|
|
hasReading = true;
|
|
|
|
mqttPublishTelemetry(DEVICE_ID_SENSOR, "temperature", temperature);
|
|
mqttPublishTelemetry(DEVICE_ID_SENSOR, "humidity", humidity);
|
|
|
|
Serial.print("[telemetry] temperature=");
|
|
Serial.print(temperature);
|
|
Serial.print(" humidity=");
|
|
Serial.println(humidity);
|
|
}
|
|
|
|
int soilRaw = analogRead(PIN_SOIL_MOISTURE);
|
|
lastSoilPercent = rawToSoilPercent(soilRaw);
|
|
hasSoilReading = true;
|
|
mqttPublishTelemetry(DEVICE_ID_SOIL, "soil_moisture", lastSoilPercent);
|
|
|
|
Serial.print("[telemetry] soil_moisture=");
|
|
Serial.print(lastSoilPercent);
|
|
Serial.print("% (raw=");
|
|
Serial.print(soilRaw);
|
|
Serial.println(")");
|
|
}
|
|
|
|
bool telemetryGetLast(float& temperature, float& humidity) {
|
|
if (!hasReading) {
|
|
return false;
|
|
}
|
|
temperature = lastTemperature;
|
|
humidity = lastHumidity;
|
|
return true;
|
|
}
|
|
|
|
bool telemetryGetLastSoil(float& percent) {
|
|
if (!hasSoilReading) {
|
|
return false;
|
|
}
|
|
percent = lastSoilPercent;
|
|
return true;
|
|
}
|