forked from winginitau/VictronVEDirectArduino
-
Notifications
You must be signed in to change notification settings - Fork 1
/
VEDirect.cpp
295 lines (269 loc) · 7.04 KB
/
VEDirect.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/******************************************************************
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();
}
}