// Uitlezen van Growatt MIC 2000TL-X via RS-485 met een Wemos mini D1 (ESP8266) // zie https://nl.aliexpress.com/item/32807187263.html // https://ae01.alicdn.com/kf/H0a1c0d7d6bd345a89d4742b62454d734m/Ttl-Beurt-Om-RS485-Module-Hardware-Automatische-Flow-Control-Module-Seri-le-Uart-Niveau-Wederzijdse-Conversie.jpg #include #include #include #include // van https://github.com/emelianov/modbus-esp8266 dd. 19-8-2021 #include #include "settings.h" // Hier staan alle passwords en dergelijke in: #define wifi_ssid WIFI_SSID #define wifi_password WIFI_PASSWORD #define mqtt_server MQTT_SERVER #define mqtt_port MQTT_PORT #define mqtt_user MQTT_USER #define mqtt_password MQTT_PASSWORD #define in_topic "/inverter/getdata" #define PIN_LED_FAULT 4 // D2 #define PIN_LED_COMM 5 // D1 #define PIN_TX 12 //D6 #define PIN_RX 14 //D5 #define SLAVE_ID 1 #define FIRST_REG 0 #define REG_COUNT 25 WiFiClient espClient; PubSubClient client; SoftwareSerial S(D5, D6); // RX = D5, TX = D6 ModbusRTU mb; uint16_t res[REG_COUNT]; float ppv = 0.0; // #0 float ul1 = 0.0; // #3 float il1 = 0.0; // #4 float pac1 = 0.0; // #5 (H) // #6 (L) float tnf = 0.0; // #37 (13 volgens pdf) float pac = 0.0; // #41 (12 volgens pdf) float kdy = 0.0; // #54 (27 volgens pdf) float kt0 = 0.0; // #56 (29 volgens pdf) float tdtotal = 0.0; // #57 (H) // #58 (L) float tkk = 0.0; // #93 (32 volgens pdf) uint16_t inverterStatus = 0; const char* bericht = "0"; String allData; unsigned long last_poll = 0; bool serialdebug = true; bool noError = false; void setup() { Serial.begin(115200); Serial.println("Growatt MQTT Datalogger 19-8-2021"); pinMode(PIN_LED_FAULT, OUTPUT); pinMode(PIN_LED_COMM, OUTPUT); digitalWrite(PIN_LED_FAULT, HIGH); digitalWrite(PIN_LED_COMM, HIGH); WiFi.begin(wifi_ssid, wifi_password); while (WiFi.status() != WL_CONNECTED) { delay(500); } digitalWrite(PIN_LED_FAULT, WiFi.status() ? 1 : 0); client.setClient(espClient); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); ArduinoOTA.begin(); digitalWrite(PIN_LED_FAULT, LOW); digitalWrite(PIN_LED_COMM, LOW); S.begin(9600, SWSERIAL_8N1); mb.begin(&S); mb.master(); } bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Callback to monitor errors noError = true; if (event != Modbus::EX_SUCCESS) { Serial.println(); Serial.print("Request result: 0x"); Serial.print(event, HEX); Serial.println(" (0xE04 == timeout)"); noError = false; } return true; } void clearArray() { for (int n = 0; n < REG_COUNT; n++) { res[n] = 0; } } void reconnect() { // Loop until we're reconnected while (!client.connected()) { //Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("GrowattInverter", mqtt_user, mqtt_password)) { //Serial.println("connected"); // Once connected, publish an announcement... client.publish("/inverter/status", "init"); // ... and resubscribe client.subscribe(in_topic); } else { //Serial.print("failed, rc="); //Serial.print(client.state()); //Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void callback(char* topic, byte* payload, unsigned int length) { last_poll = millis(); } void publishStatus(uint16_t st) { char buf[16]; String str = "unknown"; switch (st) { case 0: str = "waiting"; digitalWrite(PIN_LED_COMM, HIGH); break; case 1: str = "normal"; digitalWrite(PIN_LED_COMM, LOW); digitalWrite(PIN_LED_FAULT, LOW); break; case 3: str = "fault"; digitalWrite(PIN_LED_FAULT, HIGH); break; } str.toCharArray(buf, 16); client.publish("/inverter/status", buf, true); } void loopModbus() { // Eén op 1 overgenomen uit "Modbus_masterSync_Growatt_v2_1.ino" bool readingsOK = true; // 1 if (!mb.slave()) { // Check if no transaction in progress // Array vullen met nullen. Als je dat niet doet staat er rommel in. clearArray(); mb.readIreg(SLAVE_ID, 0, res, 6, cb); // Send Read Ireg from Modbus Server (SLAVE_ID, FIRST_REG, res, REG_COUNT, cb) while (mb.slave()) { // Check if transaction is active mb.task(); yield(); } if (noError) { ppv = res[0]; ul1 = res[3] / 10.0; il1 = res[4] / 10.0; //pac1 = ((res[5] << 16) + res[6]) / 10.0; pac1 = ((res[1] << 16) + res[2]) / 10.0; inverterStatus = ppv; if (serialdebug) { Serial.print("(Inverter status) ppv:"); Serial.print(ppv); Serial.print(", (PV1 input volt) ul1:"); Serial.print(ul1); Serial.print(", (PV1 input ampere) il1:"); Serial.print(il1); Serial.print(", (PV1 input watt) pac1:"); Serial.print(pac1); } if (ppv == 0) ppv = 20003; // aanloop if (ppv == 1) ppv = 20004; // mpp bedrijf if (ppv == 2) ppv = 20001; // error } else readingsOK = false; } delay(1000); // 2 if (!mb.slave()) { // Check if no transaction in progress // Array vullen met nullen. Als je dat niet doet staat er rommel in. clearArray(); int startReg = 37; mb.readIreg(SLAVE_ID, startReg, res, 25, cb); // Send Read Ireg from Modbus Server (SLAVE_ID, FIRST_REG, res, REG_COUNT, cb) while (mb.slave()) { // Check if transaction is active mb.task(); yield(); } if (noError) { tnf = res[37 - startReg] / 100.0; pac = ((res[40 - startReg] << 16) + res[41 - startReg]) / 10.0; // #40 kdy = res[54 - startReg] / 10.0; // #54 kt0 = ((res[55 - startReg] << 16) + res[56 - startReg]) / 10.0; // #55 tdtotal = (res[57 - startReg] << 16) + res[58 - startReg]; if (serialdebug) { Serial.print(", (Power AC) pac:"); Serial.print(pac); Serial.print(", (Net freq.) tnf:"); Serial.print(tnf); Serial.print(", (Power Today) kdy:"); Serial.print(kdy); Serial.print(", (Power Total) kt0:"); Serial.println(kt0); unsigned long y = 31557600; //365.25 * 24 * 3600; unsigned long d = 86400; //24 * 3600; unsigned long h = 3600; unsigned long m = 60; unsigned long yy = tdtotal / y; unsigned long dd = (tdtotal - (yy * y)) / d; unsigned long hh = (((tdtotal - (yy * y)) - (dd * d)) / h); unsigned long mm = ((((tdtotal - (yy * y)) - (dd * d)) - (hh * h)) / m); unsigned long ss = ((unsigned long)((((tdtotal - (yy * y)) - (dd * d)) - (hh * h)) - (mm * m)) % 60); Serial.print("(Total Time) tdtotal:"); Serial.print(tdtotal); Serial.print("; "); Serial.print(yy); Serial.print(", jaar, "); Serial.print(dd); Serial.print(", dagen, "); Serial.print(hh); Serial.print(", uren, "); Serial.print(mm); Serial.print(", minuten, "); Serial.print(ss); Serial.print(", seconden, "); } } else readingsOK = false; } delay(1000); // 3 if (!mb.slave()) { // Check if no transaction in progress // Array vullen met nullen. Als je dat niet doet staat er rommel in. clearArray(); int startReg = 93; mb.readIreg(SLAVE_ID, startReg, res, 1, cb); // Send Read Ireg from Modbus Server (SLAVE_ID, FIRST_REG, res, REG_COUNT, cb) while (mb.slave()) { // Check if transaction is active mb.task(); yield(); } if (noError) { //tkk = res[98 - startReg] / 100.0; tkk = res[93 - startReg] / 10.0; if (serialdebug) { Serial.print("(Temperature) tkk:"); Serial.println(tkk); } } else readingsOK = false; } allData = String(kdy); allData.concat(","); allData.concat(kt0); allData.concat(","); allData.concat(tnf); allData.concat(","); allData.concat(tkk); allData.concat(","); allData.concat(pac); allData.concat(","); allData.concat(pac1); allData.concat(","); allData.concat(il1); allData.concat(","); allData.concat(ul1); allData.concat(","); allData.concat(tdtotal); allData.concat(","); allData.concat(ppv); bericht = allData.c_str(); if (serialdebug) if (readingsOK) Serial.println(bericht); else Serial.println("Er ging iets niet goed... Geen verbinding?"); } void loop(){ if (millis() >= last_poll + 52000) { //interval van een minuut last_poll = millis(); loopModbus(); // ModusRTU/RS485 uitlezen publishStatus(inverterStatus); client.publish("/inverter/alldata", bericht, true); } if (!client.connected()) { reconnect(); } client.loop(); ArduinoOTA.handle(); }