Skip to content

Commit

Permalink
First version
Browse files Browse the repository at this point in the history
  • Loading branch information
helgeerbe committed Aug 8, 2022
1 parent 778cce2 commit 0e4edc9
Show file tree
Hide file tree
Showing 20 changed files with 789 additions and 76 deletions.
Binary file added .DS_Store
Binary file not shown.
7 changes: 5 additions & 2 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#define MQTT_MAX_PASSWORD_STRLEN 32
#define MQTT_MAX_TOPIC_STRLEN 32
#define MQTT_MAX_LWTVALUE_STRLEN 20
#define MQTT_MAX_ROOT_CA_CERT_STRLEN 2048
#define MQTT_MAX_ROOT_CA_CERT_STRLEN 2048

#define INV_MAX_NAME_STRLEN 31
#define INV_MAX_COUNT 10
Expand Down Expand Up @@ -72,7 +72,10 @@ struct CONFIG_T {
char Mqtt_Hass_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool Mqtt_Hass_IndividualPanels;
bool Mqtt_Tls;
char Mqtt_RootCaCert[MQTT_MAX_ROOT_CA_CERT_STRLEN + 1];
char Mqtt_RootCaCert[MQTT_MAX_ROOT_CA_CERT_STRLEN +1];

bool Vedirect_Enabled;
bool Vedirect_UpdatesOnly;
};

class ConfigurationClass {
Expand Down
27 changes: 27 additions & 0 deletions include/MqttVedirectPublishing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include "VeDirectFrameHandler.h"
#include "Configuration.h"
#include <Arduino.h>
#include <map>

#ifndef VICTRON_PIN_RX
#define VICTRON_PIN_RX 22
#endif

#ifndef VICTRON_PIN_TX
#define VICTRON_PIN_TX 21
#endif

class MqttVedirectPublishingClass {
public:
void init();
void loop();
private:
std::map<String, String> _kv_map;
VeDirectFrameHandler _myve;
uint32_t _lastPublish;
};

extern MqttVedirectPublishingClass MqttVedirectPublishing;
2 changes: 2 additions & 0 deletions include/WebApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "WebApi_sysstatus.h"
#include "WebApi_webapp.h"
#include "WebApi_ws_live.h"
#include "WebApi_vedirect.h"
#include <ESPAsyncWebServer.h>

class WebApiClass {
Expand All @@ -33,6 +34,7 @@ class WebApiClass {
WebApiSysstatusClass _webApiSysstatus;
WebApiWebappClass _webApiWebapp;
WebApiWsLiveClass _webApiWsLive;
WebApiVedirectClass _webApiVedirect;
};

extern WebApiClass WebApi;
17 changes: 17 additions & 0 deletions include/WebApi_vedirect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <ESPAsyncWebServer.h>

class WebApiVedirectClass {
public:
void init(AsyncWebServer* server);
void loop();

private:
void onVedirectStatus(AsyncWebServerRequest* request);
void onVedirectAdminGet(AsyncWebServerRequest* request);
void onVedirectAdminPost(AsyncWebServerRequest* request);

AsyncWebServer* _server;
};
5 changes: 4 additions & 1 deletion include/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,7 @@
#define MQTT_HASS_ENABLED false
#define MQTT_HASS_RETAIN true
#define MQTT_HASS_TOPIC "homeassistant/"
#define MQTT_HASS_INDIVIDUALPANELS false
#define MQTT_HASS_INDIVIDUALPANELS false

#define VEDIRECT_ENABLED false
#define VEDIRECT_UPDATESONLY true
Binary file added lib/.DS_Store
Binary file not shown.
Binary file added lib/Hoymiles/.DS_Store
Binary file not shown.
210 changes: 210 additions & 0 deletions lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/* framehandler.cpp
*
* Arduino library to read from Victron devices using VE.Direct protocol.
* Derived from Victron framehandler reference implementation.
*
* The MIT License
*
* Copyright (c) 2019 Victron Energy BV
* Portions Copyright (C) 2020 Chris Terwilliger
* https://github.com/cterwilliger/VeDirectFrameHandler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* 2020.05.05 - 0.2 - initial release
* 2020.06.21 - 0.2 - add MIT license, no code changes
* 2020.08.20 - 0.3 - corrected #include reference
*
*/

#include <Arduino.h>
#include "VeDirectFrameHandler.h"

char MODULE[] = "VE.Frame"; // Victron seems to use this to find out where logging messages were generated

// The name of the record that contains the checksum.
static constexpr char checksumTagName[] = "CHECKSUM";

VeDirectFrameHandler::VeDirectFrameHandler() :
//mStop(false), // don't know what Victron uses this for, not using
veName(),
veValue(),
frameIndex(0),
veEnd(0),
mState(IDLE),
mChecksum(0),
mTextPointer(0),
tempName(),
tempValue()
{
}

/*
* rxData
* This function is called by the application which passes a byte of serial data
* It is unchanged from Victron's example code
*/
void VeDirectFrameHandler::rxData(uint8_t inbyte)
{
//if (mStop) return;
if ( (inbyte == ':') && (mState != CHECKSUM) ) {
mState = RECORD_HEX;
}
if (mState != RECORD_HEX) {
mChecksum += inbyte;
}
inbyte = toupper(inbyte);

switch(mState) {
case IDLE:
/* wait for \n of the start of an record */
switch(inbyte) {
case '\n':
mState = RECORD_BEGIN;
break;
case '\r': /* Skip */
default:
break;
}
break;
case RECORD_BEGIN:
mTextPointer = mName;
*mTextPointer++ = inbyte;
mState = RECORD_NAME;
break;
case RECORD_NAME:
// The record name is being received, terminated by a \t
switch(inbyte) {
case '\t':
// the Checksum record indicates a EOR
if ( mTextPointer < (mName + sizeof(mName)) ) {
*mTextPointer = 0; /* Zero terminate */
if (strcmp(mName, checksumTagName) == 0) {
mState = CHECKSUM;
break;
}
}
mTextPointer = mValue; /* Reset value pointer */
mState = RECORD_VALUE;
break;
default:
// add byte to name, but do no overflow
if ( mTextPointer < (mName + sizeof(mName)) )
*mTextPointer++ = inbyte;
break;
}
break;
case RECORD_VALUE:
// The record value is being received. The \r indicates a new record.
switch(inbyte) {
case '\n':
// forward record, only if it could be stored completely
if ( mTextPointer < (mValue + sizeof(mValue)) ) {
*mTextPointer = 0; // make zero ended
textRxEvent(mName, mValue);
}
mState = RECORD_BEGIN;
break;
case '\r': /* Skip */
break;
default:
// add byte to value, but do no overflow
if ( mTextPointer < (mValue + sizeof(mValue)) )
*mTextPointer++ = inbyte;
break;
}
break;
case CHECKSUM:
{
bool valid = mChecksum == 0;
if (!valid)
logE(MODULE,"[CHECKSUM] Invalid frame");
mChecksum = 0;
mState = IDLE;
frameEndEvent(valid);
break;
}
case RECORD_HEX:
if (hexRxEvent(inbyte)) {
mChecksum = 0;
mState = IDLE;
}
break;
}
}

/*
* textRxEvent
* This function is called every time a new name/value is successfully parsed. It writes the values to the temporary buffer.
*/
void VeDirectFrameHandler::textRxEvent(char * mName, char * mValue) {
strcpy(tempName[frameIndex], mName); // copy name to temporary buffer
strcpy(tempValue[frameIndex], mValue); // copy value to temporary buffer
frameIndex++;
}

/*
* frameEndEvent
* This function is called at the end of the received frame. If the checksum is valid, the temp buffer is read line by line.
* If the name exists in the public buffer, the new value is copied to the public buffer. If not, a new name/value entry
* is created in the public buffer.
*/
void VeDirectFrameHandler::frameEndEvent(bool valid) {
if ( valid ) {
for ( int i = 0; i < frameIndex; i++ ) { // read each name already in the temp buffer
bool nameExists = false;
for ( int j = 0; j <= veEnd; j++ ) { // compare to existing names in the public buffer
if ( strcmp(tempName[i], veName[j]) == 0 ) {
strcpy(veValue[j], tempValue[i]); // overwrite tempValue in the public buffer
nameExists = true;
break;
}
}
if ( !nameExists ) {
strcpy(veName[veEnd], tempName[i]); // write new Name to public buffer
strcpy(veValue[veEnd], tempValue[i]); // write new Value to public buffer
veEnd++; // increment end of public buffer
if ( veEnd >= buffLen ) { // stop any buffer overrun
veEnd = buffLen - 1;
}
}
}
}
frameIndex = 0; // reset frame
}

/*
* logE
* This function included for continuity and possible future use.
*/
void VeDirectFrameHandler::logE(const char * module, const char * error) {
Serial.print("MODULE: ");
Serial.println(module);
Serial.print("ERROR: ");
Serial.println(error);
return;
}

/*
* hexRxEvent
* This function included for continuity and possible future use.
*/
bool VeDirectFrameHandler::hexRxEvent(uint8_t inbyte) {
return true; // stubbed out for future
}
63 changes: 63 additions & 0 deletions lib/VeDirectFrameHandler/VeDirectFrameHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* frameHandler.h
*
* Arduino library to read from Victron devices using VE.Direct protocol.
* Derived from Victron framehandler reference implementation.
*
* 2020.05.05 - 0.2 - initial release
* 2021.02.23 - 0.3 - change frameLen to 22 per VE.Direct Protocol version 3.30
*
*/

#ifndef FRAMEHANDLER_H_
#define FRAMEHANDLER_H_

#include <Arduino.h>

const byte frameLen = 22; // VE.Direct Protocol: max frame size is 18
const byte nameLen = 9; // VE.Direct Protocol: max name size is 9 including /0
const byte valueLen = 33; // VE.Direct Protocol: max value size is 33 including /0
const byte buffLen = 40; // Maximum number of lines possible from the device. Current protocol shows this to be the BMV700 at 33 lines.


class VeDirectFrameHandler {

public:
VeDirectFrameHandler();
void rxData(uint8_t inbyte); // byte of serial data to be passed by the application

char veName[buffLen][nameLen] = { }; // public buffer for received names
char veValue[buffLen][valueLen] = { }; // public buffer for received values

int frameIndex; // which line of the frame are we on
int veEnd; // current size (end) of the public buffer

private:
//bool mStop; // not sure what Victron uses this for, not using

enum States { // state machine
IDLE,
RECORD_BEGIN,
RECORD_NAME,
RECORD_VALUE,
CHECKSUM,
RECORD_HEX
};

int mState; // current state

uint8_t mChecksum; // checksum value

char * mTextPointer; // pointer to the private buffer we're writing to, name or value

char mName[9]; // buffer for the field name
char mValue[33]; // buffer for the field value
char tempName[frameLen][nameLen]; // private buffer for received names
char tempValue[frameLen][valueLen]; // private buffer for received values

void textRxEvent(char *, char *);
void frameEndEvent(bool);
void logE(const char *, const char *);
bool hexRxEvent(uint8_t);
};

#endif // FRAMEHANDLER_H_
Loading

0 comments on commit 0e4edc9

Please sign in to comment.