Odczyt danych z falownika Afore

Masz pomysł na funkcjonalność lub koncepcję na rozwój projektu. Opisz wszystko tutaj.
User avatar
SOYER
Posts: 1623
Joined: Wed Aug 10, 2022 12:29 pm
Location: Kryry
Been thanked: 2 times

Post

Cześć, sprawa rozwiązana, a nawet więcej.
Skontaktował się za mną kolega @elmaya, który przesłał mi zmieniony kod afore.h i afore.cpp oraz przykład rozwiązujący problem pobierania danych które pragnąłem(?).
Z powodu tego, że mój komp był w naprawie, nie mogłem od razu usiąść do implementacji kodu w moim projekcie.
Jakież było moje zdziwienie gdy, po naprawie kompa, zaglądnąłem konkretnie do szkiców @elmaya i okazało się, że szanowny kolega zrobił szkic który pobiera produkcję dzienną prosto z inwertera. Dotychczas myślałem, że z inwertera można pobrać tylko produkcję aktualną(aktualną moc) i produkcję całkowitą danego inwertera. Spodziewałem się, że produkcję dzienną trzeba będzie przeliczać samemu z produkcji całkowitej.
Po implementacji szkicu kolegi @elmaya okazało się, że wszystko pięknie śmiga, esp pobiera produkcję dzienną i pięknie wyświetla ją w KPOP w aplikacji Supla. Jest to nawet wygodniejsze, ponieważ supla pobiera dane znacznie częściej niż dedykowana aplikacja dla inwertera i mamy info o produkcji bez żadnych opóźnień.
Potem zostało mi tylko zaimplementowanie pobierania tych danych przez MQTT dla mojego wyświetlacza 4" opartego o ESP32, szerzej opisanego tutaj:
viewtopic.php?t=16343

Poniżej wklejam (za zgodą @elamya) pliki afore.cpp

Code: Select all

/*
 Copyright (C) AC SOFTWARE SP. Z O.O.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <stdlib.h>
#include <string.h>
#include <supla/log_wrapper.h>
#include <supla/time.h>

#include "afore.h"

namespace Supla {
namespace PV {

Afore::Afore(IPAddress ip, int port, const char *loginAndPass)
    : ip(ip),
      port(port),
      buf(),
      totalGeneratedEnergy(0),
	  todayProd(0.0),
      currentPower(0),
      bytesCounter(0),
      retryCounter(0),
      vFound(false),
      varFound(false),
      dataIsReady(false),
      dataFetchInProgress(false),
      connectionTimeoutMs(0) {
  refreshRateSec = 15;
  int len = strlen(loginAndPass);
  if (len > LOGIN_AND_PASSOWORD_MAX_LENGTH) {
    len = LOGIN_AND_PASSOWORD_MAX_LENGTH;
  }
  strncpy(loginAndPassword, loginAndPass, len);
  client = Supla::ClientBuilder();
}

void Afore::iterateAlways() {
  if (dataFetchInProgress) {
    if (millis() - connectionTimeoutMs > 30000) {
      SUPLA_LOG_DEBUG(
                "AFORE: connection timeout. Remote host is not responding");
      client->stop();
      dataFetchInProgress = false;
      dataIsReady = false;
      return;
    }
    if (!client->connected()) {
      SUPLA_LOG_DEBUG("AFORE fetch completed");
      dataFetchInProgress = false;
      dataIsReady = true;
    }
    if (client->available()) {
      SUPLA_LOG_DEBUG("Reading data from afore: %d", client->available());
    }
    while (client->available()) {
      char c;
      c = client->read();
      if (c == '\n') {
        if (varFound) {
          if (bytesCounter > 79) bytesCounter = 79;
          buf[bytesCounter] = '\0';
          char varName[80];
          char varValue[80];
          sscanf(buf, "%s = \"%s\";", varName, varValue);
          if (strncmp(varName, "webdata_now_p", strlen("webdata_now_p")) == 0) {
            float curPower = atof(varValue);
            currentPower = curPower * 100000;
          }
          if (strncmp(varName, "webdata_total_e", strlen("webdata_total_e")) ==
              0) {
            float totalProd = atof(varValue);

            totalGeneratedEnergy = totalProd * 100000;
          }
          if (strncmp(varName, "webdata_today_e", strlen("webdata_today_e")) ==
              0) {
            todayProd = atof(varValue);
          }		  
        }
        bytesCounter = 0;
        vFound = false;
        varFound = false;
      } else if (c == 'v' || vFound) {
        vFound = true;
        if (bytesCounter < 80) {
          buf[bytesCounter] = c;
        }
        bytesCounter++;
        if (bytesCounter == 4 && !varFound) {
          if (strncmp(buf, "var ", 4) == 0) {
            varFound = true;
            bytesCounter = 0;
          }
        }
      }
    }
    if (!client->connected()) {
      client->stop();
    }
  }
  if (dataIsReady) {
    dataIsReady = false;
    setFwdActEnergy(0, totalGeneratedEnergy);
    setPowerActive(0, currentPower);
    updateChannelValues();
  }
}

bool Afore::iterateConnected() {
  if (!dataFetchInProgress) {
    if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec * 1000) {
      lastReadTime = millis();
      SUPLA_LOG_DEBUG("AFORE connecting");
      if (client->connect(ip, port)) {
        retryCounter = 0;
        dataFetchInProgress = true;
        connectionTimeoutMs = lastReadTime;

        client->print("GET /status.html HTTP/1.1\nAuthorization: Basic ");
        client->println(loginAndPassword);
        client->println("Connection: close");
        client->println();

      } else {  // if connection wasn't successful, try few times. If it fails,
                // then assume that inverter is off during the night
        SUPLA_LOG_DEBUG("Failed to connect to Afore");
        retryCounter++;
        if (retryCounter > 3) {
          currentPower = 0;
          dataIsReady = true;
        }
      }
    }
  }
  return Element::iterateConnected();
}

float Afore::getTodayProd() {
  return todayProd;	
}

void Afore::readValuesFromDevice() {
}

}  // namespace PV
}  // namespace Supla
afore.h:

Code: Select all

/*
 Copyright (C) AC SOFTWARE SP. Z O.O.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef SRC_SUPLA_PV_AFORE_H_
#define SRC_SUPLA_PV_AFORE_H_

#include <IPAddress.h>
#include <supla/sensor/one_phase_electricity_meter.h>
#include <supla/network/client.h>

#define LOGIN_AND_PASSOWORD_MAX_LENGTH 100

namespace Supla {
namespace PV {
class Afore : public Supla::Sensor::OnePhaseElectricityMeter {
 public:
  Afore(IPAddress ip, int port, const char *loginAndPassword);
  void readValuesFromDevice();
  void iterateAlways();
  bool iterateConnected();
  float getTodayProd();

 protected:
  ::Supla::Client *client = nullptr;
  IPAddress ip;
  int port;
  char loginAndPassword[LOGIN_AND_PASSOWORD_MAX_LENGTH];
  char buf[80];
  unsigned _supla_int64_t totalGeneratedEnergy;
  float todayProd;
  _supla_int_t currentPower;
  int bytesCounter;
  int retryCounter;
  bool vFound;
  bool varFound;
  bool dataIsReady;
  bool dataFetchInProgress;
  uint32_t connectionTimeoutMs;
};
};  // namespace PV
};  // namespace Supla

#endif  // SRC_SUPLA_PV_AFORE_H_
mój plik oparty na powyższym i dokładający KPOP do wyświetlenia dziennej produkcji:

Code: Select all

#include <SuplaDevice.h>
#include <supla/pv/afore.h>
#include <supla/sensor/general_purpose_measurement.h>
#include <Timers.h>

// Choose proper network interface for your card:
#ifdef ARDUINO_ARCH_AVR
  // Arduino Mega with EthernetShield W5100:
  #include <supla/network/ethernet_shield.h>
  // Ethernet MAC address
  uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
  Supla::EthernetShield ethernet(mac);

  // Arduino Mega with ENC28J60:
  // #include <supla/network/ENC28J60.h>
  // Supla::ENC28J60 ethernet(mac);
#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
  // ESP8266 and ESP32 based board:
  #include <supla/network/esp_wifi.h>
  Supla::ESPWifi wifi("xxx", "xxx");
#endif

Timer minuta;
Supla::Sensor::GeneralPurposeMeasurement *dailyProduct = nullptr;
Supla::PV::Afore *sofar = nullptr;

void setup() {

  Serial.begin(9600);

  // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid
  char GUID[SUPLA_GUID_SIZE] = {xxx};

  // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey
  char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {xxx};

  // CHANNEL0
  // Put IP address of your Afore inverter, then port, and last parametere is base64 encoded "login:password"
  // You can use any online base64 encoder to convert your login and password, i.e. https://www.base64encode.org/
sofar = new Supla::PV::Afore(IPAddress(xxx), 80, "xxx");//login:password--->>admin:admin
dailyProduct = new Supla::Sensor::GeneralPurposeMeasurement();
 
minuta.begin(9999);
dailyProduct->setValue(sofar->getTodayProd());
  SuplaDevice.begin(GUID,              // Global Unique Identifier 
                    "svrxx.supla.org",  // SUPLA server address
                    "xxx",   // Email address used to login to Supla Cloud
                    AUTHKEY);          // Authorization key
}

void loop() {
  SuplaDevice.iterate();
  dailyProduction();
}

void dailyProduction(){
  if(minuta.available()){
    dailyProduct->setValue(sofar->getTodayProd());
    minuta.restart();
  }
}
Poniżej zdjęcia i film, wszystko działa:)
Dziękuję wszystkim za pomoc szczególnie oczywiście @elmaya.


https://youtube.com/shorts/rOZB51g2yjk? ... O5vOUrd0l6
You do not have the required permissions to view the files attached to this post.
https://youtube.com/@tomaszhamer8134?si=_J1cT_QWsXxgt1Fm
https://kryry01.aqi.eco/pl
https://app.weathercloud.net/d4311785603
https://github.com/Soyer79

Return to “Pomysły i koncepcje”