From b6d04a5b04a177639c362199ad55bd1e66602896 Mon Sep 17 00:00:00 2001 From: svenp Date: Fri, 12 Jul 2019 14:29:33 +0200 Subject: [PATCH 1/8] MPPT 100/30 first test --- README.md | 1 + VEDirect.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ VEDirect.h | 11 ++++++++++- example.ino | 29 ++++++++++++++++++++++++++++- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29e9828..1ce89e9 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Setup: - See also: https://www.victronenergy.com/live/vedirect_protocol:faq - Developed and tested with a BMV-700 battery monitor - Distributed under an MIT license - see LICENCE.txt + - Developed and not tested with a MPPT 100/30 Provides: - Access to basic energy readings - Volts, Power, Current, State of Charge (SOC) diff --git a/VEDirect.cpp b/VEDirect.cpp index 662f2ef..81ded3d 100644 --- a/VEDirect.cpp +++ b/VEDirect.cpp @@ -141,6 +141,54 @@ int32_t VEDirect::read(uint8_t target) { return ret; } break; + case VE_POWER_PV: + if (strcmp(label, "PPV") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_VOLTAGE_PV: + if (strcmp(label, "VPV") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_YIELD_TOTAL: + if (strcmp(label, "H19") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_YIELD_TODAY: + if (strcmp(label, "H20") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_YIELD_YESTERDAY: + if (strcmp(label, "H22") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_POWER_MAX_TODAY: + if (strcmp(label, "H21") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_ERROR: + if (strcmp(label, "ERR") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; + case VE_STATE: + if (strcmp(label, "CS") == 0) { + sscanf(value, "%ld", &ret); + return ret; + } + break; default: break; } diff --git a/VEDirect.h b/VEDirect.h index 6a6e455..1999c57 100644 --- a/VEDirect.h +++ b/VEDirect.h @@ -21,7 +21,16 @@ enum VE_DIRECT_DATA { VE_SOC = 0, VE_VOLTAGE, VE_POWER, - VE_CURRENT + VE_CURRENT, + VE_POWER_PV, + VE_VOLTAGE_PV, + VE_YIELD_TOTAL, + VE_YIELD_TODAY, + VE_YIELD_YESTERDAY, + VE_POWER_MAX_TODAY, + VE_POWER_MAX_YESTERDAY, + VE_ERROR, + VE_STATE }; class VEDirect { diff --git a/example.ino b/example.ino index eb69441..deb32b0 100644 --- a/example.ino +++ b/example.ino @@ -14,7 +14,7 @@ #include "VEDirect.h" // 32 bit ints to collect the data from the device -int32_t VE_soc, VE_power, VE_voltage, VE_current; +int32_t VE_soc, VE_power, VE_voltage, VE_current, VE_power_pv, VE_voltage_pv, VE_yield_total, VE_yield_today, VE_yield_yesterday, VE_power_max_today, VE_power_max_yesterday, VE_error, VE_state; // VEDirect instantiated with relevant serial object VEDirect myve(Serial3); @@ -31,6 +31,15 @@ void loop() { VE_power = myve.read(VE_POWER); VE_voltage = myve.read(VE_VOLTAGE); VE_current = myve.read(VE_CURRENT); + VE_power_pv = myve.read(VE_POWER_PV); + VE_voltage_pv = myve.read(VE_VOLTAGE_PV); + VE_yield_total = myve.read(VE_YIELD_TOTAL); + VE_yield_today = myve.read(VE_YIELD_TODAY); + VE_yield_yesterday = myve.read(VE_YIELD_YESTERDAY); + VE_power_max_today = myve.read(VE_POWER_MAX_TODAY); + VE_power_max_yesterday = myve.read(VE_POWER_MAX_YESTERDAY); + VE_error = myve.read(VE_ERROR); + VE_state = myve.read(VE_STATE); } else { Serial.println("Could not open serial port to VE device"); while (1); @@ -46,6 +55,24 @@ void loop() { Serial.println(VE_voltage, DEC); Serial.print("Current "); Serial.println(VE_current, DEC); + Serial.print("Power PV "); + Serial.println(VE_power_pv, DEC); + Serial.print("Voltage PV "); + Serial.println(VE_voltage_pv, DEC); + Serial.print("Yield Total kWh "); + Serial.println(VE_yield_total, DEC); + Serial.print("Yield Today kWh "); + Serial.println(VE_yield_today, DEC); + Serial.print("Yield Yesterday kWh "); + Serial.println(VE_yield_yesterday, DEC); + Serial.print("Max Power Today "); + Serial.println(VE_power_max_today, DEC); + Serial.print("Max Power Yesterday "); + Serial.println(VE_power_max_yesterday, DEC); + Serial.print("Error Code "); + Serial.println(VE_error, DEC); + Serial.print("State of operation "); + Serial.println(VE_state, DEC); Serial.println(); // Copy the raw data stream (excluding the checkdum line and byte) to Serial0 From 3aa785a2479dabdd8606777188c78e1ff6d72dcb Mon Sep 17 00:00:00 2001 From: svenp Date: Fri, 12 Jul 2019 15:25:10 +0200 Subject: [PATCH 2/8] Error/State Codes --- example.ino | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/example.ino b/example.ino index deb32b0..90ded62 100644 --- a/example.ino +++ b/example.ino @@ -16,6 +16,25 @@ // 32 bit ints to collect the data from the device int32_t VE_soc, VE_power, VE_voltage, VE_current, VE_power_pv, VE_voltage_pv, VE_yield_total, VE_yield_today, VE_yield_yesterday, VE_power_max_today, VE_power_max_yesterday, VE_error, VE_state; +String CS0 = "Off"; +String CS2 = "Fault"; +String CS3 = "Bulk"; +String CS4 = "Absorption"; +String CS5 = "Float"; +String ERR0 = "No error"; +String ERR2 = "Battery voltage too high"; +String ERR17 = "Charger voltage too high"; +String ERR18 = "Charger over current"; +String ERR20 = "Bulk time limit exceeded"; +String ERR21 = "Current sensor issue"; +String ERR26 = "Terminals overheated"; +String ERR33 = "Input Voltage too high (solar panel)"; +String ERR34 = "Input current too high (solar panel)"; +String ERR38 = "Input shutdown (due to excessive battery voltage)"; +String ERR116 = "Factory calibration lost"; +String ERR117 = "invalied/incompatible firmware"; +String ERR119 = "User settings invalid"; + // VEDirect instantiated with relevant serial object VEDirect myve(Serial3); @@ -71,8 +90,20 @@ void loop() { Serial.println(VE_power_max_yesterday, DEC); Serial.print("Error Code "); Serial.println(VE_error, DEC); + Serial.print("Error Code "); + if (VE_error == 0){Serial.println(ERR0);} + if (VE_error == 2){Serial.println(ERR2);} + if (VE_error == 17){Serial.println(ERR17);} + if (VE_error == 18){Serial.println(ERR18);} + if (VE_error == 20){Serial.println(ERR20);} Serial.print("State of operation "); Serial.println(VE_state, DEC); + Serial.print("State of operation "); + if (VE_state == 0){Serial.println(CS0);} + if (VE_state == 2){Serial.println(CS2);} + if (VE_state == 3){Serial.println(CS3);} + if (VE_state == 4){Serial.println(CS4);} + if (VE_state == 5){Serial.println(CS5);} Serial.println(); // Copy the raw data stream (excluding the checkdum line and byte) to Serial0 From 5aeda90419bc5a9339cd97fbef7f27b618c062e6 Mon Sep 17 00:00:00 2001 From: svenp Date: Wed, 31 Jul 2019 20:01:07 +0200 Subject: [PATCH 3/8] mppt_only --- examples/ReadVEDirect/ReadVEDirect.ino | 124 +++++++++++++++++++++++++ src/VEDirect.cpp | 116 +++++++++++++++++++++++ src/VEDirect.h | 84 +++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 examples/ReadVEDirect/ReadVEDirect.ino create mode 100644 src/VEDirect.cpp create mode 100644 src/VEDirect.h diff --git a/examples/ReadVEDirect/ReadVEDirect.ino b/examples/ReadVEDirect/ReadVEDirect.ino new file mode 100644 index 0000000..9ccaa5b --- /dev/null +++ b/examples/ReadVEDirect/ReadVEDirect.ino @@ -0,0 +1,124 @@ +/****************************************************************** + VEDirect Arduino + + Copyright 2018, 2019, Brendan McLearie + Distributed under MIT license - see LICENSE.txt + + See README.md + + File: ReadVEDirect.ino / ReadVEDirect.cpp + - Provides example use of the VEDirect library +******************************************************************/ + +#include "Arduino.h" +#include "VEDirect.h" + +// 32 bit ints to collect the data from the device +int32_t VE_fw, VE_voltage, VE_current, VE_voltage_pv, VE_power_pv, VE_state, VE_mppt, + VE_error, VE_yield_total, VE_yield_today, VE_power_max_today, VE_yield_yesterday, + VE_power_max_yesterday, VE_day_sequence_number; +// Boolean to collect an ON/OFF value +uint8_t VE_load; + +String CS0 = "Off"; +String CS2 = "Fault"; +String CS3 = "Bulk"; +String CS4 = "Absorption"; +String CS5 = "Float"; +String ERR0 = "No error"; +String ERR2 = "Battery voltage too high"; +String ERR17 = "Charger voltage too high"; +String ERR18 = "Charger over current"; +String ERR20 = "Bulk time limit exceeded"; +String ERR21 = "Current sensor issue"; +String ERR26 = "Terminals overheated"; +String ERR33 = "Input Voltage too high (solar panel)"; +String ERR34 = "Input current too high (solar panel)"; +String ERR38 = "Input shutdown (due to excessive battery voltage)"; +String ERR116 = "Factory calibration lost"; +String ERR117 = "invalied/incompatible firmware"; +String ERR119 = "User settings invalid"; +// VEDirect instantiated with relevant serial object +VEDirect myve(Serial2); + +void setup() { + Serial.begin(9600); // Adjust as needed +} + +void loop() { + Serial.println("Reading values from Victron Energy device using VE.Direct text mode"); + Serial.println(); + + // Read the data + if(myve.begin()) { // test connection + VE_fw = myve.read(VE_FW); + VE_voltage = myve.read(VE_VOLTAGE); + VE_current = myve.read(VE_CURRENT); + VE_voltage_pv = myve.read(VE_VOLTAGE_PV); + VE_power_pv = myve.read(VE_POWER_PV); + VE_state = myve.read(VE_STATE); + VE_mppt = myve.read(VE_MPPT); + VE_error = myve.read(VE_ERROR); + VE_load = myve.read(VE_LOAD); + VE_yield_total = myve.read(VE_YIELD_TOTAL); + VE_yield_today = myve.read(VE_YIELD_TODAY); + VE_power_max_today = myve.read(VE_POWER_MAX_TODAY); + VE_yield_yesterday = myve.read(VE_YIELD_YESTERDAY); + VE_power_max_yesterday = myve.read(VE_POWER_MAX_YESTERDAY); + VE_day_sequence_number = myve.read(VE_DAY_SEQUENCE_NUMBER); + + } else { + Serial.println("Could not open serial port to VE device"); + //while (1); + } + + // Print each of the values + Serial.print("Voltage "); + Serial.println(VE_voltage, DEC); + Serial.print("Current "); + Serial.println(VE_current, DEC); + Serial.print("Power PV "); + Serial.println(VE_power_pv, DEC); + Serial.print("Voltage PV "); + Serial.println(VE_voltage_pv, DEC); + Serial.print("Yield Total kWh "); + Serial.println(VE_yield_total, DEC); + Serial.print("Yield Today kWh "); + Serial.println(VE_yield_today, DEC); + Serial.print("Yield Yesterday kWh "); + Serial.println(VE_yield_yesterday, DEC); + Serial.print("Max Power Today "); + Serial.println(VE_power_max_today, DEC); + Serial.print("Max Power Yesterday "); + Serial.println(VE_power_max_yesterday, DEC); + Serial.print("MPPT Code "); + Serial.println(VE_mppt, DEC); + Serial.print("MPPT Firmware "); + Serial.println(VE_fw, DEC); + Serial.print("Day Sequence Number "); + Serial.println(VE_day_sequence_number, DEC); + Serial.print("Error Code "); + Serial.println(VE_error, DEC); + Serial.print("Error Code "); + if (VE_error == 0){Serial.println(ERR0);} + if (VE_error == 2){Serial.println(ERR2);} + if (VE_error == 17){Serial.println(ERR17);} + if (VE_error == 18){Serial.println(ERR18);} + if (VE_error == 20){Serial.println(ERR20);} + Serial.print("State of operation "); + Serial.println(VE_state, DEC); + Serial.print("State of operation "); + if (VE_state == 0){Serial.println(CS0);} + if (VE_state == 2){Serial.println(CS2);} + if (VE_state == 3){Serial.println(CS3);} + if (VE_state == 4){Serial.println(CS4);} + if (VE_state == 5){Serial.println(CS5);} + Serial.println(); + + // Copy the raw data stream (minus the \r) to Serial0 + // Call read() with a token that won't match any VE.Direct labels + Serial.println("All data from device:"); + myve.read(VE_DUMP); + Serial.println(); + delay(10000); +} diff --git a/src/VEDirect.cpp b/src/VEDirect.cpp new file mode 100644 index 0000000..68c720f --- /dev/null +++ b/src/VEDirect.cpp @@ -0,0 +1,116 @@ +/****************************************************************** + VEDirect Arduino + + Copyright 2018, 2019, Brendan McLearie + Distributed under MIT license - see LICENSE.txt + + See README.md + + File: VEDirect.cpp + - Implementation + Updates: + - 2019-07-14 See VEDirect.h +******************************************************************/ + +#include "VEDirect.h" + +VEDirect::VEDirect(HardwareSerial& port): + VESerial(port) + // Initialise the serial port that the + // VE.Direct device is connected to and + // store it for later use. +{ +} + +VEDirect::~VEDirect() { + // virtual destructor +} + +uint8_t VEDirect::begin() { + // Check connection the serial port + VESerial.begin(19200); + if (VESerial) { + delay(500); + if(VESerial.available()) { + VESerial.flush(); + VESerial.end(); + return 1; + } + } + return 0; +} + +int32_t VEDirect::read(uint8_t target) { + // Read VE.Direct lines from the serial port + // Search for the label specified by enum target + // Extract and return the corresponding value + // If value is "ON" return 1. If "OFF" return 0; + + uint16_t loops = VED_MAX_READ_LOOPS; + uint8_t lines = VED_MAX_READ_LINES; + int32_t ret = 0; // The value to be returned + char line[VED_LINE_SIZE] = "\0"; // Line buffer + uint8_t idx = 0; // Line buffer index + char* label; + char* value_str; + int8_t b; // byte read from the stream + + VESerial.begin(VED_BAUD_RATE); + + while (lines > 0) { + if (VESerial.available()) { + while (loops > 0) { + b = VESerial.read(); + if ((b == -1) || (b == '\r')) { // Ignore '\r' and empty reads + loops--; + } else { + if (b == '\n') { // EOL + break; + } else { + if (idx < VED_LINE_SIZE) { + line[idx++] = b; // Add it to the buffer + } else { + return 0; // Buffer overrun + } + } + } + } + line[idx] = '\0'; // Terminate the string + + // Line in buffer + if (target == VE_DUMP) { + // Diagnostic routine - just print to Serial0; + Serial.println(line); + // Continue on rather than break to reset for next line + } + + label = strtok(line, "\t"); + if (strcmp_P(label, ved_labels[target]) == 0) { + value_str = strtok(0, "\t"); + if (value_str[0] == 'O') { //ON OFF type + if (value_str[1] == 'N') { + ret = 1; // ON + break; + } else { + ret = 0; // OFF + break; + } + } else { + sscanf(value_str, "%ld", &ret); + break; + } + } else { // Line not of interest + lines--; + loops = VED_MAX_READ_LOOPS; + line[0] = '\0'; + idx = 0; + } + } + } + return ret; +} + +void VEDirect::copy_raw_to_serial0() { + read(VE_DUMP); +} + diff --git a/src/VEDirect.h b/src/VEDirect.h new file mode 100644 index 0000000..b1d2a43 --- /dev/null +++ b/src/VEDirect.h @@ -0,0 +1,84 @@ +/****************************************************************** + VEDirect Arduino + + Copyright 2018, 2019, Brendan McLearie + Distributed under MIT license - see LICENSE.txt + + See README.md + + File: VEDirect.h + - Class / enums / API + + Updates: + - 2019-07-14: + - Rewrite of read - cleaner. + - Target labels extendible with enum and PROGMEM strings + - Retired copy_raw_to_serial0() code - use VE_DUMP on read + - Added some tunable parameters see #defines +******************************************************************/ + +#ifndef VEDIRECT_H_ +#define VEDIRECT_H_ + +#include + +// Tunable parameters - defaults tested on mega2560 R3 +#define VED_LINE_SIZE 40 // Seems to be plenty. VE.Direct protocol could change +#define VED_MAX_LEBEL_SIZE 17 // Max length of all labels of interest + '\0'. See ved_labels[] +#define VED_MAX_READ_LOOPS 60000 // How many read loops to be considered a read time-out +#define VED_MAX_READ_LINES 50 // How many lines to read looking for a value + // before giving up. Also determines lines for diag dump +#define VED_BAUD_RATE 19200 + +// Extend this and ved_labels[] for needed inclusions +enum VE_DIRECT_DATA { + VE_DUMP = 0, + VE_FW, + VE_VOLTAGE, + VE_CURRENT, + VE_VOLTAGE_PV, + VE_POWER_PV, + VE_STATE, + VE_MPPT, + VE_ERROR, + VE_LOAD, + VE_YIELD_TOTAL, + VE_YIELD_TODAY, + VE_POWER_MAX_TODAY, + VE_YIELD_YESTERDAY, + VE_POWER_MAX_YESTERDAY, + VE_DAY_SEQUENCE_NUMBER, + VE_LAST_LABEL, +}; + +const char ved_labels[VE_LAST_LABEL][VED_MAX_LEBEL_SIZE] PROGMEM = { + "Dump", // a string that won't match any label + "FW", + "V", + "I", + "VPV", + "PPV", + "CS", + "MPPT", + "ERR", + "LOAD", + "H19", + "H20", + "H21", + "H22", + "H23", + "HSDS", +}; + +class VEDirect { +public: + VEDirect(HardwareSerial& port); + virtual ~VEDirect(); + uint8_t begin(); + int32_t read(uint8_t target); + void copy_raw_to_serial0(); // kept for backwards compatibility +private: + HardwareSerial& VESerial; +}; + +#endif /* VEDIRECT_H_ */ From d76af539dcc9ab8c5d09a3fa35f1c37c49a2fa67 Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Mon, 24 Aug 2020 15:30:57 +0200 Subject: [PATCH 4/8] clean up files should not exist in the root of repository --- VEDirect.cpp | 295 --------------------------------------------------- VEDirect.h | 50 --------- example.ino | 114 -------------------- 3 files changed, 459 deletions(-) delete mode 100644 VEDirect.cpp delete mode 100644 VEDirect.h delete mode 100644 example.ino diff --git a/VEDirect.cpp b/VEDirect.cpp deleted file mode 100644 index 81ded3d..0000000 --- a/VEDirect.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/****************************************************************** - VEDirect Arduino - - Copyright 2018, 2019, Brendan McLearie - Distributed under MIT license - see LICENSE.txt - - See README.md - - File: VEDirect.h - - Class / enums / API -******************************************************************/ - -#include "VEDirect.h" - -VEDirect::VEDirect(HardwareSerial& port): - VESerial(port) - // Initialise the serial port that the - // VE.Direct device is connected to and - // store it for later use. -{ -} - -VEDirect::~VEDirect() { - // virtual destructor -} - -uint8_t VEDirect::begin() { - // Check connection the serial port - VESerial.begin(19200); - if (VESerial) { - delay(500); - if(VESerial.available()) { - VESerial.flush(); - VESerial.end(); - return 1; - } - } - return 0; -} - -int32_t VEDirect::read(uint8_t target) { - // Read VE.Direct text blocks from the serial port - // Search for the label specified by enum target_label - // Extract and return the corresponding value. - - int32_t ret = 0; // The value to be returned - char VE_line[VED_LINE_SIZE]; // Line buffer - char* label; - char* value; - uint8_t buf_idx = 0; - - const char delim[2] = "\t"; // Delim between label and value - // Simple state machine as to navigate the - // flow of text data that that is sent every second - uint8_t block_count = 0; - uint8_t cr = 0; - uint8_t checksum = 0; - - uint8_t b; // byte read from the stream - - VESerial.begin(19200); - - if (VESerial) { - // BMV continuously transmits 2x text data blocks to deliver all data. - // Read 3x times, discarding the first (likely) partial block, - // to get the two complete data blocks. - while (block_count < 3) { - if (VESerial.available()) { - // Get the next byte from the serial stream - b = VESerial.read(); - switch (b) { - case '\n': // start of newline - reset read buffer - cr = 0; - VE_line[0] = '\0'; - buf_idx = 0; - break; - case '\r': // eol - terminate the buffer - cr = 1; - VE_line[buf_idx] = '\0'; - buf_idx++; - break; - default: - // Is the checksum expected? - if (checksum) { - // TODO: Capture it and use it later - // Currently: just ignore it and the preceding \t - // Assume eol, reset and increment the block_count - if (b != '\t') { - // then it a checksum byte - cr = 0; - VE_line[0] = '\0'; - buf_idx = 0; - block_count++; - checksum = 0; - } // no else - was the \t before, ignore - } else { - // Normal char of interest - // Clear cr flag which may have been set - cr = 0; - // Add the char to the buffer - VE_line[buf_idx] = b; - buf_idx++; - // Check for the Checksum label - // Turn on flag to trigger checksum - // \t and byte capture on next loops - if (strncmp(VE_line, "Checksum", 8) == 0) { - VE_line[8] = '\0'; - checksum = 1; - } - } - } - } - // Evaluate the flags and buffer contents - if (cr && buf_idx) { - // whole line in buffer - label = strtok(VE_line, delim); - value = strtok(0, delim); - // Look for the label passed requested on he call - switch (target) { - case VE_SOC: - if (strcmp(label, "SOC") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_VOLTAGE: - if (strcmp(label, "V") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_POWER: - if (strcmp(label, "P") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_CURRENT: - if (strcmp(label, "I") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_POWER_PV: - if (strcmp(label, "PPV") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_VOLTAGE_PV: - if (strcmp(label, "VPV") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_YIELD_TOTAL: - if (strcmp(label, "H19") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_YIELD_TODAY: - if (strcmp(label, "H20") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_YIELD_YESTERDAY: - if (strcmp(label, "H22") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_POWER_MAX_TODAY: - if (strcmp(label, "H21") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_ERROR: - if (strcmp(label, "ERR") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - case VE_STATE: - if (strcmp(label, "CS") == 0) { - sscanf(value, "%ld", &ret); - return ret; - } - break; - default: - break; - } - } - } - // Tidy up - VESerial.flush(); - VESerial.end(); - } - return ret; -} - -void VEDirect::copy_raw_to_serial0() { - // Read VE.Direct text blocks from the serial port - // Buffer them and then print them to Serial - // ******* - // NOTE: Do not use this function for anything serious - // To allow lower Serial0 speed (eg 9600) it buffers - // the input from the VE device (at 19200) and then prints it - // It is memory / malloc ugly and without fail safes - // Only useful for low level port dumping - - typedef struct BUFFER { - char* line; - struct BUFFER* next; - } bufline; - - bufline* head = (bufline *)malloc(sizeof(bufline)); - head->next = NULL; - bufline* walker = head; - - char VE_line[VED_LINE_SIZE]; // Line buffer - uint8_t buf_idx = 0; - - // Simple state machine as to navigate the - // flow of text data that that is sent every second - uint8_t block_count = 0; - uint8_t newline = 0; - - uint8_t b; // byte read from the stream - - VESerial.begin(19200); - - if (VESerial) { - // BMV continuously transmits 2x text data blocks to deliver all data. - // Read 3x times, discarding the first (likely) partial block, - // to get the two complete data blocks. - while (block_count < 15) { - if (VESerial.available()) { - // Get the next byte from the serial stream - b = VESerial.read(); - switch (b) { - case '\r': - break; - case '\n': // terminate the buffer - newline = 1; - VE_line[buf_idx] = '\0'; - break; - default: - // Normal char of interest - newline = 0; - // Add the char to the buffer - VE_line[buf_idx] = b; - buf_idx++; - - if (strncmp(VE_line, "Checksum", 8) == 0) { - VE_line[8] = '\0'; - block_count++; - } - - } - } - // Evaluate the flags and buffer contents - if (newline && buf_idx) { - // whole line in buffer - walker->line = (char *)malloc(strlen(VE_line) +10); - strcpy(walker->line, VE_line); - walker->next =(bufline *)malloc(sizeof(bufline)); - walker = walker->next; - walker->next = NULL; - - newline = 0; - VE_line[0] = '\0'; - buf_idx = 0; - - } - } - // print it all - Serial.println("Out of collect loop"); - walker = head; - while (walker->next != NULL) { - Serial.println(walker->line); - free(walker->line); - walker = walker->next; - free(head); - head = walker; - } - free(head); - - // Tidy up - VESerial.flush(); - VESerial.end(); - } -} diff --git a/VEDirect.h b/VEDirect.h deleted file mode 100644 index 1999c57..0000000 --- a/VEDirect.h +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************** - VEDirect Arduino - - Copyright 2018, 2019, Brendan McLearie - Distributed under MIT license - see LICENSE.txt - - See README.md - - File: VEDirect.h - - Class / enums / API -******************************************************************/ - -#ifndef VEDIRECT_H_ -#define VEDIRECT_H_ - -#include - -#define VED_LINE_SIZE 20 // Seems to be plenty. VE.Direc protocol could change. - -enum VE_DIRECT_DATA { - VE_SOC = 0, - VE_VOLTAGE, - VE_POWER, - VE_CURRENT, - VE_POWER_PV, - VE_VOLTAGE_PV, - VE_YIELD_TOTAL, - VE_YIELD_TODAY, - VE_YIELD_YESTERDAY, - VE_POWER_MAX_TODAY, - VE_POWER_MAX_YESTERDAY, - VE_ERROR, - VE_STATE -}; - -class VEDirect { -private: - HardwareSerial& VESerial; -public: - VEDirect(HardwareSerial& port); - virtual ~VEDirect(); - uint8_t begin(); - int32_t read(uint8_t target); - void copy_raw_to_serial0(); -}; - - - - -#endif /* VEDIRECT_H_ */ diff --git a/example.ino b/example.ino deleted file mode 100644 index 90ded62..0000000 --- a/example.ino +++ /dev/null @@ -1,114 +0,0 @@ -/****************************************************************** - VEDirect Arduino - - Copyright 2018, 2019, Brendan McLearie - Distributed under MIT license - see LICENSE.txt - - See README.md - - File: example.ino / example.cpp - - Provides example use of the VEDirect library -******************************************************************/ - -#include "Arduino.h" -#include "VEDirect.h" - -// 32 bit ints to collect the data from the device -int32_t VE_soc, VE_power, VE_voltage, VE_current, VE_power_pv, VE_voltage_pv, VE_yield_total, VE_yield_today, VE_yield_yesterday, VE_power_max_today, VE_power_max_yesterday, VE_error, VE_state; - -String CS0 = "Off"; -String CS2 = "Fault"; -String CS3 = "Bulk"; -String CS4 = "Absorption"; -String CS5 = "Float"; -String ERR0 = "No error"; -String ERR2 = "Battery voltage too high"; -String ERR17 = "Charger voltage too high"; -String ERR18 = "Charger over current"; -String ERR20 = "Bulk time limit exceeded"; -String ERR21 = "Current sensor issue"; -String ERR26 = "Terminals overheated"; -String ERR33 = "Input Voltage too high (solar panel)"; -String ERR34 = "Input current too high (solar panel)"; -String ERR38 = "Input shutdown (due to excessive battery voltage)"; -String ERR116 = "Factory calibration lost"; -String ERR117 = "invalied/incompatible firmware"; -String ERR119 = "User settings invalid"; - -// VEDirect instantiated with relevant serial object -VEDirect myve(Serial3); - -void setup() { - Serial.begin(9600); // Adjust as needed - Serial.println("Reading values from Victron Energy device using VE.Direct text mode"); -} - -void loop() { - // Read the data - if(myve.begin()) { // test connection - VE_soc = myve.read(VE_SOC); - VE_power = myve.read(VE_POWER); - VE_voltage = myve.read(VE_VOLTAGE); - VE_current = myve.read(VE_CURRENT); - VE_power_pv = myve.read(VE_POWER_PV); - VE_voltage_pv = myve.read(VE_VOLTAGE_PV); - VE_yield_total = myve.read(VE_YIELD_TOTAL); - VE_yield_today = myve.read(VE_YIELD_TODAY); - VE_yield_yesterday = myve.read(VE_YIELD_YESTERDAY); - VE_power_max_today = myve.read(VE_POWER_MAX_TODAY); - VE_power_max_yesterday = myve.read(VE_POWER_MAX_YESTERDAY); - VE_error = myve.read(VE_ERROR); - VE_state = myve.read(VE_STATE); - } else { - Serial.println("Could not open serial port to VE device"); - while (1); - } - - // Print it each of the values - Serial.println("Values Received"); - Serial.print("State of Charge (SOC): "); - Serial.println(VE_soc, DEC); - Serial.print("Power: "); - Serial.println(VE_power, DEC); - Serial.print("Voltage "); - Serial.println(VE_voltage, DEC); - Serial.print("Current "); - Serial.println(VE_current, DEC); - Serial.print("Power PV "); - Serial.println(VE_power_pv, DEC); - Serial.print("Voltage PV "); - Serial.println(VE_voltage_pv, DEC); - Serial.print("Yield Total kWh "); - Serial.println(VE_yield_total, DEC); - Serial.print("Yield Today kWh "); - Serial.println(VE_yield_today, DEC); - Serial.print("Yield Yesterday kWh "); - Serial.println(VE_yield_yesterday, DEC); - Serial.print("Max Power Today "); - Serial.println(VE_power_max_today, DEC); - Serial.print("Max Power Yesterday "); - Serial.println(VE_power_max_yesterday, DEC); - Serial.print("Error Code "); - Serial.println(VE_error, DEC); - Serial.print("Error Code "); - if (VE_error == 0){Serial.println(ERR0);} - if (VE_error == 2){Serial.println(ERR2);} - if (VE_error == 17){Serial.println(ERR17);} - if (VE_error == 18){Serial.println(ERR18);} - if (VE_error == 20){Serial.println(ERR20);} - Serial.print("State of operation "); - Serial.println(VE_state, DEC); - Serial.print("State of operation "); - if (VE_state == 0){Serial.println(CS0);} - if (VE_state == 2){Serial.println(CS2);} - if (VE_state == 3){Serial.println(CS3);} - if (VE_state == 4){Serial.println(CS4);} - if (VE_state == 5){Serial.println(CS5);} - Serial.println(); - - // Copy the raw data stream (excluding the checkdum line and byte) to Serial0 - Serial.println("All data from device (excluding checksum line)"); - myve.copy_raw_to_serial0(); - delay(10000); - while(1); -} From a60a9e850ddb0f70ad261e7845ba36e0629d2e76 Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Mon, 24 Aug 2020 15:31:56 +0200 Subject: [PATCH 5/8] Added labels for VE.Direct to get data from a BMW device --- src/VEDirect.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/VEDirect.h b/src/VEDirect.h index b1d2a43..9ebc07e 100644 --- a/src/VEDirect.h +++ b/src/VEDirect.h @@ -1,7 +1,7 @@ /****************************************************************** VEDirect Arduino - Copyright 2018, 2019, Brendan McLearie + Copyright 2018, 2019, 2020 Brendan McLearie Distributed under MIT license - see LICENSE.txt See README.md @@ -15,6 +15,8 @@ - Target labels extendible with enum and PROGMEM strings - Retired copy_raw_to_serial0() code - use VE_DUMP on read - Added some tunable parameters see #defines + - 2020-08-24 - Contribution of Rickard Nordström Pettersson + - Added VE_SOC, VE_POWER, VE_ALARM for BMW devices ******************************************************************/ #ifndef VEDIRECT_H_ @@ -24,7 +26,7 @@ // Tunable parameters - defaults tested on mega2560 R3 #define VED_LINE_SIZE 40 // Seems to be plenty. VE.Direct protocol could change -#define VED_MAX_LEBEL_SIZE 17 // Max length of all labels of interest + '\0'. See ved_labels[] +#define VED_MAX_LEBEL_SIZE 20 // Max length of all labels of interest + '\0'. See ved_labels[] #define VED_MAX_READ_LOOPS 60000 // How many read loops to be considered a read time-out #define VED_MAX_READ_LINES 50 // How many lines to read looking for a value // before giving up. Also determines lines for diag dump @@ -48,6 +50,9 @@ enum VE_DIRECT_DATA { VE_YIELD_YESTERDAY, VE_POWER_MAX_YESTERDAY, VE_DAY_SEQUENCE_NUMBER, + VE_SOC, + VE_POWER, + VE_ALARM, VE_LAST_LABEL, }; @@ -68,6 +73,9 @@ const char ved_labels[VE_LAST_LABEL][VED_MAX_LEBEL_SIZE] PROGMEM = { "H22", "H23", "HSDS", + "SOC", + "P", + "Alarm" }; class VEDirect { From ac38471e45c100b45461dc802e2b6b42e26a3cca Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Mon, 24 Aug 2020 15:33:04 +0200 Subject: [PATCH 6/8] updated ReadMe --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 1ce89e9..9b6a357 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,31 @@ -# VictronVEDirectArduino +# Victron VE.Direct Arduino library Light-weight Arduino library to read basic data using the VE.Direct protocol from Victron Energy Built as part of a larger project, now making it available separately in case others find it useful. Setup: - - An Arduino(ish) board - - A Victron Energy device that sends serial data using the text version of the VE.Direct protocol - - A 5v to 3.3v serial converter (BMV is 3.3v - don't plug it directly into an Arduino!) + - An Arduino(ish) board, works on Particle devices like Particle Photon and Particle Argon + - A Victron Energy device that sends serial data using the text version of the VE.Direct protocol (little different values for different devices) + - A 5v to 3.3v serial converter (BMV is 3.3v - don't plug it directly into an Arduino!) - Plugged into the Arduino on a serial port (eg Serial1, Serial2 etc) - See also: https://www.victronenergy.com/live/vedirect_protocol:faq - - Developed and tested with a BMV-700 battery monitor - Distributed under an MIT license - see LICENCE.txt - - Developed and not tested with a MPPT 100/30 - + - Developed and tested with: MPPT 75/15, MPPT 100/30, BMW-712 + Provides: - - Access to basic energy readings - Volts, Power, Current, State of Charge (SOC) + - Access to the full protocol of VE.Direct variables like Power, Voltages, Panel Voltage and Panel Power depending on device. - A diagnostic "full dump" of everything coming from the device -### Usage: +Code examples exists in the examples directory and the library you find in the src directory. + +### Example of basic usage: #include "VEDirect.h" - VEDirect my_bmv(Serial3); + VEDirect my_bmv(Serial1); if my_bmv.begin() { - my_int32 = my_bmv.read(VE_SOC); + int32_t my_int32 = my_bmv.read(VE_SOC); } - // VE_SOC, VE_VOLTAGE, VE_CURRENT, VE_POWER + // Data you can get: VE_FW, VE_VOLTAGE, VE_CURRENT, VE_VOLTAGE_PV, VE_POWER_PV, VE_STATE, VE_MPPT, VE_ERROR, VE_LOAD, VE_YIELD_TOTAL, VE_YIELD_TODAY, VE_POWER_MAX_TODAY, VE_YIELD_YESTERDAY, VE_POWER_MAX_YESTERDAY, VE_DAY_SEQUENCE_NUMBER, VE_LAST_LABEL, VE_SOC, VE_POWER, VE_ALARM From 00562011dddf4ee332d267a46ea4afb413cbb063 Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Mon, 24 Aug 2020 15:33:14 +0200 Subject: [PATCH 7/8] no message --- examples/Particle/victron-bmw.ino | 0 examples/Particle/victron-mppt.ino | 50 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 examples/Particle/victron-bmw.ino create mode 100644 examples/Particle/victron-mppt.ino diff --git a/examples/Particle/victron-bmw.ino b/examples/Particle/victron-bmw.ino new file mode 100644 index 0000000..e69de29 diff --git a/examples/Particle/victron-mppt.ino b/examples/Particle/victron-mppt.ino new file mode 100644 index 0000000..5432a34 --- /dev/null +++ b/examples/Particle/victron-mppt.ino @@ -0,0 +1,50 @@ +/****************************************************************** + VE.Direct Arduino Example for Particle devices + + Copyright 2020, Rickard Nordström Pettersson + Distributed under MIT license - see LICENSE.txt + + See README.md + + File: victron-mppt.ino + - Provides example use of the VEDirect library on a Particle Photon +******************************************************************/ + +#include "VEDirect.h" + +#define PUBLISH_EVENT_NAME "Victron-VEDirect" + +char tmp[200]; + +// 32 bit ints to collect the data from the device +int32_t VE_fw, VE_voltage, VE_current, VE_voltage_pv, VE_power_pv, VE_state, VE_mppt, + VE_error, VE_yield_total, VE_yield_today, VE_power_max_today, VE_yield_yesterday, + VE_power_max_yesterday, VE_day_sequence_number; + +uint8_t VE_load; + +VEDirect myve(Serial1); + +void setup() { + Serial.begin(9600); // Adjust as needed +} + +void loop() { + // Read the data + if(myve.begin()) { + VE_voltage = myve.read(VE_VOLTAGE); + VE_current = myve.read(VE_CURRENT); + VE_voltage_pv = myve.read(VE_VOLTAGE_PV); + VE_power_pv = myve.read(VE_POWER_PV); + VE_state = myve.read(VE_STATE); + VE_error = myve.read(VE_ERROR); + + sprintf(tmp ,"{ \"Voltage\": \"%d\", \"Current\": \"%d\", \"PowerPV\": \"%d\", \"VoltagePV\": \"%d\", \"ErrorCode\": \"%d\", \"StateOfOperation\": \"%d\" }\r\n", VE_voltage, VE_current, VE_power_pv, VE_voltage_pv, VE_error, VE_state); + + Particle.publish(PUBLISH_EVENT_NAME, tmp, 60, PRIVATE); + } else { + // Serial.println("Could not open serial port to VE device"); + } + + delay(60000); +} From 0b7f93b36b2dced885928c096cf03cdbe6ee1112 Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Tue, 25 Aug 2020 21:58:36 +0200 Subject: [PATCH 8/8] added good to know information --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b6a357..40e027d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Built as part of a larger project, now making it available separately in case ot Setup: - An Arduino(ish) board, works on Particle devices like Particle Photon and Particle Argon - A Victron Energy device that sends serial data using the text version of the VE.Direct protocol (little different values for different devices) - - A 5v to 3.3v serial converter (BMV is 3.3v - don't plug it directly into an Arduino!) + - A 5v to 3.3v serial converter (BMV is 3.3v - don't plug it directly into an Arduino!) + - Good to know is hat BMW-712 takes 3.3v in the VE.Direct port but the SmartSolar MPPT 75/15 need 5v to communicate on the VE.Direct port - Plugged into the Arduino on a serial port (eg Serial1, Serial2 etc) - See also: https://www.victronenergy.com/live/vedirect_protocol:faq - Distributed under an MIT license - see LICENCE.txt