Skocz do zawartości

Pomocna odpowiedź

Podoba Ci się ten projekt? Zostaw pozytywny komentarz i daj znać autorowi, że zbudował coś fajnego!

Masz uwagi? Napisz kulturalnie co warto zmienić. Doceń pracę autora nad konstrukcją oraz opisem.

Nadal niestety walczę z połączeniem z rejestratorem. Niby jest ale część powiadomień nie dochodzi. Skrócę timeout reconnecta, bo teraz mam 5 minut zobaczymy. Ale tu znowu pewnie pojawi się problem ilości logowań i blokady ze strony rejestratora. Po południu wrzucę aktualny szkic, może poratujecie.

(edytowany)

Nie testowałem ->  click ale pewnie wypróbowałbym coś takiego. esp cam wstępnie musiałby rozpoznać że coś jest na podjeździe i jeśli uzna że to może być samochód, dodatkowo wysłać do sieci celem pełnej weryfikacji. W ogóle, dałbym sobie w małym palcu paznokieć przyciąć że widziałem takie coś w przykładach od espressif. 

 

Edytowano przez _LM_
(edytowany)
//płytka esp32 dev module
/////////////////////////////////////
#define SUPLA_LOG_LEVEL 0 
/////////////////////////////////////
#include <WiFi.h>
#include <lwip/sockets.h>
#include <MD5Builder.h>
#include <SuplaDevice.h>
#include <supla/network/esp_wifi.h>
#include <supla/control/virtual_relay.h>

// --- KONFIGURACJA ---
const char* ssid = "x";
const char* password = "x";
const char* host = "x";
const char* user = "x";
const char* pass = "x";
const char* url  = "/cgi-bin/eventManager.cgi?action=attach&codes=[CrossLineDetection]&channel=2&heartbeat=30";
IPAddress local_IP(x, x, x, x); // Wybierz wolny adres, np. .150
IPAddress gateway(192, 168, 0, 1);    // Adres Twojego Archera
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);     // DNS Google (opcjonalnie)
char GUID[16] = {x};
char AUTHKEY[16] = {x};

WiFiClient d_client;
Supla::ESPWifi supla_wifi(ssid, password); 
auto vRelay = new Supla::Control::VirtualRelay();

unsigned long lastEventTime = 0;
unsigned long lastDataTime = 0;
String currentLine = "";                 // Deklaracja globalna bufora
volatile bool alarmPending = false;      // Deklaracja globalna flagi (volatile dla Core 0/1)

// --- FUNKCJE POMOCNICZE ---
String md5(String payload) {
  MD5Builder _md5; _md5.begin(); _md5.add(payload); _md5.calculate();
  return _md5.toString();
}

String getDigestAuth(String header) {
  auto getParam = [&](String param) {
    int start = header.indexOf(param + "=\"");
    if (start == -1) return String("");
    start += param.length() + 2;
    return header.substring(start, header.indexOf("\"", start));
  };
  String realm = getParam("realm"), nonce = getParam("nonce"), qop = "auth", nc = "00000001", cnonce = "abcdefgh";
  String ha1 = md5(String(user) + ":" + realm + ":" + String(pass));
  String ha2 = md5("GET:" + String(url));
  String resp = md5(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2);
  return "Digest username=\"" + String(user) + "\", realm=\"" + realm + "\", nonce=\"" + nonce + 
         "\", uri=\"" + url + "\", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + cnonce + 
         "\", response=\"" + resp + "\", algorithm=MD5";
}

// Funkcja chroniąca przed zrywaniem przez router Archer
void setupKeepAlive(int fd) {
  int enable = 1;
  int idle = 5;     // Wyślij pierwszy pakiet po 5s ciszy
  int interval = 3; // Kolejne co 3s
  int count = 2;     // Po 2 nieudanych próbach zamknij
  setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(int));
  setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
  setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(int));
  setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(int));
}

// --- WĄTEK DAHUA (Core 0) ---
void dahuaTask(void * pvParameters) {

  while(1) {
    if (WiFi.status() == WL_CONNECTED) {
      if (!d_client.connected()) {
        d_client.stop(); 
        vTaskDelay(500 / portTICK_PERIOD_MS);
        
        Serial.println("\n[XVR] Logowanie...");
        if (d_client.connect(host, 80)) {
          d_client.print(String("GET ") + url + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n");

          String response = "";
          unsigned long t = millis();
          while (millis() - t < 3000) {
            if (d_client.available()) {
              char c = d_client.read(); // Czytamy znak 
              Serial.write(c);          // Wypisujemy go
              response += c;            // Dodajemy do analizy autoryzacji
              if (response.indexOf("\r\n\r\n") != -1) break;
            }
            vTaskDelay(1);
          }
          
          int authPos = response.indexOf("WWW-Authenticate: Digest");
          if (authPos != -1) {
            String authLine = response.substring(authPos, response.indexOf("\r", authPos));
            d_client.stop();
            vTaskDelay(200); 
            
            if (d_client.connect(host, 80)) {
              setupKeepAlive(d_client.fd()); 
              d_client.print(String("GET ") + url + " HTTP/1.1\r\nHost: " + host + 
                             "\r\nAuthorization: " + getDigestAuth(authLine) + 
                             "\r\nConnection: keep-alive\r\n\r\n");
              
              bool loginSuccess = false;
              unsigned long waitStart = millis();
              while (millis() - waitStart < 5000) {
                if (d_client.available()) {
                  String headerLine = d_client.readStringUntil('\n');
                  if (headerLine.indexOf("200 OK") != -1) { loginSuccess = true; break; }
                }
                vTaskDelay(1);
              }

              if (loginSuccess) {
                Serial.println("[XVR] POŁĄCZONO.");
                lastDataTime = millis();
                currentLine = "";

                while (d_client.connected()) {
                  while (d_client.available() > 0) {
                    char c = d_client.read();
                    lastDataTime = millis(); 
                    currentLine += c;

                    // Szukamy alarmu w locie
                    if (currentLine.indexOf("CrossLineDetection") != -1) {
                        if (currentLine.indexOf("Start") != -1) {
                            Serial.println("\n>>> [XVR] ALARM WYKRYTY !!! <<<");
                            alarmPending = true; // USTAWIANIE FLAGI DLA LOOP
                            currentLine = ""; 
                        }
                    }

                    if (currentLine.length() > 400) {
                      currentLine = currentLine.substring(currentLine.length() - 64);
                    }
                  }

                  if (millis() - lastDataTime > 3600000) { 
                    Serial.println("\n[XVR] Timeout 1h - restart.");
                    break;
                  }
                  vTaskDelay(1); 
                }
              }
            }
          }
        }
        d_client.stop();
        vTaskDelay(5000 / portTICK_PERIOD_MS);
      }
    }
    vTaskDelay(100 / portTICK_PERIOD_MS);
  }
}


void setup() {
  Serial.begin(115200);
   if (!WiFi.config(local_IP, gateway, subnet, primaryDNS)) {
    Serial.println("[WiFi] Błąd konfiguracji statycznego IP!");
  }
  WiFi.setSleep(false);
  currentLine.reserve(512); 
  // Start Supla
  SuplaDevice.begin(GUID, "svrx.supla.org", "[email protected]", AUTHKEY);

  // Uruchomienie Dahua na Core 0 (Supla działa na Core 1)
  xTaskCreatePinnedToCore(dahuaTask, "DahuaTask", 8192, NULL, 1, NULL, 0);
  
  Serial.println("System gotowy.");
}

void loop() {
  SuplaDevice.iterate();

  if (alarmPending) {
    vRelay->turnOn();
    lastEventTime = millis();
    alarmPending = false; // Resetujemy flagę po obsłużeniu
    Serial.println("[Supla] Virtual Relay ON.");
  }

  // Automatyczne wyłączenie po 2 sekundach
  if (vRelay->isOn() && (millis() - lastEventTime > 2000)) {
    vRelay->turnOff();
    Serial.println("[Supla] Virtual Relay OFF.");
  }
}

Sprawdzony kod powyżej. Reaguje na wszystkie powiadomienia z rejestratora. Kilka dni testów. Problem był taki, że rejestrator nie wysyła heartbeat. Router po jakimś czasie bezczynności tez podobno rozłącza połączenie.  Nie wiem , nie znam się. Jednak taki kod już działa bezproblemowo. Funkcja keepalive, podobno działa gdzieś "niżej" robi robotę by router nas nie rozłączał. Dowiedziałem się, że jest też coś takiego jak digest. Robiąc coś czego się nie zna, można się wiele dowiedzieć, a najwięcej tego jak mało się wie.

Na razie nie mam ochoty implementować tego fizycznie. Samo otwieranie bramy przy pomocy kamery jest mi niepotrzebne. Jednak jako ciekawa nauka było to bardzo fajne doświadczenie. Zamówię u majfriendów esp32s3 i spróbuję zbierać klatkę ze streamingu rejestratora i taką klatkę wysyłać do gemini. Jedyny problem to usytuowanie kamery która będzie źródłem dla wysyłanej klatki, duży kąt... Zobaczymy co z tego wyjdzie. Wtedy wystarczyłoby jedno esp32 za 20 zeta umieszczone w zasięgu sieci w której jest rejestrator. Choć i tak musiałbym podjechać pod bramę i czekać na otwarcie bramy. Wolę otworzyć na minutę przed wjazdem.... Jednak doświadczenie programistyczne ciekawe. 

Edytowano przez SOYER
  • Lubię! 2

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...