SmartPool - pomoc przy kodzie ;)

User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

Harmonogram ogarnięty.
Niestety ciągle mam kłopot z zapisem danych.

Po kolei, wgrywan na nowo firmware z kasowaniem pamięci, podaje dane do serwera zapisz i reset. Moduł się ładnie dodaje i wszystko chodzi.
Wchodzę na lokalne IP i na moją stronę SmartPool, zapisuje konfigurację i moduł dalej działa poprawnie aż do resetu (?) - odłączenia prądu i ponownym zasilaniu ponownie jest w trybie konfiguracji,

Teraz jestem już w pracy, ale będę kombinować z podejrzeniem danych do zapisu..
No bo chyba pamięci nie powinno braknąć na parę suwaków i termometrów. Patrząc na to co GG albo bramka ZigBee pamięta.
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
klew
Posts: 13907
Joined: Thu Jun 27, 2019 12:16 pm
Location: Wrocław
Has thanked: 134 times
Been thanked: 137 times

Post

Lector wrote: Mon Jun 01, 2026 11:09 am Harmonogram ogarnięty.
Niestety ciągle mam kłopot z zapisem danych.

Po kolei, wgrywan na nowo firmware z kasowaniem pamięci, podaje dane do serwera zapisz i reset. Moduł się ładnie dodaje i wszystko chodzi.
Wchodzę na lokalne IP i na moją stronę SmartPool, zapisuje konfigurację i moduł dalej działa poprawnie aż do resetu (?) - odłączenia prądu i ponownym zasilaniu ponownie jest w trybie konfiguracji,

Teraz jestem już w pracy, ale będę kombinować z podejrzeniem danych do zapisu..
No bo chyba pamięci nie powinno braknąć na parę suwaków i termometrów. Patrząc na to co GG albo bramka ZigBee pamięta.
Niedawno do SD dodwałem token CSRF do formularzy w web interface. Jeśli używasz "main" zamiast oficjalmnych releaseów, to ten token jest sprawdzany.
Kod wymaga, aby to był pierwszy element w przetwarzanym POST HTML. Tak powinno być, o ile nie masz np. zepsutego HTML na urządzeniu. Najprościej pobrać sobie wygenerowany HTML i wrzucić go do jakiegoś online HTML validator, który sprawdzi, czy wszystko masz poprawnie podomykane.

W kolejnym kroku -> sprawdź logi
Najlepsze suple dla Twojego domu :mrgreen:
User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

Tak na szybko zapytałem moje AI i to może być to.
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

SD mam w 26.4 - to co pobiera z automatu Arduino Ide.
Jak mam wstawić ten token?

Code: Select all

html += "<form action=/smartpool method=POST><input type=\"hidden\" name=\"supla_csrf\" value=\"" + server->arg("supla_csrf") + "\">";
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

Zrobiłem to co chciałem, cała logika działa poprawnie (jeszcze nie wiem jak z termometrami), ale ciągle mam problem z zapisem konfiguracji. Po zaniku napięcie i powrocie mam tryb konfiguracji.

Code: Select all

#include <SuplaDevice.h>
#include <supla/network/esp_wifi.h>
#include <supla/control/relay.h>
#include <supla/control/button.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/device/status_led.h>
#include <supla/control/action_trigger.h>
#include <supla/control/virtual_relay.h>

#include <supla/storage/storage.h>
#include <supla/storage/config.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>

#ifdef ARDUINO_ARCH_ESP32
#include <HTTPUpdateServer.h>
#else
#include <ESP8266HTTPUpdateServer.h>
#endif

#include <OneWire.h>
#include <DallasTemperature.h>
#include <supla/sensor/thermometer.h>

// ======================================================
// GPIO - ESP32 Relay X8
// ======================================================
#define STATUS_LED_GPIO          23
#define BUTTON_CFG_GPIO          0

#define RELAY_1_GPIO             32
#define RELAY_2_GPIO             33
#define RELAY_3_GPIO             25
#define RELAY_4_GPIO             26
#define RELAY_5_GPIO             27
#define RELAY_6_GPIO             14
#define RELAY_7_GPIO             12
#define RELAY_8_GPIO             13

#define ONE_WIRE_BUS             16
#define LIGHT_BUTTON_GPIO        17

// ======================================================
// CONFIG & VARIABLES
// ======================================================
const char* const SENSOR_NAMES[] = {
  "Woda w basenie",
  "Woda w solarach",
  "Woda powrotna",
  "Powietrze"
};
const int SENSOR_COUNT = sizeof(SENSOR_NAMES) / sizeof(SENSOR_NAMES[0]);

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

#ifdef ARDUINO_ARCH_ESP32
HTTPUpdateServer httpUpdater;
#else
ESP8266HTTPUpdateServer httpUpdater;
#endif

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

DeviceAddress discoveredAddresses[10];
int discoveredCount = 0;

Supla::Control::Relay* relayLock = nullptr;
Supla::Control::Relay* relayPump = nullptr;
Supla::Control::Relay* relayChlorine = nullptr;
Supla::Control::Relay* relayHeatPump = nullptr;
Supla::Control::Relay* relayUV = nullptr;
Supla::Control::Relay* relayLight = nullptr;
Supla::Control::VirtualRelay* virtualRelay = nullptr;
Supla::Control::HvacBase* hvacPool = nullptr;
bool depChlorine = false;
bool depUV = false;

bool lockPump = false;
bool lockChlorine = false;
bool lockHeating = false;
bool lockUV = false;

bool schControlDependencies = true;
bool schEnabled = true;
unsigned long heatPumpStartTime = 0;
bool heatPumpRunningInAuto = false;

// ======================================================
// DS18B20
// ======================================================
void scanDS18B20() {
  pinMode(ONE_WIRE_BUS, INPUT_PULLUP);
  delay(250);
  sensors.begin();
  delay(50);
  discoveredCount = 0;
  DeviceAddress tempAddr;
  oneWire.reset_search();
  while (oneWire.search(tempAddr) && discoveredCount < 10) {
    if (sensors.validAddress(tempAddr) && tempAddr[0] == 0x28) {
      memcpy(discoveredAddresses[discoveredCount], tempAddr, sizeof(DeviceAddress));
      discoveredCount++;
    }
  }
}

class ConfigurableDS18B20 : public Supla::Sensor::Thermometer {
 public:
  ConfigurableDS18B20(int sensorNumber) : sensorNum(sensorNumber) {
    memset(deviceAddress, 0, sizeof(deviceAddress));
  }

  void onInit() override {
    loadAddress();
    channel.setNewValue(getValue());
  }

  double getValue() override {
    if (deviceAddress[0] == 0 && deviceAddress[7] == 0) return -275.0;
    sensors.requestTemperaturesByAddress(deviceAddress);
    double temp = sensors.getTempC(deviceAddress);
    if (temp == DEVICE_DISCONNECTED_C) return -275.0;
    return temp;
  }

  void loadAddress() {
    auto cfg = Supla::Storage::ConfigInstance();
    if (!cfg) return;
    char key[20];
    for (int i = 0; i < 8; i++) {
      snprintf(key, sizeof(key), "ds_%d_byte_%d", sensorNum, i);
      uint8_t b = 0;
      cfg->getUInt8(key, &b);
      deviceAddress[i] = b;
    }
  }

 protected:
  int sensorNum;
  DeviceAddress deviceAddress;
};

ConfigurableDS18B20* dsSensors[SENSOR_COUNT];

// ======================================================
// FUNKCJE POMOCNICZE
// ======================================================
void setRelayBlocked(Supla::Control::Relay* relay, bool blocked) {
  if (!relay || !relay->getChannel()) return;
  if (blocked) {
    relay->turnOff();
    relay->getChannel()->setStateOffline();
  } else {
    relay->getChannel()->setStateOnline();
  }
}

// ======================================================
// ZAPIS FORMULARZA
// ======================================================
void handleSmartPoolSaveAction() {
  auto server = suplaServer.getServerPtr();
  if (!server) return;

  auto cfg = Supla::Storage::ConfigInstance();
  if (cfg) {
    cfg->setUInt8("lock_pump", server->hasArg("lock_pump") ? 1 : 0);
    cfg->setUInt8("lock_chlor", server->hasArg("lock_chlor") ? 1 : 0);
    cfg->setUInt8("lock_heat", server->hasArg("lock_heat") ? 1 : 0);
    cfg->setUInt8("lock_uv", server->hasArg("lock_uv") ? 1 : 0);
    cfg->setUInt8("dep_chlorine", server->hasArg("dep_chlorine") ? 1 : 0);
    cfg->setUInt8("dep_uv", server->hasArg("dep_uv") ? 1 : 0);
    cfg->setUInt8("p_sch_dep", server->hasArg("p_sch_dep") ? 1 : 0);

    for (int t = 0; t < SENSOR_COUNT; t++) {
      char argName[20];
      snprintf(argName, sizeof(argName), "ds_assign_%d", t);

      if (server->hasArg(argName)) {
        int selectedIdx = server->arg(argName).toInt();
        char idxKey[20];
        snprintf(idxKey, sizeof(idxKey), "ds_idx_%d", t);
        cfg->setUInt8(idxKey, selectedIdx);

        char byteKey[20];
        for (int i = 0; i < 8; i++) {
          snprintf(byteKey, sizeof(byteKey), "ds_%d_byte_%d", t, i);
          cfg->setUInt8(byteKey, (selectedIdx >= 0 && selectedIdx < discoveredCount) ? discoveredAddresses[selectedIdx][i] : 0);
        }
      }
    }

    if (server->hasArg("heat_on_t1")) cfg->setUInt8("heat_on_t1", server->arg("heat_on_t1").toInt());
    if (server->hasArg("heat_on_t2")) cfg->setUInt8("heat_on_t2", server->arg("heat_on_t2").toInt());
    if (server->hasArg("heat_on_diff")) cfg->setInt32("heat_on_diff", server->arg("heat_on_diff").toInt());
    if (server->hasArg("heat_off_t1")) cfg->setUInt8("heat_off_t1", server->arg("heat_off_t1").toInt());
    if (server->hasArg("heat_off_t2")) cfg->setUInt8("heat_off_t2", server->arg("heat_off_t2").toInt());
    if (server->hasArg("heat_off_diff")) cfg->setInt32("heat_off_diff", server->arg("heat_off_diff").toInt());
    if (server->hasArg("heat_min_time")) cfg->setInt32("heat_min_time", server->arg("heat_min_time").toInt());

    cfg->commit();

    for (int i = 0; i < SENSOR_COUNT; i++) {
      if (dsSensors[i]) dsSensors[i]->loadAddress();
    }
  }

  server->sendHeader("Location", "/smartpool?saved=1");
  server->send(303, "text/plain", "");
}

// ======================================================
// STRONA WWW
// ======================================================
void handleSmartPoolPage() {
  auto server = suplaServer.getServerPtr();
  if (!server) return;
  auto cfg = Supla::Storage::ConfigInstance();

  String html = "";
  html += "<!doctype html><html lang=en><head><meta charset=UTF-8><meta name=viewport content=width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no><title>SUPLA-SmartPool</title><style>*,*::before,*::after{box-sizing:border-box}html:focus-within{scroll-behavior:smooth}html,body,h1,h2,h3,h4,p{font-family:HelveticaNeue,Helvetica,Arial,sans-serif;margin:0;padding:0}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5;font-size:14px;background:#00d151;color:#fff}h1,h3{margin:10px 0;font-weight:300;font-size:23px}.wrapper{display:flex;flex-direction:column;justify-content:center;min-height:100vh}.content{text-align:center;padding:20px 10px}.box{background:#fff;color:#000;border-radius:10px;padding:5px 10px;margin:10px 0;box-shadow:0 5px 6px rgb(0 0 0 / .3)}#msg{position:fixed;top:0;left:0;width:100%;height:119px;box-sizing:border-box;padding:15px 20px;background:#ffe836;color:#000;text-align:center;font-size:26px;box-shadow:0 2px 5px rgb(0 0 0 / .3);z-index:9999;display:flex;align-items:center;justify-content:center}.form{text-align:left;max-width:500px;margin:0 auto;margin-top:-40px;padding:10px;padding-top:70px}.form-field{display:flex;align-items:center;padding:8px 10px;border-top:1px solid #00d150;margin:0 -10px}.box>.form-field:first-of-type{border-top:0}.form-field label{width:250px;margin-right:5px;color:#00d151}@media screen and (max-width:530px){.form-field{flex-direction:column}.form-field label{width:100%;margin:3px 0}}.form-field input,.form-field select{width:100%;border:1px solid #ccc;border-radius:6px;padding:3px 8px;background:#fff}.form-field.right-checkbox label:last-child{width:100%;text-align:center}.switch{position:relative;display:inline-block;width:51px;height:25px}.switch input{opacity:0;width:0;height:0}.slider{position:absolute;cursor:pointer;inset:0;background-color:#ccc;transition:.4s;border-radius:34px}.slider:before{position:absolute;content:\"\";height:17px;width:17px;left:4px;bottom:4px;background-color:#fff;transition:.4s;border-radius:50%}input:checked+.slider{background-color:#00d151}input:checked+.slider:before{transform:translateX(26px)}button{display:block;color:#fff;background:#000;width:100%;border:0;border-radius:10px;padding:10px;text-transform:uppercase;cursor:pointer;font-size:1.3em;margin-top:15px}</style></head><body><div class=wrapper><div class=content><div class=form><h1>SmartPool</h1>";
  if (server->hasArg("saved")) html += "<div id=\"msg\">Data saved</div>";
  html += "<form action=/smartpool method=POST><input type=\"hidden\" name=\"supla_csrf\" value=\"" + server->arg("supla_csrf") + "\">";

  html += "<div class=box><h3>Czujniki DS18B20</h3>";
  for (int t = 0; t < SENSOR_COUNT; t++) {
    uint8_t selectedIdx = 255;
    if (cfg) {
      char key[20];
      snprintf(key, sizeof(key), "ds_idx_%d", t);
      cfg->getUInt8(key, &selectedIdx);
    }
    html += "<div class=form-field><label for=ds_assign_" + String(t) + ">" + String(SENSOR_NAMES[t]) + "</label><div><select name=ds_assign_" + String(t) + " id=ds_assign_" + String(t) + "><option value=255" + String(selectedIdx == 255 ? " selected" : "") + ">Nie przypisany</option>";
    for (int i = 0; i < discoveredCount; i++) {
      String idStr = "ID: ";
      for (int b = 0; b < 4; b++) {
        char hex[4];
        snprintf(hex, sizeof(hex), "%02X", discoveredAddresses[i][b]);
        idStr += hex;
      }
      html += "<option value=\"" + String(i) + "\"" + String(selectedIdx == i ? " selected" : "") + ">" + idStr + "...</option>";
    }
    html += "</select></div></div>";
  }
  html += "</div>";

  uint8_t h_on_t1 = 0, h_on_t2 = 1, h_off_t1 = 0, h_off_t2 = 2;
  int32_t h_on_diff = 5, h_off_diff = 3, h_min_time = 5;
  if (cfg) {
    cfg->getUInt8("heat_on_t1", &h_on_t1);
    cfg->getUInt8("heat_on_t2", &h_on_t2);
    cfg->getInt32("heat_on_diff", &h_on_diff);
    cfg->getUInt8("heat_off_t1", &h_off_t1);
    cfg->getUInt8("heat_off_t2", &h_off_t2);
    cfg->getInt32("heat_off_diff", &h_off_diff);
    cfg->getInt32("heat_min_time", &h_min_time);
  }

  html += "<div class=box><h3>Pompa ogrzewania</h3>";
  html += "<div class=form-field style=gap:6px;><label>Warunek startu pompy</label><select name=heat_on_t1>";
  for (int i = 0; i < SENSOR_COUNT; i++) html += "<option value=\"" + String(i) + "\"" + String(h_on_t1 == i ? " selected" : "") + ">" + String(SENSOR_NAMES[i]) + "</option>";
  html += "</select><span>&lt;</span><select name=heat_on_t2>";
  for (int i = 0; i < SENSOR_COUNT; i++) html += "<option value=\"" + String(i) + "\"" + String(h_on_t2 == i ? " selected" : "") + ">" + String(SENSOR_NAMES[i]) + "</option>";
  html += "</select><span>o</span><input type=number min=0 name=heat_on_diff style=width:60px value=\"" + String(h_on_diff) + "\"></div>";
  html += "<div class=form-field style=gap:6px;><label>Warunek stopu pompy</label><select name=heat_off_t1>";
  for (int i = 0; i < SENSOR_COUNT; i++) html += "<option value=\"" + String(i) + "\"" + String(h_off_t1 == i ? " selected" : "") + ">" + String(SENSOR_NAMES[i]) + "</option>";
  html += "</select><span>&lt;</span><select name=heat_off_t2>";
  for (int i = 0; i < SENSOR_COUNT; i++) html += "<option value=\"" + String(i) + "\"" + String(h_off_t2 == i ? " selected" : "") + ">" + String(SENSOR_NAMES[i]) + "</option>";
  html += "</select><span>o</span><input type=number min=0 name=heat_off_diff style=width:60px value=\"" + String(h_off_diff) + "\"></div>";
  html += "<div class=form-field style=justify-content:space-between;><label>Minimalny czas pracy (min.)</label><input type=number min=0 name=heat_min_time style=width:60px;text-align:center value=\"" + String(h_min_time) + "\"></div></div>";

  auto addToggleSwitch = [](String name, String label, uint8_t val) {
    return "<div class=\"form-field right-checkbox\"><label for=\"" + name + "\">" + label + "</label><label><span class=switch><input type=checkbox value=1 name=\"" + name + "\" id=\"" + name + "\"" + String(val ? " checked" : "") + "><span class=slider></span></span></label></div>";
  };

  uint8_t l_pump = 0, l_chlor = 0, l_heat = 0, l_uv = 0, depSch = 0;
  if (cfg) {
    cfg->getUInt8("lock_pump", &l_pump);
    cfg->getUInt8("lock_chlor", &l_chlor);
    cfg->getUInt8("lock_heat", &l_heat);
    cfg->getUInt8("lock_uv", &l_uv);
    cfg->getUInt8("p_sch_dep", &depSch);
  }

  html += "<div class=box><h3>Zabezpieczenie - blokada</h3>";
  html += addToggleSwitch("lock_pump", "Pompa filtracyjna", l_pump);
  html += addToggleSwitch("lock_chlor", "Generator chloru i ozonu", l_chlor);
  html += addToggleSwitch("lock_heat", "Pompa ogrzewania", l_heat);
  html += addToggleSwitch("lock_uv", "Lampa UV-C", l_uv);
  html += "</div>";

  uint8_t chlorine = 0, uv = 0;
  if (cfg) {
    cfg->getUInt8("dep_chlorine", &chlorine);
    cfg->getUInt8("dep_uv", &uv);
  }
  html += "<div class=box><h3>Kanały zależne od pompy filtracyjnej</h3>";
  html += addToggleSwitch("dep_chlorine", "Generator chloru", chlorine);
  html += addToggleSwitch("dep_uv", "Lampa UV-C", uv);
  html += "</div>";

  html += "<div class=box><h3>Harmonogramy pompy filtracyjnej</h3>";
  html += addToggleSwitch("p_sch_dep", "Steruj kanałami zależnymi", depSch);
  html += "</div>";

  html += "<script>setTimeout(function(){var msgEl=document.getElementById('msg');if(msgEl){msgEl.remove();}},3200);</script>";
  html += "<button type=button onclick=\"window.location.href='/'\">SUPLA SETTINGS</button>";
  html += "<button type=button onclick=\"window.location.href='/update'\">UPDATE FIRMWARE</button>";
  html += "<button type=submit>SAVE</button>";
  html += "</form></div></div></div></body></html>";

  server->send(200, "text/html", html);
}

class SmartPoolMenuLink : public Supla::HtmlElement {
 public:
  SmartPoolMenuLink() : HtmlElement(Supla::HTML_SECTION_FORM) {}
  void send(Supla::WebSender* sender) override {
    sender->send("</div><button type=button onclick=\"window.location.href='/smartpool'\">SMARTPOOL SETTINGS</button><button type=button onclick=\"window.location.href='/update'\">UPDATE FIRMWARE</button><div>");
  }
};

// ======================================================
// HARMONOGRAM HVAC
// ======================================================
void checkHvacSchedulePump() {
  auto cfg = Supla::Storage::ConfigInstance();
  if (!cfg || !relayPump || !hvacPool || !virtualRelay) return;

  bool harmonogramActive = virtualRelay->isOn();
  if (!harmonogramActive) return;

  auto currentProgram = hvacPool->getCurrentProgram();
  double my_temp = currentProgram.SetpointTemperatureHeat / 100.0;

  bool shouldBeOn = false;
  if (my_temp >= 40.0) {
    shouldBeOn = true;
  } else if (my_temp <= 20.0) {
    shouldBeOn = false;
  } else {
    shouldBeOn = false;
  }

  bool pumpState = relayPump->isOn();
  if (shouldBeOn && !pumpState) {
    relayPump->turnOn();

    uint8_t dep = 0;
    cfg->getUInt8("p_sch_dep", &dep);
    if (dep == 1) {
      bool schDepChlorine = false;
      bool schDepUV = false;
      uint8_t temp = 0;

      cfg->getUInt8("dep_chlorine", &temp);
      schDepChlorine = (temp == 1);

      cfg->getUInt8("dep_uv", &temp);
      schDepUV = (temp == 1);

      bool mainLockActive = (relayLock && relayLock->isOn());

      cfg->getUInt8("lock_chlor", &temp);
      if (mainLockActive && temp == 1) schDepChlorine = false;

      cfg->getUInt8("lock_uv", &temp);
      if (mainLockActive && temp == 1) schDepUV = false;

      if (schDepChlorine && relayChlorine && !relayChlorine->isOn()) {
        relayChlorine->turnOn();
      }
      if (schDepUV && relayUV && !relayUV->isOn()) {
        relayUV->turnOn();
      }
    }
  } else if (!shouldBeOn && pumpState) {
    relayPump->turnOff();
  }

  if (shouldBeOn && pumpState) {

  uint8_t dep = 0;
  cfg->getUInt8("p_sch_dep", &dep);

  if (dep == 1) {

    bool schDepChlorine = false;
    bool schDepUV = false;
    uint8_t temp = 0;

    cfg->getUInt8("dep_chlorine", &temp);
    schDepChlorine = (temp == 1);

    cfg->getUInt8("dep_uv", &temp);
    schDepUV = (temp == 1);

    bool mainLockActive = (relayLock && relayLock->isOn());

    cfg->getUInt8("lock_chlor", &temp);
    if (mainLockActive && temp == 1) schDepChlorine = false;

    cfg->getUInt8("lock_uv", &temp);
    if (mainLockActive && temp == 1) schDepUV = false;

    if (schDepChlorine && relayChlorine && !relayChlorine->isOn()) {
      relayChlorine->turnOn();
    }

    if (schDepUV && relayUV && !relayUV->isOn()) {
      relayUV->turnOn();
    }
  }
}
}

// ======================================================
// LOGIKA ZALEŻNOŚCI
// ======================================================
void handleRelayDependencies() {
  auto cfg = Supla::Storage::ConfigInstance();
  if (cfg) {
    uint8_t temp = 0;
    cfg->getUInt8("lock_pump", &temp); lockPump = (temp == 1);
    cfg->getUInt8("lock_chlor", &temp); lockChlorine = (temp == 1);
    cfg->getUInt8("lock_heat", &temp); lockHeating = (temp == 1);
    cfg->getUInt8("lock_uv", &temp); lockUV = (temp == 1);
    cfg->getUInt8("dep_chlorine", &temp); depChlorine = (temp == 1);
    cfg->getUInt8("dep_uv", &temp); depUV = (temp == 1);
  }

  bool mainLockActive = (relayLock && relayLock->isOn());

  setRelayBlocked(relayPump, mainLockActive && lockPump);
  setRelayBlocked(relayChlorine, mainLockActive && lockChlorine);
  setRelayBlocked(relayHeatPump, mainLockActive && lockHeating);
  setRelayBlocked(relayUV, mainLockActive && lockUV);

  if (mainLockActive && lockHeating) {
    heatPumpRunningInAuto = false;
  }

  if (relayPump && !(mainLockActive && lockPump)) {
    static bool lastPumpState = false;
    bool currentPumpState = relayPump->isOn();

    if (lastPumpState == true && currentPumpState == false) {
      if (depChlorine && relayChlorine && relayChlorine->isOn()) relayChlorine->turnOff();
      if (depUV && relayUV && relayUV->isOn()) relayUV->turnOff();
      lastPumpState = currentPumpState;
      return;
    }

    if (!currentPumpState) {
      if (depChlorine && relayChlorine && relayChlorine->isOn()) {
        relayPump->turnOn();
        currentPumpState = true;
      } else if (depUV && relayUV && relayUV->isOn()) {
        relayPump->turnOn();
        currentPumpState = true;
      }
    }

    if (!currentPumpState) {
      if (depChlorine && relayChlorine && relayChlorine->isOn()) relayChlorine->turnOff();
      if (depUV && relayUV && relayUV->isOn()) relayUV->turnOff();
    }

    lastPumpState = currentPumpState;
  }
}

void handleRelayLocks() {
  bool mainLockActive = (relayLock && relayLock->isOn());
  setRelayBlocked(relayPump, mainLockActive && lockPump);
  setRelayBlocked(relayChlorine, mainLockActive && lockChlorine);
  setRelayBlocked(relayHeatPump, mainLockActive && lockHeating);
  setRelayBlocked(relayUV, mainLockActive && lockUV);
  if (mainLockActive && lockHeating) {
    heatPumpRunningInAuto = false;
  }
}

// ======================================================
// SETUP
// ======================================================
void setup() {
  Serial.begin(115200);
  delay(1000);
  new Supla::Clock;

  SuplaDevice.allowWorkInOfflineMode(0);
  SuplaDevice.setName("SUPLA-SmartPool");
  SuplaDevice.setSwVersion("SmartPool v2.4");

  new Supla::Html::DeviceInfo(&SuplaDevice);
  new Supla::Html::WifiParameters;
  new Supla::Html::ProtocolParameters(false, false);
  new Supla::Html::StatusLedParameters;
  new Supla::Html::TimeParameters(&SuplaDevice);
  new SmartPoolMenuLink();

  relayLock = new Supla::Control::Relay(RELAY_1_GPIO, true);
  relayPump = new Supla::Control::Relay(RELAY_2_GPIO, true);
  relayChlorine = new Supla::Control::Relay(RELAY_3_GPIO, true);
  relayHeatPump = new Supla::Control::Relay(RELAY_4_GPIO, true);
  relayUV = new Supla::Control::Relay(RELAY_5_GPIO, true);
  relayLight = new Supla::Control::Relay(RELAY_6_GPIO, true);

  relayLock->getChannel()->setChannelNumber(0);
  relayLock->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  relayLock->setInitialCaption("Zabezpieczenie");

  relayPump->getChannel()->setChannelNumber(1);
  relayPump->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  relayPump->setInitialCaption("Pompa filtracyjna");

  relayChlorine->getChannel()->setChannelNumber(2);
  relayChlorine->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  relayChlorine->setInitialCaption("Generator chloru i ozonu");

  relayHeatPump->getChannel()->setChannelNumber(3);
  relayHeatPump->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  relayHeatPump->setInitialCaption("Pompa ogrzewania");

  relayUV->getChannel()->setChannelNumber(4);
  relayUV->setDefaultFunction(SUPLA_CHANNELFNC_LIGHTSWITCH);
  relayUV->setInitialCaption("Lampa UV-C");

  relayLight->getChannel()->setChannelNumber(5);
  relayLight->setDefaultFunction(SUPLA_CHANNELFNC_LIGHTSWITCH);
  relayLight->setInitialCaption("Oświetlenie basenu");

  virtualRelay = new Supla::Control::VirtualRelay();
  virtualRelay->getChannel()->setChannelNumber(6);
  virtualRelay->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  virtualRelay->setInitialCaption("Harmonogram pompy filtracyjnej");
  virtualRelay->setDefaultStateRestore();

  pinMode(RELAY_7_GPIO, OUTPUT);
  pinMode(RELAY_8_GPIO, OUTPUT);
  digitalWrite(RELAY_7_GPIO, LOW);
  digitalWrite(RELAY_8_GPIO, LOW);

  auto buttonLight = new Supla::Control::Button(LIGHT_BUTTON_GPIO, true, true);
  buttonLight->setHoldTime(1000);
  buttonLight->setMulticlickTime(300);

  auto atLight = new Supla::Control::ActionTrigger();
  atLight->setRelatedChannel(relayLight);
  atLight->attach(buttonLight);
  buttonLight->addAction(Supla::TOGGLE, relayLight, Supla::ON_PRESS);
  buttonLight->addAction(Supla::TOGGLE, relayLock, Supla::ON_HOLD);

  auto hvacOutput = new Supla::Control::InternalPinOutput(-1);
  hvacPool = new Supla::Control::HvacBase(hvacOutput);
  hvacPool->getChannel()->setChannelNumber(7);
  hvacPool->setInitialCaption("Termostat Basenu");
  hvacPool->setMainThermometerChannelNo(8);
  hvacPool->setDefaultTemperatureRoomMin(SUPLA_CHANNELFNC_HVAC_THERMOSTAT, 2000);
  hvacPool->setDefaultTemperatureRoomMax(SUPLA_CHANNELFNC_HVAC_THERMOSTAT, 4000);
  hvacPool->getChannel()->setHvacSetpointTemperatureHeat(3300);
  hvacPool->setTemperatureHisteresis(10);
  hvacPool->setTemperatureHisteresisMin(10);
  hvacPool->setTemperatureHisteresisMax(1000);

  scanDS18B20();
  for (int i = 0; i < SENSOR_COUNT; i++) {
    dsSensors[i] = new ConfigurableDS18B20(i);
    dsSensors[i]->getChannel()->setChannelNumber(8 + i);
    dsSensors[i]->setInitialCaption(SENSOR_NAMES[i]);
  }

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

  SuplaDevice.setSuplaCACert(suplaCACert);
  SuplaDevice.setSupla3rdPartyCACert(supla3rdCACert);

  SuplaDevice.begin(21);
  if (suplaServer.getServerPtr()) {
    httpUpdater.setup(suplaServer.getServerPtr(), "/update");
    suplaServer.getServerPtr()->on("/smartpool", HTTP_GET, handleSmartPoolPage);
    suplaServer.getServerPtr()->on("/smartpool", HTTP_POST, handleSmartPoolSaveAction);
  }
}

// ======================================================
// LOOP
// ======================================================
void loop() {
  SuplaDevice.iterate();
  handleRelayLocks();

  static unsigned long lastTimeAndTempCheck = 0;
  if (millis() - lastTimeAndTempCheck >= 10000) {
    lastTimeAndTempCheck = millis();
    checkHvacSchedulePump();
    if (hvacPool) {
      bool hvacEnabled = hvacPool->getMode() != SUPLA_HVAC_MODE_OFF;
      digitalWrite(RELAY_7_GPIO, hvacEnabled ? HIGH : LOW);
      bool hvacHeating = false;
      double currentTemp = dsSensors[0]->getValue();
      double targetTemp = hvacPool->getTemperatureSetpointHeat() / 100.0;
      double hysteresis = hvacPool->getCurrentHysteresis(false) / 100.0;
      if (hvacEnabled && currentTemp > -100 && currentTemp < (targetTemp - hysteresis)) {
        hvacHeating = true;
      }
      bool mainLockActive = (relayLock && relayLock->isOn());
      if (mainLockActive && lockHeating) hvacHeating = false;
      digitalWrite(RELAY_8_GPIO, hvacHeating ? HIGH : LOW);
    }
  }

  handleRelayDependencies();

  static bool LOCAL_WEB_SERVER = false;
  if (!LOCAL_WEB_SERVER && Supla::Network::IsReady()) {
    LOCAL_WEB_SERVER = true;
    SuplaDevice.handleAction(0, Supla::START_LOCAL_WEB_SERVER);
  }
}
A jeszcze nie pamieta i stanu.

Code: Select all

  virtualRelay = new Supla::Control::VirtualRelay();
  virtualRelay->getChannel()->setChannelNumber(6);
  virtualRelay->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  virtualRelay->setInitialCaption("Harmonogram pompy filtracyjnej");
  virtualRelay->setDefaultStateRestore();
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
klew
Posts: 13907
Joined: Thu Jun 27, 2019 12:16 pm
Location: Wrocław
Has thanked: 134 times
Been thanked: 137 times

Post

Tokenu nie wstawisz samemu. Ta wersja tego nie wymaga.
Daj logi od startu urządzenia, przez zapisanie danych i ponowny restart
Najlepsze suple dla Twojego domu :mrgreen:
User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

Już myślałem ze mi się udało rozwiązać problem, ale jednak nie.
W załączeniu daje logi.
You do not have the required permissions to view the files attached to this post.
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
Lector
Posts: 2470
Joined: Fri Nov 17, 2017 2:26 pm
Location: Poznań
Has thanked: 13 times
Been thanked: 25 times

Post

Takie może głupie pytanie. Czy zapis mojej konfiguracji ma być do tego samego pliku co supla?
W tedy moja konfiguracja nadpisywała ustawienia Supli?

Teraz próbuje rozbić na dwa pliki.
Niespełniony automatyk. :mrgreen:
https://3d-lamp.photos/
https://pool.lector.top/
User avatar
vajera
Posts: 7290
Joined: Wed Oct 31, 2018 7:58 am
Location: Biedrusko
Has thanked: 289 times
Been thanked: 161 times

Post

Lector wrote: Tue Jun 02, 2026 7:21 pm Takie może głupie pytanie. Czy zapis mojej konfiguracji ma być do tego samego pliku co supla?
W tedy moja konfiguracja nadpisywała ustawienia Supli?

Teraz próbuje rozbić na dwa pliki.
Jeżeli używasz klasy Config to możesz zapisywać do tego samego pliku, co Supla. Pamiętaj tylko, że domyślny rozmiar Config to chyba 1024 bajty.
Bramka Zigbee <=> SUPLA
Więcej informacji tutaj:
https://forum.supla.org/viewforum.php?f=127
FAQ https://forum.supla.org/viewtopic.php?t=17277
User avatar
lukfud
Posts: 2480
Joined: Thu Nov 23, 2017 11:33 pm
Location: Warszawa
Has thanked: 13 times
Been thanked: 11 times

Post

Lector wrote: Mon Jun 01, 2026 8:10 pm A jeszcze nie pamieta i stanu.

Code: Select all

  virtualRelay = new Supla::Control::VirtualRelay();
  virtualRelay->getChannel()->setChannelNumber(6);
  virtualRelay->setDefaultFunction(SUPLA_CHANNELFNC_POWERSWITCH);
  virtualRelay->setInitialCaption("Harmonogram pompy filtracyjnej");
  virtualRelay->setDefaultStateRestore();
brakuje:

Code: Select all

#include <supla/storage/eeprom.h>
Supla::Eeprom eeprom;
https://www.facebook.com/groups/supladiy
https://www.facebook.com/groups/suplazamel
https://www.facebook.com/groups/suplaauraton
https://smartnerzeczy.pl

Return to “Pomoc”