commit add94126db171f396ed741c3544a9d9fbde2041e Author: Marcus Date: Thu Aug 10 02:28:13 2023 +0200 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..781d31b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +small sketch to receive modbus data from epever solar charger in json over serial. support for two rs485 connections. diff --git a/arduino-max485-epever-json-serial.ino b/arduino-max485-epever-json-serial.ino new file mode 100644 index 0000000..e5884bb --- /dev/null +++ b/arduino-max485-epever-json-serial.ino @@ -0,0 +1,258 @@ +/*tracerRegisters + This program is based off of RS485_HalfDuplex.ino found at + https://github.com/4-20ma/ModbusMaster/blob/master/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino + + RS485_HalfDuplex.pde - example using ModbusMaster library to communicate + with EPSolar LS2024B controller using a half-duplex RS485 transceiver. + + This example is tested against an EPSolar LS2024B solar charge controller. + See here for protocol specs: + http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf + + Library:: ModbusMaster + Author:: Marius Kintel + + Copyright:: 2009-2016 Doc Walker + + Modified:: 2023 DeltaLima + Notes: I took the register stuff from https://github.com/Bettapro/Solar-Tracer-Blynk-V3 + Had to hack around e.g. the battOverallCurrent as I only got garbage + when using the method from Bettapro. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + + +/* + We're using a MAX485-compatible RS485 Transceiver. + + In order to allow for Modbus communication and serial communication with the Arduino Uno, + we're using software serial.Rx/Tx is hooked up to the software serial port at RX=10 & TX=11. + The Data Enable and Receiver Enable pins are hooked up at DE = 3 & RE = 2. + + Wiring + MAX485=>Arduino Uno + GND => GND + VCC => 5V + DI => 11 + DE => 3 + RE => 2 + RO => 10 + + TRACER => MAX485 + blue => B + green => A +*/ +//#include // empty library for potential future use + +#include +#include +#include "ArduinoJson.h" + +SoftwareSerial myserial(10, 11); // RX, TX + + +#define MAX485_DE 3 +#define MAX485_RE_NEG 2 + +// instantiate ModbusMaster object +ModbusMaster node; + +// not needed... +//float batVolt; +//float batPercentage = 0.0; +float battChargeCurrent, battDischargeCurrent, battChargePower; +float bvoltage, ctemp, btemp, bremaining, lpower, lcurrent, pvvoltage, pvcurrent, pvpower; +float stats_today_pv_volt_min, stats_today_pv_volt_max; +uint16_t battOverallCurrent_raw; +int16_t battOverallCurrent_war; +float battOverallCurrent; + +bool rs485DataReceived = true; +bool loadPoweredOn = true; + +uint8_t result; +uint16_t data[6]; + +int outputValue1 = 0; +int countIt = 0; + +void preTransmission() +{ + digitalWrite(MAX485_RE_NEG, 1); + digitalWrite(MAX485_DE, 1); +} + +void postTransmission() +{ + digitalWrite(MAX485_RE_NEG, 0); + digitalWrite(MAX485_DE, 0); +} + +void setup() +{ + pinMode(MAX485_RE_NEG, OUTPUT); + pinMode(MAX485_DE, OUTPUT); + // Init in receive mode + digitalWrite(MAX485_RE_NEG, 0); + digitalWrite(MAX485_DE, 0); + + // Modbus communication runs at 115200 baud + + //Tracer connection + myserial.begin(115200); + + //USB Serial connection + Serial.begin(115200); + + // Modbus slave ID 1 + node.begin(1, myserial); + + // Callbacks allow us to configure the RS485 transceiver correctly + node.preTransmission(preTransmission); + node.postTransmission(postTransmission); + +} + +//was true +bool state = true; + +void loop() +{ + // uint8_t is short hand for a byte or an integer of length 8 bits + AddressRegistry_3100(); + AddressRegistry_3106(); + AddressRegistry_310D(); + AddressRegistry_311A(); + AddressRegistry_331B(); + + + StaticJsonDocument<128> jsonOut; + jsonOut["pvpower"] = pvpower; + jsonOut["pvcurrent"] = pvcurrent; + jsonOut["pvvoltage"] = pvvoltage; + jsonOut["lcurrent"] = lcurrent; + jsonOut["pvpower"] = pvpower; + jsonOut["lpower"] = lpower; + jsonOut["pvpower"] = pvpower; + jsonOut["btemp"] = btemp; + jsonOut["bvoltage"] = bvoltage; + jsonOut["bremaining"] = bremaining; + jsonOut["ctemp"] = ctemp; + jsonOut["battChargeCurrent"] = battChargeCurrent; + jsonOut["battChargePower"] = battChargePower; + jsonOut["battOverallCurrent"] = battOverallCurrent; + jsonOut["loadPoweredOn"] = loadPoweredOn; + serializeJson(jsonOut, Serial); + Serial.println(); + delay(1000); +} + + +// AddressRegistry from esp8266_max485_simple + + + + void AddressRegistry_3100() { + result = node.readInputRegisters(0x3100, 6); + + if (result == node.ku8MBSuccess) { + + pvvoltage = node.getResponseBuffer(0x00) / 100.0f; + //Serial.print("PV Voltage: "); + //Serial.println(pvvoltage); + + pvcurrent = node.getResponseBuffer(0x01) / 100.0f; + //Serial.print("PV Current: "); + //Serial.println(pvcurrent); + + pvpower = (node.getResponseBuffer(0x02) | node.getResponseBuffer(0x03) << 16) / 100.0f; + //Serial.print("PV Power: "); + //Serial.println(pvpower); + + bvoltage = node.getResponseBuffer(0x04) / 100.0f; + //Serial.print("Battery Voltage: "); + //Serial.println(bvoltage); + + battChargeCurrent = node.getResponseBuffer(0x05) / 100.0f; + //Serial.print("Battery Charge Current: "); + //Serial.println(battChargeCurrent); + } + } + + void AddressRegistry_3106() + { + result = node.readInputRegisters(0x3106, 2); + + if (result == node.ku8MBSuccess) { + battChargePower = (node.getResponseBuffer(0x00) | node.getResponseBuffer(0x01) << 16) / 100.0f; + //Serial.print("Battery Charge Power: "); + //Serial.println(battChargePower); + } + } + + void AddressRegistry_310D() + { + result = node.readInputRegisters(0x310D, 3); + + if (result == node.ku8MBSuccess) { + lcurrent = node.getResponseBuffer(0x00) / 100.0f; + //Serial.print("Load Current: "); + //Serial.println(lcurrent); + + lpower = (node.getResponseBuffer(0x01) | node.getResponseBuffer(0x02) << 16) / 100.0f; + //Serial.print("Load Power: "); + //Serial.println(lpower); + } else { + rs485DataReceived = false; + //Serial.println("Read register 0x310D failed!"); + } + } + + void AddressRegistry_311A() { + result = node.readInputRegisters(0x311A, 2); + + if (result == node.ku8MBSuccess) { + bremaining = node.getResponseBuffer(0x00) / 1.0f; + //Serial.print("Battery Remaining %: "); + //Serial.println(bremaining); + + btemp = node.getResponseBuffer(0x01) / 100.0f; + //Serial.print("Battery Temperature: "); + //Serial.println(btemp); + } else { + rs485DataReceived = false; + //Serial.println("Read register 0x311A failed!"); + } + } + + void AddressRegistry_331B() { + result = node.readInputRegisters(0x331B, 2); + + if (result == node.ku8MBSuccess) { + + //battOverallCurrent = (node.getResponseBuffer(0x00)| node.getResponseBuffer(0x01) << 16) / 100.0f; + // idk why this works not with arduino. with esp8266 it worked + //battOverallCurrent_war = *((int16_t *)&battOverallCurrent_raw); + //battOverallCurrent = 0.001 * battOverallCurrent_war; + battOverallCurrent = (battChargeCurrent - lcurrent); + + + Serial.print("Battery Discharge Current: "); + Serial.println(battOverallCurrent); + } else { + rs485DataReceived = false; + //Serial.println("Read register 0x331B failed!"); + } + }