I am attempting to connect a Victron MPPT 100|50 to a Teensy 4.1. I continue to get the error in the program that it us unable to open the serial port. I am using a Victron VE direct cable with one end cut off and connected to the teensy.
I am hoping someone here can give me some troubleshooting advice.
/******************************************************************
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
Other Code By Whispering Hill Farms. Copy the non VE.Direct away!
******************************************************************/
#include "Arduino.h"
#include "VEDirect.h"
#include <ArduinoJson.h>
//Pressure Switch Variables
unsigned long PressureSwTimer;
bool PressureSwState;
int PressureSwCounter;
unsigned long PressureSwOffTime;
unsigned long PressureSwOnTime;
//Float Switch Variables
unsigned long FloatSwTimer;
bool FloatSwState;
int FloatSwCounter;
unsigned long FloatSwOffTime;
unsigned long FloatSwOnTime;
//Motor Variables
int MotorRunningCounter;
//Pinouts
//Serial2 Pins 7RX and 8RX
byte PressureSw = 4; // Pressure switch pin, other side of switch to GND
byte FloatSw = 5; // Pressure switch pin, other side of switch to GND
// 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 initiate with relevant serial object
VEDirect myve(Serial2);
void setup()
{
pinMode(PressureSw, INPUT_PULLUP);
pinMode(FloatSw, INPUT_PULLUP);
Serial.begin(19200); // Adjust as needed
}
void loop()
{
// Start-Measure the time the pressure switch
// TESTING LINE PressureSwState = true; END OF TESTING
// Modify This https://forum.arduino.cc/t/state-change-detection-for-active-low-inputs/680899
Serial.print("Reading values from Pressure Switch");
Serial.println();
if(digitalRead(PressureSw) != PressureSwState) // If there is a State Change
{
PressureSwState ^= 1; // make them the same
PressureSwCounter ++; //add 1 to the counter
Serial.print("Pressure switch counter: ");
Serial.println(PressureSwCounter);
if(PressureSwState == true)
{
PressureSwOffTime = (millis() - PressureSwTimer ) / 1000;
Serial.print("Pressure switch OFF time: ");
Serial.print(PressureSwOffTime);
Serial.println(" Seconds");
}
else
{
PressureSwOnTime = (millis() - PressureSwTimer ) / 1000;
Serial.print("Pressure switch ON time: ");
Serial.print(PressureSwOnTime);
Serial.println(" Seconds");
}
PressureSwTimer = millis(); // reset timer
}
else
{
Serial.print("Pressure switch State Unchanged");
Serial.println();
}
//END-Measure the time the pressure switch
// START-Measure if the Float Valve is Open or Closed
Serial.println();
Serial.print("Reading values from Float switch");
Serial.println();
if(digitalRead(FloatSw) != FloatSwState) // If there is a State Change
{
FloatSwState ^= 1; // make them the same
FloatSwCounter ++; //add 1 to the counter
Serial.print("Float switch counter: ");
Serial.println(FloatSwCounter);
if(FloatSwState == true)
{
FloatSwOffTime = (millis() - FloatSwTimer ) / 1000;
Serial.print("Float switch OFF time: ");
Serial.print(FloatSwOffTime);
Serial.println(" Seconds");
}
else
{
FloatSwOnTime = (millis() - FloatSwTimer ) / 1000;
Serial.print("Float switch ON time: ");
Serial.print(FloatSwOnTime);
Serial.println(" Seconds");
}
FloatSwTimer = millis(); // reset timer
}
else
{
Serial.print("Float Switch State Unchanged");
Serial.println();
}
//END-Measure if the Float valve is Open Or Closed
//Start Motor Running
//PressureSwState is a Bool. Bools are True or False. Im not convinced that directly transalates to HIGH or LOW. Investigate further.
if(PressureSwState == LOW && FloatSwState == LOW)
{
MotorRunningCounter ++;
Serial.print("Motor is Running");
Serial.println();
//Need some more code here it is going to just 1 up each loop when running. It needs a state change possibly or figure out what we should be monitoring.
}
//End Motor Running
Serial.println();
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 Msg ");
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);
//}
//void json()
//{
//Set the Error message to human readable
String ErrorMsg ;
if (VE_error == 0){ErrorMsg = (ERR0);}
if (VE_error == 2){ErrorMsg = (ERR2);}
if (VE_error == 17){ErrorMsg = (ERR17);}
if (VE_error == 18){ErrorMsg = (ERR18);}
if (VE_error == 20){ErrorMsg = (ERR20);}
//Set the operation Mmessage to human readable
String OpMsg;
if (VE_state == 0){OpMsg = (CS0);}
if (VE_state == 2){OpMsg = (CS2);}
if (VE_state == 3){OpMsg = (CS3);}
if (VE_state == 4){OpMsg = (CS4);}
if (VE_state == 5){OpMsg = (CS5);}
// Allocate the JSON document
JsonDocument doc;
// Add values in the document
//doc["sensor"] = "gps";
// doc["time"] = 1351824120;
doc["ID"] = "WaterWagon";
doc["Voltage"] = (VE_voltage, DEC);
doc["Current"] = (VE_current, DEC);
doc["Power PV"] = (VE_power_pv, DEC);
doc["Voltage PV"] = (VE_voltage_pv, DEC);
doc["Yield Total kWh"] = (VE_yield_total, DEC);
doc["Yield Today kWh"] = (VE_yield_today, DEC);
doc["Yield Yesterday kWh"] = (VE_yield_yesterday, DEC);
doc["Max Power Today"] = (VE_power_max_today, DEC);
doc["Max Power Yesterday"] = (VE_power_max_yesterday, DEC);
doc["MPPT Code"] = (VE_mppt, DEC);
doc["MPPT Firmware"] = (VE_fw, DEC);
doc["Day Sequence Number"] = (VE_day_sequence_number, DEC);
doc["Error Code"] = (VE_error, DEC);
doc["Error Code Msg"] = ErrorMsg;
doc["State of operation"] = (VE_state, DEC);
doc["State of operation"] = OpMsg;
//PressureSwitchState
doc["PressureSwOnTime"] = PressureSwOnTime;
doc["PressureSwOffTime"] = PressureSwOffTime;
//FloatSwitchState
doc["FloatSwOnTime"] = FloatSwOnTime;
doc["FloatwOffTime"] = FloatSwOffTime;
doc["MotorRunningCounter"] = MotorRunningCounter;
// Generate the minified JSON and send it to the Serial port.
serializeJson(doc, Serial);
// Start a new line
Serial.println();
// Generate the prettified JSON and send it to the Serial port.
// serializeJsonPretty(doc, Serial);
}
/******************************************************************
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);
}
/******************************************************************
VEDirect Arduino
Copyright 2018, 2019, 2020 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
- 2020-08-24 - Contribution of Rickard Nordström Pettersson
- Added VE_SOC, VE_POWER, VE_ALARM for BMW devices
******************************************************************/
#ifndef VEDIRECT_H_
#define VEDIRECT_H_
#include <Arduino.h>
// 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 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
#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_SOC,
VE_POWER,
VE_ALARM,
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",
"SOC",
"P",
"Alarm"
};
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_ */