From d76af539dcc9ab8c5d09a3fa35f1c37c49a2fa67 Mon Sep 17 00:00:00 2001 From: RickardPetterson Date: Mon, 24 Aug 2020 15:30:57 +0200 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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