diff --git a/examples/Fronius/Fronius3p.ino b/examples/Fronius/Fronius3p.ino new file mode 100644 index 00000000..d9f0263f --- /dev/null +++ b/examples/Fronius/Fronius3p.ino @@ -0,0 +1,80 @@ +/* +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 +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + // CHANNEL0 + // Put IP address of your Fronius 3 phase inverter, then port (deafult is 80) + new Supla::PV::Fronius3p(IPAddress(192, 168, 0, 59)); + // CHANNEL1 + // Put IP address of your Fronius inventer connected to 3 phase Smart Meter + new Supla::PV::Fronius3pmeter(IPAddress(192, 168, 0, 59)); + + // You can use one of those channels or both simultaneously. + // You have to delete device on SUPLA server every time you decide to use/unuse channel. + + /* + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key +} + +void loop() { + SuplaDevice.iterate(); +} + diff --git a/src/supla/pv/fronius3p.cpp b/src/supla/pv/fronius3p.cpp new file mode 100644 index 00000000..2eb15d2e --- /dev/null +++ b/src/supla/pv/fronius3p.cpp @@ -0,0 +1,289 @@ +/* + 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 "fronius3p.h" + +using namespace Supla; +using namespace PV; + +enum ParametersToRead { NONE, TOTAL_ENERGY, FAC, IAC, PAC, UAC, + IAC_L1, IAC_L2, IAC_L3, UAC_L1, UAC_L2, UAC_L3 }; + +Fronius3p::Fronius3p(IPAddress ip, int port, int deviceId) + : ip(ip), + port(port), + buf(), + totalGeneratedEnergy(0), + currentPower(0), + currentActivePower{}, + currentCurrent{}, + currentFreq(0), + currentVoltage{}, + bytesCounter(0), + retryCounter(0), + valueToFetch(NONE), + deviceId(deviceId), + startCharFound(false), + dataIsReady(false), + dataFetchInProgress(false), + connectionTimeoutMs(0), + fetch3p(false) { + refreshRateSec = 15; +} + +void Fronius3p::iterateAlways() { + if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("Fronius: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } + if (!pvClient.connected()) { + Serial.println(F("Fronius fetch completed")); + dataFetchInProgress = false; + dataIsReady = true; + } + if (pvClient.available()) { + Serial.print(F("Reading data from Fronius: ")); + Serial.println(pvClient.available()); + } + while (pvClient.available()) { + char c; + c = pvClient.read(); + if (c == '\n') { + if (startCharFound) { + if (bytesCounter > 79) bytesCounter = 79; + buf[bytesCounter] = '\0'; + char varName[80]; + char varValue[80]; + sscanf(buf, " %s : %s", varName, varValue); + if (valueToFetch != NONE && strncmp(varName, "Value", strlen("Value")) == 0) { + switch (valueToFetch) { + case TOTAL_ENERGY: { + float totalProd = atof(varValue); + Serial.print(F("Total production: ")); + Serial.print(totalProd); + Serial.println(F(" Wh")); + totalGeneratedEnergy = totalProd * 100; + + break; + } + case PAC: { + float curPower = atof(varValue); + Serial.print(F("Current power: ")); + Serial.println(curPower); + currentPower = curPower * 100000; + + break; + } + case IAC: { + float curCurrent = atof(varValue); + Serial.print(F("Current: ")); + Serial.println(curCurrent); + //currentCurrent[0] = curCurrent * 1000; + + break; + } + case FAC: { + float curFreq = atof(varValue); + Serial.print(F("Frequency: ")); + Serial.println(curFreq); + currentFreq = curFreq * 100; + + break; + } + case UAC: { + float curVoltage = atof(varValue); + Serial.print(F("Voltage: ")); + Serial.println(curVoltage); + //currentVoltage[0] = curVoltage * 100; + + break; + } + case IAC_L1: { + float curCurrent = atof(varValue); + Serial.print(F("Current Phase 1: ")); + Serial.println(curCurrent); + currentCurrent[0] = curCurrent * 1000; + + break; + } + case IAC_L2: { + float curCurrent = atof(varValue); + Serial.print(F("Current Phase 2: ")); + Serial.println(curCurrent); + currentCurrent[1] = curCurrent * 1000; + + break; + } + case IAC_L3: { + float curCurrent = atof(varValue); + Serial.print(F("Current Phase 3: ")); + Serial.println(curCurrent); + currentCurrent[2] = curCurrent * 1000; + + break; + } + case UAC_L1: { + float curVoltage = atof(varValue); + Serial.print(F("Voltage Phase 1: ")); + Serial.println(curVoltage); + currentVoltage[0] = curVoltage * 100; + + break; + } + case UAC_L2: { + float curVoltage = atof(varValue); + Serial.print(F("Voltage Phase 2: ")); + Serial.println(curVoltage); + currentVoltage[1] = curVoltage * 100; + + break; + } + case UAC_L3: { + float curVoltage = atof(varValue); + Serial.print(F("Voltage Phase 3: ")); + Serial.println(curVoltage); + currentVoltage[2] = curVoltage * 100; + + break; + } + } + valueToFetch = NONE; + } else if (strncmp(varName, "IAC_L1", strlen("IAC_L1")) == 0) { + valueToFetch = IAC_L1; + } else if (strncmp(varName, "IAC_L2", strlen("IAC_L2")) == 0) { + valueToFetch = IAC_L2; + } else if (strncmp(varName, "IAC_L3", strlen("IAC_L3")) == 0) { + valueToFetch = IAC_L3; + } else if (strncmp(varName, "UAC_L1", strlen("UAC_L1")) == 0) { + valueToFetch = UAC_L1; + } else if (strncmp(varName, "UAC_L2", strlen("UAC_L2")) == 0) { + valueToFetch = UAC_L2; + } else if (strncmp(varName, "UAC_L3", strlen("UAC_L3")) == 0) { + valueToFetch = UAC_L3; + } else if (strncmp(varName, "TOTAL_ENERGY\"", strlen("TOTAL_ENERGY")) == 0) { + valueToFetch = TOTAL_ENERGY; + } else if (strncmp(varName, "FAC", strlen("FAC")) == 0) { + valueToFetch = FAC; + } else if (strncmp(varName, "UAC", strlen("UAC")) == 0) { + valueToFetch = UAC; + } else if (strncmp(varName, "IAC", strlen("IAC")) == 0) { + valueToFetch = IAC; + } else if (strncmp(varName, "PAC", strlen("PAC")) == 0) { + valueToFetch = PAC; + } + } + bytesCounter = 0; + startCharFound = false; + } else if (c == '"' || startCharFound) { + startCharFound = true; + if (c == '"') { + c = ' '; + } + if (bytesCounter < 80) { + buf[bytesCounter] = c; + } + bytesCounter++; + } + } + if (!pvClient.connected()) { + pvClient.stop(); + } + } + if (dataIsReady) { + dataIsReady = false; + // reverse logic + if (fetch3p) { + setFwdActEnergy(0, totalGeneratedEnergy); + setFreq(currentFreq); + } else { + setCurrent(0, currentCurrent[0]); + setVoltage(0, currentVoltage[0]); + setPowerActive(0, currentCurrent[0] * currentVoltage[0]); + setCurrent(1, currentCurrent[1]); + setVoltage(1, currentVoltage[1]); + setPowerActive(1, currentCurrent[1] * currentVoltage[1]); + setCurrent(2, currentCurrent[2]); + setVoltage(2, currentVoltage[2]); + setPowerActive(2, currentCurrent[2] * currentVoltage[2]); + //setPowerActive(0, currentPower); + } + updateChannelValues(); + } +} + +bool Fronius3p::iterateConnected(void *srpc) { + if (!dataFetchInProgress) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + Serial.print(F("Fronius connecting ")); + Serial.println(deviceId); + if (pvClient.connect(ip, port)) { + retryCounter = 0; + dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; + Serial.println(F("Succesful connect")); + + char buf[100]; + char idBuf[20]; + sprintf(idBuf, "%d", deviceId); + if (fetch3p) { + fetch3p = false; + strcpy(buf, "GET /solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceID="); + strcat(buf, idBuf); + strcat(buf, "&DataCollection=3PInverterData HTTP/1.1"); + pvClient.println(buf); + } else { + fetch3p = true; + strcpy(buf, "GET /solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceID="); + strcat(buf, idBuf); + strcat(buf, "&DataCollection=CommonInverterData HTTP/1.1"); + pvClient.println(buf); + } + pvClient.println("Host: localhost"); + pvClient.println("Connection: close"); + pvClient.println(); + + } else { // if connection wasn't successful, try few times. If it fails, + // then assume that inverter is off during the night + Serial.print(F("Failed to connect to Fronius at: ")); + Serial.print(ip); + Serial.print(F(":")); + Serial.println(port); + retryCounter++; + if (retryCounter > 3) { + //currentPower = 0; + currentFreq = 0; + currentCurrent[0] = 0; + currentVoltage[0] = 0; + currentCurrent[1] = 0; + currentVoltage[1] = 0; + currentCurrent[2] = 0; + currentVoltage[2] = 0; + dataIsReady = true; + } + } + } + } + return Element::iterateConnected(srpc); +} + +void Fronius3p::readValuesFromDevice() { +} + diff --git a/src/supla/pv/fronius3p.h b/src/supla/pv/fronius3p.h new file mode 100644 index 00000000..cfd976d2 --- /dev/null +++ b/src/supla/pv/fronius3p.h @@ -0,0 +1,66 @@ +/* + 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 __fronius3p_h +#define __fronius3p_h + +#include +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#else +#include +#endif + +namespace Supla { +namespace PV { +class Fronius3p : public Supla::Sensor::ElectricityMeter { + public: + Fronius3p(IPAddress ip, int port = 80, int deviceId = 1); + void readValuesFromDevice(); + void iterateAlways(); + bool iterateConnected(void *srpc); + + protected: +#if defined(ARDUINO_ARCH_AVR) + EthernetClient pvClient; +#else + WiFiClient pvClient; +#endif + IPAddress ip; + int port; + char buf[80]; + unsigned _supla_int64_t totalGeneratedEnergy; + _supla_int_t currentPower; + _supla_int_t currentActivePower[3]; + unsigned _supla_int_t currentCurrent[3]; + unsigned _supla_int16_t currentFreq; + unsigned _supla_int16_t currentVoltage[3]; + int bytesCounter; + int retryCounter; + int valueToFetch; + int deviceId; + bool startCharFound; + bool dataIsReady; + bool dataFetchInProgress; + unsigned long connectionTimeoutMs; + bool fetch3p; +}; +}; // namespace PV +}; // namespace Supla + +#endif diff --git a/src/supla/pv/fronius3pmeter.cpp b/src/supla/pv/fronius3pmeter.cpp new file mode 100644 index 00000000..43fd144f --- /dev/null +++ b/src/supla/pv/fronius3pmeter.cpp @@ -0,0 +1,334 @@ +/* + 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 "fronius3pmeter.h" + +using namespace Supla; +using namespace PV; + +enum ParametersToRead { NONE, + Current_AC_Phase_1, + Current_AC_Phase_2, + Current_AC_Phase_3, + EnergyReactive_VArAC_Sum_Consumed, + EnergyReactive_VArAC_Sum_Produced, + EnergyReal_WAC_Sum_Consumed, + EnergyReal_WAC_Sum_Produced, + Frequency_Phase_Average, + PowerApparent_S_Phase_1, + PowerApparent_S_Phase_2, + PowerApparent_S_Phase_3, + PowerFactor_Phase_1, + PowerFactor_Phase_2, + PowerFactor_Phase_3, + PowerReactive_Q_Phase_1, + PowerReactive_Q_Phase_2, + PowerReactive_Q_Phase_3, + PowerReal_P_Phase_1, + PowerReal_P_Phase_2, + PowerReal_P_Phase_3, + Voltage_AC_Phase_1, + Voltage_AC_Phase_2, + Voltage_AC_Phase_3 }; + +Fronius3pmeter::Fronius3pmeter(IPAddress ip, int port, int deviceId) + : ip(ip), + port(port), + buf(), + FwdReactEnergy(0), + RvrReactEnergy(0), + FwdActEnergy(0), + RvrActEnergy(0), + currentPower(0), + currentActivePower{}, + currentApparentPower{}, + currentReactivePower{}, + currentPowerFactor{}, + currentCurrent{}, + currentFreq(0), + currentVoltage{}, + bytesCounter(0), + retryCounter(0), + valueToFetch(NONE), + deviceId(deviceId), + startCharFound(false), + dataIsReady(false), + dataFetchInProgress(false), + connectionTimeoutMs(0) { + refreshRateSec = 15; +} + +void Fronius3pmeter::iterateAlways() { + if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("Fronius: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } + if (!pvClient.connected()) { + Serial.println(F("Fronius fetch completed")); + dataFetchInProgress = false; + dataIsReady = true; + } + if (pvClient.available()) { + Serial.print(F("Reading data from Fronius: ")); + Serial.println(pvClient.available()); + } + while (pvClient.available()) { + char c; + c = pvClient.read(); + if (c == '\n') { + if (startCharFound) { + if (bytesCounter > 79) bytesCounter = 79; + // remove last char if it is a comma + if (buf[bytesCounter - 1] == ','){ + buf[bytesCounter - 1] = '\0'; + } else { + buf[bytesCounter] = '\0'; + } + char varName[80]; + char varValue[80]; + sscanf(buf, " %s : %s", varName, varValue); + if (strncmp(varName, "Current_AC_Phase_1", strlen("Current_AC_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current current 1: ")); + Serial.println(valueToFetch); + currentCurrent[0] = valueToFetch * 1000; + } else if (strncmp(varName, "Current_AC_Phase_2", strlen("Current_AC_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current current 2: ")); + Serial.println(valueToFetch); + currentCurrent[1] = valueToFetch * 1000; + } else if (strncmp(varName, "Current_AC_Phase_3", strlen("Current_AC_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current current 3: ")); + Serial.println(valueToFetch); + currentCurrent[2] = valueToFetch * 1000; + } else if (strncmp(varName, "EnergyReactive_VArAC_Sum_Consumed", strlen("EnergyReactive_VArAC_Sum_Consumed")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current reactive energy consumed: ")); + Serial.println(valueToFetch); + FwdReactEnergy = valueToFetch * 100; + } else if (strncmp(varName, "EnergyReactive_VArAC_Sum_Produced", strlen("EnergyReactive_VArAC_Sum_Produced")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current reactive energy produced: ")); + Serial.println(valueToFetch); + RvrReactEnergy = valueToFetch * 100; + } else if (strncmp(varName, "EnergyReal_WAC_Sum_Consumed", strlen("EnergyReal_WAC_Sum_Consumed")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current active energy consumed: ")); + Serial.println(valueToFetch); + FwdActEnergy = valueToFetch * 100; + } else if (strncmp(varName, "EnergyReal_WAC_Sum_Produced", strlen("EnergyReal_WAC_Sum_Produced")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current active energy produced: ")); + Serial.println(valueToFetch); + RvrActEnergy = valueToFetch * 100; + } else if (strncmp(varName, "Frequency_Phase_Average", strlen("Frequency_Phase_Average")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current frequency: ")); + Serial.println(valueToFetch); + currentFreq = valueToFetch * 100; + } else if (strncmp(varName, "PowerApparent_S_Phase_1", strlen("PowerApparent_S_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power apparent 1: ")); + Serial.println(valueToFetch); + currentApparentPower[0] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerApparent_S_Phase_2", strlen("PowerApparent_S_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power apparent 2: ")); + Serial.println(valueToFetch); + currentApparentPower[1] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerApparent_S_Phase_3", strlen("PowerApparent_S_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power apparent 3: ")); + Serial.println(valueToFetch); + currentApparentPower[2] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerFactor_Phase_1", strlen("PowerFactor_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power factor 1: ")); + Serial.println(valueToFetch); + currentPowerFactor[0] = valueToFetch * 1000; + } else if (strncmp(varName, "PowerFactor_Phase_2", strlen("PowerFactor_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power factor 2: ")); + Serial.println(valueToFetch); + currentPowerFactor[1] = valueToFetch * 1000; + } else if (strncmp(varName, "PowerFactor_Phase_3", strlen("PowerFactor_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power factor 3: ")); + Serial.println(valueToFetch); + currentPowerFactor[2] = valueToFetch * 1000; + } else if (strncmp(varName, "PowerReactive_Q_Phase_1", strlen("PowerReactive_Q_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power reactive 1: ")); + Serial.println(valueToFetch); + currentReactivePower[0] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerReactive_Q_Phase_2", strlen("PowerReactive_Q_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power reactive 2: ")); + Serial.println(valueToFetch); + currentReactivePower[1] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerReactive_Q_Phase_3", strlen("PowerReactive_Q_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power reactive 3: ")); + Serial.println(valueToFetch); + currentReactivePower[2] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerReal_P_Phase_1", strlen("PowerReal_P_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power active 1: ")); + Serial.println(valueToFetch); + currentActivePower[0] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerReal_P_Phase_2", strlen("PowerReal_P_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power active 2: ")); + Serial.println(valueToFetch); + currentActivePower[1] = valueToFetch * 100000; + } else if (strncmp(varName, "PowerReal_P_Phase_3", strlen("PowerReal_P_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current power active 3: ")); + Serial.println(valueToFetch); + currentActivePower[2] = valueToFetch * 100000; + } else if (strncmp(varName, "Voltage_AC_Phase_1", strlen("Voltage_AC_Phase_1")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current voltage 1: ")); + Serial.println(valueToFetch); + currentVoltage[0] = valueToFetch * 100; + } else if (strncmp(varName, "Voltage_AC_Phase_2", strlen("Voltage_AC_Phase_2")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current voltage 2: ")); + Serial.println(valueToFetch); + currentVoltage[1] = valueToFetch * 100; + } else if (strncmp(varName, "Voltage_AC_Phase_3", strlen("Voltage_AC_Phase_3")) == 0) { + valueToFetch = atof(varValue); + Serial.print(F("Current voltage 3: ")); + Serial.println(valueToFetch); + currentVoltage[2] = valueToFetch * 100; + } + } + bytesCounter = 0; + startCharFound = false; + } else if (c == '"' || startCharFound) { + startCharFound = true; + if (c == '"') { + c = ' '; + } + if (bytesCounter < 80) { + buf[bytesCounter] = c; + } + bytesCounter++; + } + } + if (!pvClient.connected()) { + pvClient.stop(); + } + } + if (dataIsReady) { + dataIsReady = false; + setFwdActEnergy(0, FwdActEnergy); + setRvrActEnergy(0, RvrActEnergy); + setFwdReactEnergy(0, FwdReactEnergy); + setRvrReactEnergy(0, RvrReactEnergy); + setVoltage(0, currentVoltage[0]); + setVoltage(1, currentVoltage[1]); + setVoltage(2, currentVoltage[2]); + setCurrent(0, currentCurrent[0]); + setCurrent(1, currentCurrent[1]); + setCurrent(2, currentCurrent[2]); + setFreq(currentFreq); + setPowerActive(0, currentActivePower[0]); + setPowerActive(1, currentActivePower[1]); + setPowerActive(2, currentActivePower[2]); + setPowerReactive(0, currentReactivePower[0]); + setPowerReactive(1, currentReactivePower[1]); + setPowerReactive(2, currentReactivePower[2]); + setPowerApparent(0, currentApparentPower[0]); + setPowerApparent(1, currentApparentPower[1]); + setPowerApparent(2, currentApparentPower[2]); + setPowerFactor(0, currentPowerFactor[0]); + setPowerFactor(1, currentPowerFactor[1]); + setPowerFactor(2, currentPowerFactor[2]); + updateChannelValues(); + } +} + +bool Fronius3pmeter::iterateConnected(void *srpc) { + if (!dataFetchInProgress) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + Serial.print(F("Fronius connecting ")); + Serial.println(deviceId); + if (pvClient.connect(ip, port)) { + retryCounter = 0; + dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; + Serial.println(F("Succesful connect")); + + char buf[100]; + strcpy(buf, "GET /solar_api/v1/GetMeterRealtimeData.cgi?Scope=Device&DeviceId="); + char idBuf[20]; + sprintf(idBuf, "%d", deviceId); + strcat(buf, idBuf); + strcat(buf, " HTTP/1.1"); + pvClient.println(buf); + pvClient.println("Host: localhost"); + pvClient.println("Connection: close"); + pvClient.println(); + + } else { // if connection wasn't successful, try few times. If it fails, + // then assume that inverter is off during the night + Serial.print(F("Failed to connect to Fronius at: ")); + Serial.print(ip); + Serial.print(F(":")); + Serial.println(port); + retryCounter++; + if (retryCounter > 3) { + FwdActEnergy = 0; + RvrActEnergy = 0; + FwdReactEnergy = 0; + RvrReactEnergy = 0; + currentVoltage[0] = 0; + currentVoltage[1] = 0; + currentVoltage[2] = 0; + currentCurrent[0] = 0; + currentCurrent[1] = 0; + currentCurrent[2] = 0; + currentFreq = 0; + currentActivePower[0] = 0; + currentActivePower[1] = 0; + currentActivePower[2] = 0; + currentReactivePower[0] = 0; + currentReactivePower[1] = 0; + currentReactivePower[2] = 0; + currentApparentPower[0] = 0; + currentApparentPower[1] = 0; + currentApparentPower[2] = 0; + currentPowerFactor[0] = 0; + currentPowerFactor[1] = 0; + currentPowerFactor[2] = 0; + dataIsReady = true; + } + } + } + } + return Element::iterateConnected(srpc); +} + +void Fronius3pmeter::readValuesFromDevice() { +} + diff --git a/src/supla/pv/fronius3pmeter.h b/src/supla/pv/fronius3pmeter.h new file mode 100644 index 00000000..2ac4e0f5 --- /dev/null +++ b/src/supla/pv/fronius3pmeter.h @@ -0,0 +1,71 @@ +/* + 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 __fronius3pmeter_h +#define __fronius3pmeter_h + +#include +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#else +#include +#endif + +namespace Supla { +namespace PV { +class Fronius3pmeter : public Supla::Sensor::ElectricityMeter { + public: + Fronius3pmeter(IPAddress ip, int port = 80, int deviceId = 0); + void readValuesFromDevice(); + void iterateAlways(); + bool iterateConnected(void *srpc); + + protected: +#if defined(ARDUINO_ARCH_AVR) + EthernetClient pvClient; +#else + WiFiClient pvClient; +#endif + IPAddress ip; + int port; + char buf[80]; + unsigned _supla_int64_t FwdReactEnergy; + unsigned _supla_int64_t RvrReactEnergy; + unsigned _supla_int64_t FwdActEnergy; + unsigned _supla_int64_t RvrActEnergy; + _supla_int_t currentPower; + _supla_int_t currentActivePower[3]; + _supla_int_t currentApparentPower[3]; + _supla_int_t currentReactivePower[3]; + _supla_int_t currentPowerFactor[3]; + unsigned _supla_int_t currentCurrent[3]; + unsigned _supla_int16_t currentFreq; + unsigned _supla_int16_t currentVoltage[3]; + int bytesCounter; + int retryCounter; + float valueToFetch; + int deviceId; + bool startCharFound; + bool dataIsReady; + bool dataFetchInProgress; + unsigned long connectionTimeoutMs; +}; +}; // namespace PV +}; // namespace Supla + +#endif