Arduino to Victron VE.Direct

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_ */

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.