Victron Energy VEDirect Bluetooth

I found that sometimes the serial settings goes haywire. I ended up putting this into crontab:

# Run the TTY script, sometimes it resets for unknown reason
* * * * * /etc/rc.local

Try it and see what happens.

Did you make it work?

Just completed my automation and the charging works perfectly.

I have to summarize my config in here soon!

Hi Corvy

Yes all seems good, ive not added any automations my self as yet. During the summer months i dont run out of solar energy. however when winter comes, i will automate it so if battery levels gets low i will have it turn off all devices with the exception of the the pi running HA and the Teltonika 4G Router (GPS tracker built in as well), which is also configured in HA, using a custom component someone here created using the traccar server/client.

Good work and thank you again

1 Like

Anyone done anything with Venus OS?

Not yet @automationpirate. But since last year I have migrated to using Node-Red for the serial input. It works a lot better. :slight_smile:

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

5 Likes

@KinDR Just what I need, thank you and welcome.

Does this still need the USB VEDirect adapter or do you go straight on to the port on the VictronConnect product for the serial data?

Would this be a possible using ESPhome as well?

Good work

1 Like

Hi
it’s an esp8266 node board connected directly to the ve.direct port
usb cable is not needed
with “Esp Home” I didn’t try it right away, I wrote it in arduino because I need data elsewhere than in HA

< sorry for the bad english i’m using google translator >

1 Like

Thank you, it all makes sense :grinning:

I will try this when I get some time. I use ESPhome for all my devices but I don’t use MQTT in HA, but no reason why I cannot set it up.

I have had no idea where to start with this so your input is a massive help.

Thanks again

@KinDR - great job !

@frank451 - did you manage to adjust this code for ESPHome ?

I believe I am also looking for such integration (have Victron BlueSolar MPPT 100 i 30) with HA. ESPHome is by far my preference as I already have several modules setup via ESPHome on NodeMCU.

Hi, it is possible that this code cannot be modified for esphome. I apologize for the bad news

Understand. Thanks.
At the same time I hope it is a mater of time and effort to make it… I can see this: Custom Sensor Component — ESPHome as an entry point.
Will see how to (if at all) proceed.
Thanks.

hi great work, i wrote to you on victron community too but also now found your post on here :slight_smile:

do you know if it will work with the bmv 712 battery monitor? im going to give this a try over the christmas period hopefully it works

Hi thanks, Yes, after a small modification of the code, it will also work for BMW … unfortunately I do not have a BMW at home so I have no way to debug and deliver the finished code

Very interesting. Is it possible to do this with the IP22 Smart Charger? It doesn’t not (AFAIK) have a separate wired output, only bluetooth.

1 Like

Sorry Victron Bluetooth API is still closed.

1 Like

So was Victron at all interested in developing an integration for their devices? I am planning the power system for my RV and and energy monitoring in HA is one of my fundamental requirements. I am surprised Victron hasn’t jumped on this.
Are there any other out of the box options for a PV/DC system that integrate nicely in HA?

victron but the solution has is i recommend to look at their whole product line

@KinDR I would like to implement your project “ve.direct” to MQTT with ESP8266 (nodemcu v1) to integrate my BlueSolar Victron controler MPPT 100/30 into HA.

Do I understand correctly that the following steps are needed:

  1. Get/prepare physical cable connection between controller and ESP8266 (that is easy for me)
  2. Upload your code and confih.h to ESP8266 (Which tool do you recommend ? Is Arduino IDE OK ?). Here little “How to” would be appreciated
  3. Setup sensors in HA (this looks easy)

Assuming all is OK it should work.

Any other hint ? Thank you !

1 Like

Hi all, victron mppt custom componnets for esphome its now available on my GitHub

2 Likes