Termostat plus drugi wemos wykonawczy

maniek
Posts: 28
Joined: Sun Oct 15, 2017 7:21 pm
Location: Góra

Post

Witam. Wrzucam dla kogoś kto tak jak ja będzie potrzebował sterować zaworem lub podobnym ale brak przewodu między np. salon --- rozdzielacz podłogówka itp. (mieszkanie po generalnym remoncie i nie chcę niszczyć :D ) Na wemosa wgrany kod termostat plus ESP_now który przesyła o stanie pinu LOW/HIGH na drugiego wemosa, a ten steruje już przekaźnikiem. Od kilku dni działa a teraz dało by rade dołożyć wyświetlacz oled? może ktoś pomoże.


Code: Select all

#include <supla/sensor/DS18B20.h>
// ESP-NOW
#include <ESP8266WiFi.h>
extern "C" {
  #include <espnow.h>
}

#define CFG_BUTTON_GPIO 0
#define STATUS_LED_GPIO 2
#define LED_RED_GPIO 13
#define LED_BLUE_GPIO 12
#define OUTPUT_GPIO 14 
#define INPUT_GPIO 12  //GPIO do sprawdzania stanu
#define DS18B20_GPIO 4

const char* ssid = "xxxxxxxx";
const char* password = "0123456789";

 uint8_t broadcastAddress[] = {0xC8, 0xC9, 0xA3, 0x3A, 0xEA, 0xB3};  // Adres MAC drugiego Wemosa
  

  // Struktura danych do wysyłania
   typedef struct struct_message 
   {
    bool relayState; // Przechowuje stan GPIO14
}    struct_message;

    struct_message relayData;

 
    
    void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
    Serial.print("Wysyłanie ESP-NOW: ");
    Serial.println(sendStatus == 0 ? "Sukces" : "Błąd");
    
}

Supla::ESPWifi wifi;
Supla::Eeprom eeprom;
Supla::LittleFsConfig configSupla;
Supla::Device::StatusLed statusLed(STATUS_LED_GPIO, true);
Supla::EspWebServer suplaServer;

void setup() {
    Serial.begin(115200);
    
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    Serial.println("\nPołączono z WiFi!");

   // Pobierz aktualny kanał WiFi
   int wifiChannel = WiFi.channel();
   Serial.print("Kanał WiFi: ");
   Serial.println(wifiChannel);

// Ustawienie ESP-NOW na ten sam kanał
   //esp_wifi_set_channel(wifiChannel, WIFI_SECOND_CHAN_NONE);

   
    // Supla
    new Supla::Clock;
    new Supla::Html::DeviceInfo(&SuplaDevice);
    new Supla::Html::WifiParameters;
    new Supla::Html::ProtocolParameters;
    new Supla::Html::StatusLedParameters;
    new Supla::Html::TimeParameters(&SuplaDevice);

    new Supla::Device::StatusLed(STATUS_LED_GPIO, true);
    eeprom.setStateSavePeriod(5000);

    auto output = new Supla::Control::InternalPinOutput(OUTPUT_GPIO);
    auto redStatusLed = new Supla::Control::InternalPinOutput(LED_RED_GPIO, false);
    auto blueStatusLed = new Supla::Control::InternalPinOutput(LED_BLUE_GPIO, false);

    auto hvac = new Supla::Control::HvacBase(output);
    new Supla::Html::HvacParameters(hvac);

    DeviceAddress ds1addr = {0x28, 0x97, 0xD7, 0x06, 0x0A, 0x00, 0x00, 0x46};
    DeviceAddress ds2addr = {0x28, 0xFF, 0x54, 0x73, 0x6E, 0x18, 0x01, 0x77};
    new Supla::Sensor::DS18B20(DS18B20_GPIO, ds1addr);
    new Supla::Sensor::DS18B20(DS18B20_GPIO, ds2addr);

    hvac->setMainThermometerChannelNo(1);
    hvac->setAuxThermometerChannelNo(2);
    hvac->setTemperatureHisteresisMin(20);
    hvac->setTemperatureHisteresisMax(1000);
    hvac->setTemperatureAuxMin(500);
    hvac->setTemperatureAuxMax(7500);
    hvac->setTemperatureHisteresis(40);

    auto cfgButton = new Supla::Control::Button(CFG_BUTTON_GPIO, true, true);
    cfgButton->configureAsConfigButton(&SuplaDevice);

    SuplaDevice.setSuplaCACert(suplaCACert);
    SuplaDevice.setSupla3rdPartyCACert(supla3rdCACert);
    SuplaDevice.setName("Termostat ESP");
    SuplaDevice.begin(21);

    // ESP-NOW


// Ustawienie ESP-NOW na ten sam kanał
   //esp_wifi_set_channel(wifiChannel);
    
    WiFi.mode(WIFI_STA);
    if (esp_now_init() != 0) {
        Serial.println("Błąd inicjalizacji ESP-NOW");
        return;
    }

     esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
     
    esp_now_register_send_cb(OnDataSent);
    
    esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

    pinMode(OUTPUT_GPIO, OUTPUT);  //INPUT_GPIO,INPUT
}

void loop() {
    SuplaDevice.iterate();

   relayData.relayState = digitalRead(OUTPUT_GPIO);
    esp_now_send(broadcastAddress, (uint8_t *) &relayData, sizeof(relayData));

    Serial.print("Wysłano stan GPIO14: ");
    Serial.println(relayData.relayState ? "HIGH" : "LOW");

    delay(1000);
}

a tu kod do drugiego wemosa

Code: Select all

  #include <ESP8266WiFi.h>
  extern "C" {
   #include <espnow.h>
  }

  // Definicja pinów
   #define LED_BUILTIN_GPIO 2 // Wbudowana dioda LED
   #define RELAY_GPIO 5       //przekaźnik
  
  // Struktura danych do odbierania
   typedef struct struct_message {
    bool relayState;
   } struct_message;
    struct_message relayData;
    
   unsigned long lastReceiveTime = 0;  // Czas ostatniego odbioru danych
   const unsigned long timeout = 10000;  // Czas (ms) do automatycznego wyłączenia LED
   
   // Callback – wywoływana, gdy dane zostaną odebrane
   void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) {
    memcpy(&relayData, incomingData, sizeof(relayData));
    
    Serial.print("Odebrano stan: ");
    Serial.println(relayData.relayState ? "HIGH" : "LOW");

    // Włącz lub wyłącz diodę LED wbudowaną w Wemos D1 Mini
    digitalWrite(LED_BUILTIN_GPIO, relayData.relayState ? LOW : HIGH);
     digitalWrite(RELAY_GPIO, relayData.relayState ? HIGH : LOW);  
     
     // Aktualizacja czasu ostatniego odbioru
    lastReceiveTime = millis();
  }

    void setup() {
    Serial.begin(115200);
    
    // Ustawienie pinu diody LED jako OUTPUT
    pinMode(LED_BUILTIN_GPIO, OUTPUT);
     pinMode(RELAY_GPIO, OUTPUT);
    
    digitalWrite(LED_BUILTIN_GPIO, HIGH);  // Wyłączona domyślnie
    digitalWrite(RELAY_GPIO, LOW);         // Przekaźnik domyślnie wyłączony

    // Ustawienie trybu Wi-Fi na station mode
    WiFi.mode(WIFI_STA);

    // Inicjalizacja ESP-NOW
    if (esp_now_init() != 0) {
        Serial.println("Błąd inicjalizacji ESP-NOW!");
        return;
    }

    // Rejestracja callbacka do odbioru danych
    esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
    esp_now_register_recv_cb(OnDataRecv);

    Serial.println("Oczekiwanie na dane ESP-NOW...");
}

void loop() {
    // W tej wersji loop() jest puste, bo ESP-NOW działa w tle
    
     // Sprawdź, czy minęło 10 sekund od ostatniego odebrania danych
    if (millis() - lastReceiveTime > timeout) {
       digitalWrite(LED_BUILTIN_GPIO, HIGH);  // Wyłącz diodę LED
       digitalWrite(RELAY_GPIO, LOW);         // Wyłącz przekaźnik
    }
}
maniek
Posts: 28
Joined: Sun Oct 15, 2017 7:21 pm
Location: Góra

Post

Coś nie poszło z pierwszym kodem

Code: Select all

#ifdef ARDUINO_ARCH_AVR
#error "This example is not supported on Arduino AVR"
#endif

#include <SuplaDevice.h>
#include <supla/storage/eeprom.h>
#include <supla/network/esp_wifi.h>
#include <supla/device/status_led.h>
#include <supla/storage/littlefs_config.h>
#include <supla/network/esp_web_server.h>
#include <supla/network/html/device_info.h>   
#include <supla/network/html/protocol_parameters.h>
#include <supla/network/html/status_led_parameters.h>
#include <supla/network/html/wifi_parameters.h>
#include <supla/device/supla_ca_cert.h>
#include <supla/control/hvac_base.h>
#include <supla/clock/clock.h>
#include <supla/network/html/time_parameters.h>
#include <supla/network/html/hvac_parameters.h>
#include <supla/control/internal_pin_output.h>
#include <supla/events.h>
#include <supla/actions.h>
#include <supla/sensor/virtual_thermometer.h>
#include <supla/control/button.h>
#include <supla/sensor/DS18B20.h>
// ESP-NOW
#include <ESP8266WiFi.h>
extern "C" {
  #include <espnow.h>
}

#define CFG_BUTTON_GPIO 0
#define STATUS_LED_GPIO 2
#define LED_RED_GPIO 13
#define LED_BLUE_GPIO 12
#define OUTPUT_GPIO 14 
#define INPUT_GPIO 12  //GPIO do sprawdzania stanu
#define DS18B20_GPIO 4

const char* ssid = "Chatka-22";
const char* password = "0123456789";

 uint8_t broadcastAddress[] = {0xC8, 0xC9, 0xA3, 0x3A, 0xEA, 0xB3};  // Adres MAC drugiego Wemosa
  

  // Struktura danych do wysyłania
   typedef struct struct_message 
   {
    bool relayState; // Przechowuje stan GPIO14
}    struct_message;

    struct_message relayData;

 
    
    void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
    Serial.print("Wysyłanie ESP-NOW: ");
    Serial.println(sendStatus == 0 ? "Sukces" : "Błąd");
    
}

Supla::ESPWifi wifi;
Supla::Eeprom eeprom;
Supla::LittleFsConfig configSupla;
Supla::Device::StatusLed statusLed(STATUS_LED_GPIO, true);
Supla::EspWebServer suplaServer;

void setup() {
    Serial.begin(115200);
    
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    Serial.println("\nPołączono z WiFi!");

   // Pobierz aktualny kanał WiFi
   int wifiChannel = WiFi.channel();
   Serial.print("Kanał WiFi: ");
   Serial.println(wifiChannel);

// Ustawienie ESP-NOW na ten sam kanał
   //esp_wifi_set_channel(wifiChannel, WIFI_SECOND_CHAN_NONE);

   
    // Supla
    new Supla::Clock;
    new Supla::Html::DeviceInfo(&SuplaDevice);
    new Supla::Html::WifiParameters;
    new Supla::Html::ProtocolParameters;
    new Supla::Html::StatusLedParameters;
    new Supla::Html::TimeParameters(&SuplaDevice);

    new Supla::Device::StatusLed(STATUS_LED_GPIO, true);
    eeprom.setStateSavePeriod(5000);

    auto output = new Supla::Control::InternalPinOutput(OUTPUT_GPIO);
    auto redStatusLed = new Supla::Control::InternalPinOutput(LED_RED_GPIO, false);
    auto blueStatusLed = new Supla::Control::InternalPinOutput(LED_BLUE_GPIO, false);

    auto hvac = new Supla::Control::HvacBase(output);
    new Supla::Html::HvacParameters(hvac);

    DeviceAddress ds1addr = {0x28, 0x97, 0xD7, 0x06, 0x0A, 0x00, 0x00, 0x46};
    DeviceAddress ds2addr = {0x28, 0xFF, 0x54, 0x73, 0x6E, 0x18, 0x01, 0x77};
    new Supla::Sensor::DS18B20(DS18B20_GPIO, ds1addr);
    new Supla::Sensor::DS18B20(DS18B20_GPIO, ds2addr);

    hvac->setMainThermometerChannelNo(1);
    hvac->setAuxThermometerChannelNo(2);
    hvac->setTemperatureHisteresisMin(20);
    hvac->setTemperatureHisteresisMax(1000);
    hvac->setTemperatureAuxMin(500);
    hvac->setTemperatureAuxMax(7500);
    hvac->setTemperatureHisteresis(40);

    auto cfgButton = new Supla::Control::Button(CFG_BUTTON_GPIO, true, true);
    cfgButton->configureAsConfigButton(&SuplaDevice);

    SuplaDevice.setSuplaCACert(suplaCACert);
    SuplaDevice.setSupla3rdPartyCACert(supla3rdCACert);
    SuplaDevice.setName("Termostat ESP");
    SuplaDevice.begin(21);

    // ESP-NOW


// Ustawienie ESP-NOW na ten sam kanał
   //esp_wifi_set_channel(wifiChannel);
    
    WiFi.mode(WIFI_STA);
    if (esp_now_init() != 0) {
        Serial.println("Błąd inicjalizacji ESP-NOW");
        return;
    }

     esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
     
    esp_now_register_send_cb(OnDataSent);
    
    esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);

    pinMode(OUTPUT_GPIO, OUTPUT);  //INPUT_GPIO,INPUT
}

void loop() {
    SuplaDevice.iterate();

   relayData.relayState = digitalRead(OUTPUT_GPIO);
    esp_now_send(broadcastAddress, (uint8_t *) &relayData, sizeof(relayData));

    Serial.print("Wysłano stan GPIO14: ");
    Serial.println(relayData.relayState ? "HIGH" : "LOW");

    delay(1000);
}
User avatar
Zibi_007
Posts: 3985
Joined: Tue Oct 31, 2023 10:06 pm
Has thanked: 10 times
Been thanked: 31 times

Post

maniek wrote: Sat Feb 15, 2025 10:18 pm Witam. Wrzucam dla kogoś kto tak jak ja będzie potrzebował sterować zaworem lub podobnym ale brak przewodu między np. salon --- rozdzielacz podłogówka itp. (mieszkanie po generalnym remoncie i nie chcę niszczyć :D ) Na wemosa wgrany kod termostat plus ESP_now który przesyła o stanie pinu LOW/HIGH na drugiego wemosa, a ten steruje już przekaźnikiem. Od kilku dni działa a teraz dało by rade dołożyć wyświetlacz oled? może ktoś pomoże.
Nie żebym krytykował, wręcz przeciwnie. Szacun za włożony trud i pracę, szczególnie, że sam stanąłem niedawno przed podobnym problemem (zdalny termostat), ale... to wszystko jest w GG. Na jednym ESP generujesz soft z termostatem, ekranem oled (który chcesz teraz dołożyć), odczytem linków bezpośrednich (jeśli chcesz odczytywać temperaturę nie tylko z lokalnego termometru) i cobyś tam chciał jeszcze. Na drugie esp generujesz kod z przekaźnikiem i jeśli potrzebujesz to możesz sobie jeszcze coś dodać. Na pierwszym ustawiasz termostat (wirtualne GPIO, kanał temperatury, przyciski jeśli masz). Resztę (zmianę stanu przekaźnika) załatwiasz reakcjami (kiedyś szło też linkami bezpośrednimi, ale teraz się sypie). Lekko, łatwo i przyjemnie. Całość konfiguracji (razem z wygenerowaniem i wgraniem softu w GG) to kilka, kilkanaście minut. Wygoda i możliwość dodania wszystkiego co oferuje GG bezcenna :)

Mnie też kiedyś odpowiednio nakierowano, oszczędzając sporo czasu - jeszcze raz dziękuję @robertklepacki94, @krycha88 - Babcia Edyta jest wniebowzięta :)
Image Wiesz, że Supla obsługuje Zigbee? Wstąp do Klubu Promila: https://forum.supla.org/viewtopic.php?t=18018
Image Szukasz nowego GG? Jest tutaj: https://forum.supla.org/viewtopic.php?t=18036
User avatar
Duch__
Posts: 2199
Joined: Wed Aug 24, 2016 7:26 pm
Location: Opole
Been thanked: 6 times

Post

Wadą tego softu jest to że trzeba znać adres MAC docelowego modułu esp. Ja preferuję podobne rozwiązanie (steruje zdalnie pompą co z pwm-em) ale wysyłam globalne pakiety udp, tak że każde urządzenie w sieci je dostaje, a reagują tylko te które rozumieją polecenie. Nie muszę znać adresu IP, ani MAC odbiorcy.

Dodatkowo sugeruje zaimplementować zabezpieczenia w postaci, co ma się wydarzyć z przekaźnikiem w sytuacji nieodebrania danych przez określony czas(awaria wifi, awaria głównego esp), oraz liczenie chociaż prostej sumy kontrolnej, na wypadek podobnych transmisji w sieci.
maniek
Posts: 28
Joined: Sun Oct 15, 2017 7:21 pm
Location: Góra

Post

Sprawdzenie adresu MAC to kilka linijek kodu a przy braku odbioru danych po 10s wyjście przekaźnika zostaje wyłączone. A tak ogólnie to że mam taki internet który działa jak chce :shock: więc to połączenie jest stabilne.

Code: Select all

// Czas ostatniego odbioru danych
unsigned long lastReceiveTime = 0;
const unsigned long timeout = 10000;  // Czas (ms) do automatycznego wyłączenia

Code: Select all

 // Sprawdź, czy minęło 10 sekund od ostatniego odebrania danych
    if (millis() - lastReceiveTime > timeout) {
        digitalWrite(LED_BUILTIN_GPIO, HIGH);  // Wyłącz diodę LED
        digitalWrite(RELAY_GPIO, LOW);         // Wyłącz przekaźnik
    }
Do spr. adresu MAC

Code: Select all

#include <ESP8266WiFi.h>

void setup() {
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);
    Serial.print("Adres MAC: ");
    Serial.println(WiFi.macAddress());
}

void loop() {}
User avatar
Duch__
Posts: 2199
Joined: Wed Aug 24, 2016 7:26 pm
Location: Opole
Been thanked: 6 times

Post

Ale mnie chodziło o to że przy każdej podmianie procesora wykonawczego, musisz kompilować soft dla dwóch procków (głównego i wykonawczego). Lepsze rozwiązanie to wysyłanie globalnie informacji do wszystkich urządzeń w sieci w tej samej chwili, a ten który rozumie treść wiadomości, to ma na nią zareagować.
maniek
Posts: 28
Joined: Sun Oct 15, 2017 7:21 pm
Location: Góra

Post

Kolego Duch__ masz RACJE, poczytałem i faktycznie przez udp jest lepszym rozwiązaniem. A czy jeżeli to nie problem możesz udostępnić kod do sterowania pompą pwm. No i dalej będę musiał w wolnych chwilach się dokształcać :D :D
User avatar
Duch__
Posts: 2199
Joined: Wed Aug 24, 2016 7:26 pm
Location: Opole
Been thanked: 6 times

Post

Nie wiem czy kod ci się przyda, bo moja pompa Grundfos ma po prostu wejście PWM.

A co do samego sterowania to tam nie ma nic szczególnego. PWM aktualne jest x. PWM docelowe jest y. Jeśli x < y to ++x, jeśli x > y to x-- z np najprostszym delayem co 100ms żeby płynnie się rozpędzało i zwalniało. Jeśli x = y to nic nie rób. Jeśli nie ma wifi lub dane nie były wysyłane dłużej niż 30 sekund to y = max PWM (nie wiadomo w jakim stanie jest główny moduł). Ot cały algorytm.
krycha88
Posts: 5575
Joined: Fri Nov 16, 2018 7:25 am
Been thanked: 5 times

Post

Zibi_007 wrote: Sun Feb 16, 2025 12:01 am Nie żebym krytykował, wręcz przeciwnie. Szacun za włożony trud i pracę, szczególnie, że sam stanąłem niedawno przed podobnym problemem (zdalny termostat), ale... to wszystko jest w GG. Na jednym ESP generujesz soft z termostatem, ekranem oled (który chcesz teraz dołożyć), odczytem linków bezpośrednich (jeśli chcesz odczytywać temperaturę nie tylko z lokalnego termometru) i cobyś tam chciał jeszcze. Na drugie esp generujesz kod z przekaźnikiem i jeśli potrzebujesz to możesz sobie jeszcze coś dodać. Na pierwszym ustawiasz termostat (wirtualne GPIO, kanał temperatury, przyciski jeśli masz). Resztę (zmianę stanu przekaźnika) załatwiasz reakcjami (kiedyś szło też linkami bezpośrednimi, ale teraz się sypie). Lekko, łatwo i przyjemnie. Całość konfiguracji (razem z wygenerowaniem i wgraniem softu w GG) to kilka, kilkanaście minut. Wygoda i możliwość dodania wszystkiego co oferuje GG bezcenna :)

Mnie też kiedyś odpowiednio nakierowano, oszczędzając sporo czasu - jeszcze raz dziękuję @robertklepacki94, @krycha88 - Babcia Edyta jest wniebowzięta :)
Ale zastosowanie ESP-NOW jest dużo lepszym rozwiązaniem niż korzystanie z linków bezpośrednich - bo jest tworzona niezależna sieć między dwoma ESP działa to bez internetu nawet nie trzeba być podłączonym do routera ;)
https://gui-generic-builder.supla.io/
mmiikk
Posts: 670
Joined: Mon Aug 06, 2018 1:27 pm
Has thanked: 1 time
Been thanked: 2 times

Post

Bardzo fajne rozwiazanie z tym UDP , zestawienie mostu na dwa wemosy niezalezne od stanu intrernetu bez kabli - mistrzostwo. Chciałbym coś takiefgo w GG niestety zaprogramować nie dam rady.

Z drugiej strony od 3 lat korzystam z wirtualnego termostatu supla scripts i tylko 2 ray byl problem z internetem ze zrobilo sie w domu za zimno na 3 h :)

Return to “Pomoc”