-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathemeterpacket.h
190 lines (160 loc) · 5.2 KB
/
emeterpacket.h
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
#ifndef EMETERPACKET_H
#define EMETERPACKET_H
#include <stdint.h>
#include <string.h>
/**
* @brief Class to create SMA energy meter packets.
*
* For details see:
* https://www.sma.de/fileadmin/content/global/Partner/Documents/SMA_Labs/EMETER-Protokoll-TI-de-10.pdf
*/
class EmeterPacket {
public:
// IDs to identify values in the energy meter packets
static const uint32_t SMA_POSITIVE_ACTIVE_POWER = 0x00010400;
static const uint32_t SMA_POSITIVE_REACTIVE_POWER = 0x00030400;
static const uint32_t SMA_NEGATIVE_ACTIVE_POWER = 0x00020400;
static const uint32_t SMA_NEGATIVE_REACTIVE_POWER = 0x00040400;
static const uint32_t SMA_POSITIVE_ENERGY = 0x00010800;
static const uint32_t SMA_NEGATIVE_ENERGY = 0x00020800;
static const uint32_t SMA_VERSION = 0x90000000;
/**
* @brief Constructor
*/
EmeterPacket(uint32_t serNo = 0U) {
initEmeterPacket(serNo);
begin(0U);
end();
}
/**
* @brief Initialize the packet with the given serial number
*/
void init(uint32_t serNo) {
initEmeterPacket(serNo);
}
/**
* @brief Begin the update sequence
*/
void begin(unsigned long timeStampMs) {
_pPacketPos = meterPacket + _headerLength;
storeU32BE(_pMeterTime, timeStampMs);
// Initial length of packet (ID + SN + TS)
_length = INITIAL_PAYLOAD_LENGTH;
}
/**
* @brief Add a measurement value (32 bit)
*/
void addMeasurementValue(uint32_t id, uint32_t value) {
_pPacketPos = storeU32BE(_pPacketPos, id);
_pPacketPos = storeU32BE(_pPacketPos, value);
_length += 8;
}
/**
* @brief Add a counter value (64 bit)
*/
void addCounterValue(uint32_t id, uint64_t value) {
_pPacketPos = storeU32BE(_pPacketPos, id);
_pPacketPos = storeU64BE(_pPacketPos, value);
_length += 12;
}
/**
* @brief End the update sequence
*/
uint16_t end() {
// Store version
_pPacketPos = storeU32BE(_pPacketPos, SMA_VERSION);
_pPacketPos = storeU32BE(_pPacketPos, 0x01020452);
_length += 8;
// Update length
storeU16BE(_pDataSize, _length);
// Add end-tag
storeU32BE(_pPacketPos, 0);
_length += 4;
// Calculate final length
_length = _headerLength + _length - INITIAL_PAYLOAD_LENGTH;
return _length;
}
/**
* @brief Get the data of the current packet
*/
const uint8_t *getData() const {
return meterPacket;
}
/**
* @brief Get the length of the current packet
*/
uint16_t getLength() const {
return _length;
}
private:
// Initial length of the payload (Protocol-ID + SRC + Time)
static const int INITIAL_PAYLOAD_LENGTH = 12;
// Buffer to store the energy-meter packet
static const int METER_PACKET_SIZE = 1000;
uint8_t meterPacket[METER_PACKET_SIZE];
// Length of the energy meter packet header
uint16_t _headerLength;
// Pointer to the data-length field in the energy meter packet
uint8_t *_pDataSize;
// Pointer to the time field in the energy meter packet
uint8_t *_pMeterTime;
// Current position in the packet
uint8_t *_pPacketPos;
// Current length of the packet
uint16_t _length;
/**
* @brief Store an U16 in big endian byte order
*/
uint8_t *storeU16BE(uint8_t *pPos, uint16_t value) {
*(pPos++) = value >> 8;
*(pPos++) = value & 0xff;
return pPos;
}
/**
* @brief Store an U32 in big endian byte order
*/
uint8_t *storeU32BE(uint8_t *pPos, uint32_t value) {
pPos = storeU16BE(pPos, value >> 16);
return storeU16BE(pPos, value & 0xffff);
}
/**
* @brief Store an U64 in big endian byte order
*/
uint8_t *storeU64BE(uint8_t *pPos, uint64_t value) {
pPos = storeU32BE(pPos, value >> 32);
return storeU32BE(pPos, value & 0xffffffff);
}
/**
* @brief Find the offset of the given identifier
*/
uint8_t* offsetOf(uint8_t *pData, uint8_t identifier, int size) {
for (int i = 0; i < size; ++i) {
if (pData[i] == identifier) {
return pData + i;
}
}
return 0;
}
/**
* @brief Initialize energy meter packet
*/
void initEmeterPacket(uint32_t serNo) {
// IDs to identify offsets in the SMA meter header
const uint8_t WLEN = 0xfa;
const uint8_t DSRC = 0xfb;
const uint8_t DTIM = 0xfc;
// Protocol-header for SMA energy meter packet
uint8_t SMA_METER_HEADER[] = { 'S', 'M', 'A', 0, // Identifier
0x00, 0x04, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x01, // Group 1
WLEN, WLEN, 0x00, 0x10, 0x60, 0x69, // Start of protocol 0x6069
0x01, 0x0e, DSRC, DSRC, DSRC, DSRC, // Source address
DTIM, DTIM, DTIM, DTIM }; // Timestamp
_headerLength = sizeof(SMA_METER_HEADER);
memcpy(meterPacket, SMA_METER_HEADER, _headerLength);
_pDataSize = offsetOf(meterPacket, WLEN, _headerLength);
_pMeterTime = offsetOf(meterPacket, DTIM, _headerLength);
uint8_t *pSerNo = offsetOf(meterPacket, DSRC, _headerLength);
storeU32BE(pSerNo, serNo);
}
};
#endif // EMETERPACKET_H