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
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_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();
}
}Dziękuję wszystkim za pomoc szczególnie oczywiście @elmaya.
https://youtube.com/shorts/rOZB51g2yjk? ... O5vOUrd0l6
