Передатчик:
Спойлер
Код: Выделить всё
// Transmitter
#include <esp_now.h>
#include <WiFi.h>
#include <esp_sleep.h>
#define THERMOSTAT_PIN 4
#define HEARTBEAT_INTERVAL 300000000 // 5 минут в микросекундах (300 * 10^6)
#define RETRY_COUNT 3
#define RETRY_DELAY 200
RTC_DATA_ATTR static bool lastState;
RTC_DATA_ATTR static unsigned long sendCounter;
RTC_DATA_ATTR static unsigned long failedCounter;
uint8_t receiverMac[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
typedef struct message
{
bool state;
bool heartbeat;
} message;
bool ackReceived = false;
int retries = 0;
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
ackReceived = (status == ESP_NOW_SEND_SUCCESS);
if (status == ESP_NOW_SEND_SUCCESS)
{
sendCounter++;
}
else
{
failedCounter++;
}
}
void sendMessage(bool currentState, bool isHeartbeat)
{
message msg;
msg.state = currentState;
msg.heartbeat = isHeartbeat;
retries = 0;
do
{
ackReceived = false;
esp_err_t result = esp_now_send(receiverMac, (uint8_t *)&msg, sizeof(msg));
if (result == ESP_OK)
{
unsigned long start = millis();
while (!ackReceived && (millis() - start < RETRY_DELAY))
{
delay(10);
}
}
retries++;
} while (!ackReceived && retries < RETRY_COUNT);
}
void setup()
{
pinMode(THERMOSTAT_PIN, INPUT_PULLUP);
WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_register_send_cb(OnDataSent);
esp_now_peer_info_t peerInfo;
memcpy(peerInfo.peer_addr, receiverMac, 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
bool currentState = digitalRead(THERMOSTAT_PIN) == LOW;
bool stateChanged = (currentState != lastState);
if (stateChanged)
{
sendMessage(currentState, false);
}
else
{
sendMessage(currentState, true);
}
lastState = currentState;
esp_sleep_enable_timer_wakeup(HEARTBEAT_INTERVAL);
esp_deep_sleep_start();
}
void loop() {}
Приёмник:
Спойлер
Код: Выделить всё
// Receiver
#include <esp_now.h>
#include <WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define RELAY_PIN 5
#define DS18B20_PIN 6
#define SAFE_TEMP 4.0
#define HYSTERESIS 1.0
#define CONFIRMATION_TIMEOUT 200
OneWire oneWire(DS18B20_PIN);
DallasTemperature sensors(&oneWire);
typedef struct message
{
bool state;
bool heartbeat;
} message;
bool lastValidState = false;
unsigned long lastMsgTime = 0;
const unsigned long TIMEOUT = 600000; // 10 минут таймаут
void sendConfirmation()
{
uint8_t ff[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
esp_now_send(ff, nullptr, 0);
}
void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len)
{
if (len == sizeof(message))
{
message msg;
memcpy(&msg, data, len);
if (!msg.heartbeat)
{
digitalWrite(RELAY_PIN, msg.state);
lastValidState = msg.state;
}
lastMsgTime = millis();
sendConfirmation();
}
}
void setup()
{
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
sensors.begin();
WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_register_recv_cb(OnDataRecv);
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, "\xFF\xFF\xFF\xFF\xFF\xFF", 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
}
void emergencyMode()
{
sensors.requestTemperatures();
float temp = sensors.getTempCByIndex(0);
static bool heating = false;
if (heating && temp >= SAFE_TEMP + HYSTERESIS)
{
heating = false;
}
else if (!heating && temp <= SAFE_TEMP)
{
heating = true;
}
digitalWrite(RELAY_PIN, !heating);
}
void loop()
{
if (millis() - lastMsgTime > TIMEOUT)
{
emergencyMode();
}
else
{
digitalWrite(RELAY_PIN, lastValidState);
}
delay(5000 + random(0, 1000));
}
Словами
Спойлер
Передатчик:Просыпается каждые 5 минут
Отправляет сообщение ВСЕГДА при пробуждении:
isHeartbeat = true если состояние не изменилось
isHeartbeat = false если состояние изменилось
3 попытки отправки с подтверждением
Глубокий сон между отправками
Приемник:
Реагирует ТОЛЬКО на сообщения с isHeartbeat = false
Обновляет таймер при получении ЛЮБОГО сообщения
Таймаут 10 минут для активации аварийного режима
Автоматическая обработка переполнения millis()
Проверка состояния каждую секунду
Подожду ваших реальных тестов, может какие добавления понадобятся.