Hi everyone,
here is my project “ve.direct” to MQTT with ESP8266 (nodemcu v1)
VictronMQTT.ino
/*
PID 0xA043 -- Product ID for BlueSolar MPPT 100/15
FW 119 -- Firmware version of controller, v1.19
SER# HQXXXXXXXXX -- Serial number
V 13790 -- Battery voltage, mV
I -10 -- Battery current, mA
VPV 15950 -- Panel voltage, mV
PPV 0 -- Panel power, W
CS 5 -- Charge state, 0 to 9
ERR 0 -- Error code, 0 to 119
LOAD ON -- Load output state, ON/OFF
IL 0 -- Load current, mA
H19 0 -- Yield total, kWh
H20 0 -- Yield today, kWh
H21 397 -- Maximum power today, W
H22 0 -- Yield yesterday, kWh
H23 0 -- Maximum power yesterday, W
HSDS 0 -- Day sequence number, 0 to 365
Checksum l:A0002000148 -- Message checksum
*/
#include "config.h"
#include <WiFiUdp.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <SoftwareSerial.h>
SoftwareSerial Vser(13, 15); // RX | TX on nodemcu
WiFiClient espClient;
PubSubClient client(espClient);
//String value;
String label, val;
char mptt_location[16];
float floatValue;
char buf[45];
char char_current[6];
char panel_power[6];
char laadstatus[12];
char prod_yesterday[6];
char max_power_h[6];
char prod_today[6];
byte len = 12;
int intValue;
void setup() {
// Serial.begin(19200);
Vser.begin(19200);
// Wait for hardware to initialize
delay(1000);
// Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.hostname(OTA_HOSTNAME);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
// Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname(OTA_HOSTNAME);
// No authentication by default
//ArduinoOTA.setPassword((const char *)"123");
ArduinoOTA.onStart([]() {
// Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
// Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
// Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
// Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
client.setServer(mqtt_server, 1883);
client.publish("Victron/Live", "0");
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
// Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("Victron", mqtt_user, mqtt_pass)) {
// Serial.println("connected");
// Once connected, publish an announcement...
} else {
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void loop() {
ArduinoOTA.handle();
if (!client.connected()) {
reconnect();
}
client.loop();
if (Vser.available())
{
label = Vser.readStringUntil('\t'); // this is the actual line that reads the label from the MPPT controller
val = Vser.readStringUntil('\r\r\n'); // this is the line that reads the value of the label
char charBufL[label.length() + 1];
label.toCharArray(charBufL, label.length() + 1);
if (label == "I")
{ // In this case I chose to read charging current
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 1000;
dtostrf(floatValue, len, 2, char_current);
client.publish("Victron/Battery current, I", char_current);
}
else if (label == "V")
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 1000;
dtostrf(floatValue, len, 2, char_current);
client.publish("Victron/Battery voltage, V", char_current);
}
else if (label == "PPV")
{
val.toCharArray(buf, sizeof(buf));
floatValue = atof(buf);
dtostrf(floatValue, len, 0, panel_power);
panel_power[len] = ' '; panel_power[len + 1] = 0;
client.publish("Victron/Panel power, W", panel_power);
}
else if (label == "VPV") //Solar
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 1000;
dtostrf(floatValue, len, 2, char_current);
client.publish("Victron/Panel voltage", char_current);
}
else if (label == "H20")
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 100;
dtostrf(floatValue, len, 2, prod_today);
prod_today[len] = ' '; prod_today[len + 1] = 0;
client.publish("Victron/Yield today, kWh", prod_today);
}
else if (label == "H22") //Yield yesterday, kWh
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 100;
dtostrf(floatValue, len, 2, prod_yesterday);
prod_yesterday[len] = ' '; prod_yesterday[len + 1] = 0;
client.publish("Victron/Yield yesterday, kWh", prod_yesterday);
}
else if (label == "H19") //-- Yield total, kWh
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 100;
dtostrf(floatValue, len, 2, prod_yesterday);
prod_yesterday[len] = ' '; prod_yesterday[len + 1] = 0;
client.publish("Victron/Yield total, kWh", prod_yesterday);
}
else if (label == "H21") //Maximum power today, W
{
val.toCharArray(buf, sizeof(buf));
floatValue = atof(buf);
dtostrf(floatValue, len, 0, max_power_h);
max_power_h[len] = ' '; max_power_h[len + 1] = 0;
client.publish("Victron/Maximum power today, W", max_power_h);
}
else if (label == "H23") //Maximum power yesterday, W
{
val.toCharArray(buf, sizeof(buf));
floatValue = atof(buf);
dtostrf(floatValue, len, 0, max_power_h);
max_power_h[len] = ' '; max_power_h[len + 1] = 0;
client.publish("Victron/Maximum power yesterday, W", max_power_h);
}
else if (label == "FW") //FW 119 -- Firmware version of controller, v1.19
{
val.toCharArray(buf, sizeof(buf));
float floatValue = atof(buf);
floatValue = floatValue / 100;
dtostrf(floatValue, len, 2, prod_today);
prod_today[len] = ' '; prod_today[len + 1] = 0;
client.publish("Victron/Firmware version", prod_today);
}
else if (label == "HSDS") //Day sequence number (0..364)
{
val.toCharArray(buf, sizeof(buf));
floatValue = atof(buf);
dtostrf(floatValue, len, 0, panel_power);
panel_power[len] = ' '; panel_power[len + 1] = 0;
client.publish("Victron/Day sequence number", panel_power);
}
else if (label == "MPPT")
{
val.toCharArray(buf, sizeof(buf));
intValue = atof(buf);
if (intValue == 0)
{
client.publish("Victron/Tracker operation", "Off");
}
else if (intValue == 1)
{
client.publish("Victron/Tracker operation", "Limited");
}
else if (intValue == 2)
{
client.publish("Victron/Tracker operation", "Active");
}
}
else if (label == "ERR") // This routine reads the error code.
{
val.toCharArray(buf, sizeof(buf));
intValue = atoi(buf);
if (intValue == 0)
{
client.publish("Victron/Error code", "No error");
}
else if (intValue == 2)
{
client.publish("Victron/Error code", "Battery voltage too high"); //'2': 'Battery voltage too high',
}
else if (intValue == 17) // '17': 'Charger temperature too high',
{
client.publish("Victron/Error code", "Charger temperature too high");
}
else if (intValue == 18) //'18': 'Charger over current',
{
client.publish("Victron/Error code", "Charger over current");
}
else if (intValue == 19) // '19': 'Charger current reversed',
{
client.publish("Victron/Error code", "Charger current reversed");
}
else if (intValue == 20) //'20': 'Bulk time limit exceeded',
{
client.publish("Victron/Error code", "Bulk time limit exceeded");
}
else if (intValue == 21) // '21': 'Current sensor issue',
{
client.publish("Victron/Error code", "Current sensor issue");
}
else if (intValue == 26) //'26': 'Terminals overheated',
{
client.publish("Victron/Error code", "Terminals overheated");
}
else if (intValue == 28) // '28': 'Converter issue', # (dual converter models only)
{
client.publish("Victron/Error code", "Converter issue");
}
else if (intValue == 33) // '33': 'Input voltage too high (solar panel)',
{
client.publish("Victron/Error code", "Input voltage too high (solar panel)");
}
else if (intValue == 34) // '34': 'Input current too high (solar panel)',
{
client.publish("Victron/Error code", "Input current too high (solar panel)");
}
else if (intValue == 38) // '38': 'Input shutdown (excessive battery voltage)',
{
client.publish("Victron/Error code", "Input shutdown (excessive battery voltage)");
}
else if (intValue == 39) // '39': 'Input shutdown (due to current flow during off mode)',
{
client.publish("Victron/Error code", "Input shutdown (due to current flow during off mode)");
}
else if (intValue == 65) // '65': 'Lost communication with one of devices',
{
client.publish("Victron/Error code", "Lost communication with one of devices");
}
else if (intValue == 66) // '66': 'Synchronised charging device configuration issue',
{
client.publish("Victron/Error code", "Synchronised charging device configuration issue");
}
else if (intValue == 67) // '67': 'BMS connection lost',
{
client.publish("Victron/Error code", "BMS connection lost");
}
else if (intValue == 68) // '68': 'Network misconfigured',
{
client.publish("Victron/Error code", "Network misconfigured");
}
else if (intValue == 116) // '116': 'Factory calibration data lost',
{
client.publish("Victron/Error code", "Factory calibration data lost");
}
else if (intValue == 117) // '117': 'Invalid/incompatible firmware',
{
client.publish("Victron/Error code", "Invalid/incompatible firmware");
}
else if (intValue == 119) // '119': 'User settings invalid'
{
client.publish("Victron/Error code", "User settings invalid");
}
}
else if (label == "CS") // Charge Status
{
val.toCharArray(buf, sizeof(buf));
intValue = atoi(buf);
if (intValue == 0)
{
client.publish("Victron/Charge state", "Off");
}
else if (intValue == 2)
{
client.publish("Victron/Charge state", "Fault");
}
else if (intValue == 3)
{
client.publish("Victron/Charge state", "Bulk");
}
else if (intValue == 4)
{
client.publish("Victron/Charge state", "Absorption");
}
else if (intValue == 5)
{
client.publish("Victron/Charge state", "Float");
}
else if (intValue == 7)
{
client.publish("Victron/Charge state", "Equalize (manual)");
}
else if (intValue == 245)
{
client.publish("Victron/Charge state", "Starting-up");
}
else if (intValue == 247)
{
client.publish("Victron/Charge state", "Auto equalize / Recondition");
}
else if (intValue == 252)
{
client.publish("Victron/Charge state", "External control");
}
}
}
}
config.h
> const char* ssid = "SSID";
> const char* password = "hidden_password_to_my_wifi"; //password to wifi
> const char* mqtt_server = "192.168.0.10"; //ip to mqtt server
> const char* mqtt_user = ""; //mqtt user name
> const char* mqtt_pass = ""; //mqtt password
>
> #define OTA_HOSTNAME "VictronMPPT"
> #define EPSOLAR_DEVICE_ID 1
> #define MQTT_ROOT "Victron"
and in HA config
#. Victron
- platform: mqtt
name: "Victron - Battery voltage"
state_topic: "Victron/Battery voltage, V"
unit_of_measurement: "V"
icon: mdi:mdi-battery
unique_id: sensor.text.victron.battery.voltage
- platform: mqtt
name: "Victron - Battery current"
state_topic: "Victron/Battery current, I"
unit_of_measurement: "I"
icon: mdi:mdi-current-dc
unique_id: sensor.text.victron.panel.current
- platform: mqtt
name: "Victron PV power"
state_topic: "Victron/Panel power, W"
unit_of_measurement: "W"
icon: mdi:gauge
unique_id: sensor.text.victron.panel.power
- platform: mqtt
name: "Victron Panel voltage"
state_topic: "Victron/Panel voltage"
unit_of_measurement: "V"
unique_id: sensor.text.victron.panel.voltage
- platform: mqtt
name: "Victron Tracker operation"
state_topic: "Victron/Tracker operation"
unique_id: sensor.text.victron.tracker.operation
icon: mdi:mdi-solar-power
- platform: mqtt
name: "Victron Yield today"
state_topic: "Victron/Yield today, kWh"
unique_id: sensor.text.victron.yield.today
icon: mdi:gauge
unit_of_measurement: "Kw/h"
- platform: mqtt
name: "Victron - Maximum power today"
state_topic: "Victron/Maximum power today, W"
unique_id: sensor.text.victron.maximum.power.today
unit_of_measurement: "W"
icon: mdi:gauge
- platform: mqtt
name: "Victron/Yield total, kWh"
state_topic: "Victron/Yield total, kWh"
unique_id: sensor.text.victron.yield.total
icon: mdi:gauge
unit_of_measurement: "Kw/h"
or check my GitHub
