5
5
* @author : Erwan Martin <[email protected] >
6
6
******************************************************************************
7
7
*/
8
+ #include <math.h>
9
+ #include <stdio.h>
10
+
8
11
#include "PRIOS.h"
9
12
13
+ const char * const unit_displays [] = {
14
+ [UNKNOWN_UNIT ] = "banana" ,
15
+ [VOLUME_CUBIC_METER ] = "m3" ,
16
+ };
17
+
10
18
/* The default encryption keys of IZAR devices */
11
19
uint8_t PRIOS_DEFAULT_KEY1 [8 ] = {0x39 , 0xBC , 0x8A , 0x10 , 0xE6 , 0x6D , 0x83 , 0xF8 };
12
20
uint8_t PRIOS_DEFAULT_KEY2 [8 ] = {0x51 , 0x72 , 0x89 , 0x10 , 0xE6 , 0x6D , 0x83 , 0xF8 };
@@ -18,7 +26,7 @@ uint8_t PRIOS_DEFAULT_KEY2[8] = {0x51, 0x72, 0x89, 0x10, 0xE6, 0x6D, 0x83, 0xF8}
18
26
* @param int offset Offset from the start of the location.
19
27
* @retval uint32_t
20
28
*/
21
- uint32_t read_uint32_le (uint8_t * data , int offset ) {
29
+ uint32_t read_uint32_le (const uint8_t * const data , int offset ) {
22
30
uint32_t result = * (data + offset + 3 ) << 24 ;
23
31
result |= * (data + offset + 2 ) << 16 ;
24
32
result |= * (data + offset + 1 ) << 8 ;
@@ -33,7 +41,7 @@ uint32_t read_uint32_le(uint8_t *data, int offset) {
33
41
* @param int offset Offset from the start of the location.
34
42
* @retval uint32_t
35
43
*/
36
- uint32_t read_uint32_be (uint8_t * data , int offset ) {
44
+ uint32_t read_uint32_be (const uint8_t * const data , int offset ) {
37
45
uint32_t result = * (data + offset ) << 24 ;
38
46
result |= * (data + offset + 1 ) << 16 ;
39
47
result |= * (data + offset + 2 ) << 8 ;
@@ -47,7 +55,7 @@ uint32_t read_uint32_be(uint8_t *data, int offset) {
47
55
* @param uint8_t *bytes Location of where to extract the key from.
48
56
* @retval uint32_t
49
57
*/
50
- uint32_t preparePRIOSKey (uint8_t * bytes ) {
58
+ uint32_t preparePRIOSKey (const uint8_t * const bytes ) {
51
59
uint32_t key1 = read_uint32_be (bytes , 0 );
52
60
uint32_t key2 = read_uint32_be (bytes , 4 );
53
61
uint32_t key = key1 ^ key2 ;
@@ -68,7 +76,7 @@ uint32_t preparePRIOSKey(uint8_t *bytes) {
68
76
* @param uint8_t *out Buffer to store the decoded data
69
77
* @retval uint8_t
70
78
*/
71
- uint8_t decodePRIOSPayload (uint8_t * in , uint8_t payload_len , uint32_t key , uint8_t * out ) {
79
+ uint8_t decodePRIOSPayload (const uint8_t * const in , const uint8_t payload_len , uint32_t key , uint8_t * out ) {
72
80
// modify seed key with header values
73
81
key ^= read_uint32_be (in , 2 ); // manufacturer + address[0-1]
74
82
key ^= read_uint32_be (in , 6 ); // address[2-3] + version + type
@@ -95,27 +103,105 @@ uint8_t decodePRIOSPayload(uint8_t *in, uint8_t payload_len, uint32_t key, uint8
95
103
}
96
104
97
105
/**
98
- * @brief Get the water metrics from a decoded PRIOS payload.
99
- * @param uint8_t *payload The location of the decoded PRIOS payload
100
- * @param uint32_t *total_consumption Where to store the current total
101
- consumption
102
- * @param uint32_t *last_month_total_consumption Where to store last month's
103
- consumption
104
- */
105
- void getMetricsFromPriosPayload (uint8_t * payload , uint32_t * total_consumption , uint32_t * last_month_total_consumption ) {
106
- * total_consumption = read_uint32_le (payload , 1 );
107
- * last_month_total_consumption = read_uint32_le (payload , 5 );
106
+ * @brief Extract the data from a PRIOS frame.
107
+ * @param uint8_t *header_data The start of the wmbus frame application data.
108
+ * @param uint8_t *decoded_data The data that was decrypted from the frame.
109
+ * @param izar_reading *reading Where to store the extracted data.
110
+ */
111
+ void parsePRIOSFrame (const uint8_t * const header_data , const uint8_t * const decoded_data , izar_reading * const reading ) {
112
+ // Extract the header values:
113
+ reading -> radio_interval = 1 << ((header_data [0 ] & 0x0F ) + 2 );
114
+ reading -> random_generator = (header_data [0 ] >> 4 ) & 0x3 ;
115
+
116
+ // Extract the battery remaining life:
117
+ reading -> remaining_battery_life = (header_data [1 ] & 0x1F ) / 2.0 ;
118
+
119
+ // Read the alarms:
120
+ reading -> alarms .general_alarm = header_data [0 ] >> 7 ;
121
+ reading -> alarms .leakage_currently = header_data [1 ] >> 7 ;
122
+ reading -> alarms .leakage_previously = header_data [1 ] >> 6 & 0x1 ;
123
+ reading -> alarms .meter_blocked = header_data [1 ] >> 5 & 0x1 ;
124
+ reading -> alarms .back_flow = header_data [2 ] >> 7 ;
125
+ reading -> alarms .underflow = header_data [2 ] >> 6 & 0x1 ;
126
+ reading -> alarms .overflow = header_data [2 ] >> 5 & 0x1 ;
127
+ reading -> alarms .submarine = header_data [2 ] >> 4 & 0x1 ;
128
+ reading -> alarms .sensor_fraud_currently = header_data [2 ] >> 3 & 0x1 ;
129
+ reading -> alarms .sensor_fraud_previously = header_data [2 ] >> 2 & 0x1 ;
130
+ reading -> alarms .mechanical_fraud_currently = header_data [2 ] >> 1 & 0x1 ;
131
+ reading -> alarms .mechanical_fraud_previously = header_data [2 ] & 0x1 ;
132
+
133
+ // Read the readings:
134
+ reading -> current_reading = read_uint32_le (decoded_data , 1 );
135
+ reading -> h0_reading = read_uint32_le (decoded_data , 5 );
136
+
137
+ // Apply the multiplier to the values:
138
+ int8_t multiplier_exponent = (header_data [3 ] & 0x07 ) - 6 ;
139
+ if (multiplier_exponent > 0 ) {
140
+ reading -> current_reading *= pow (10 , multiplier_exponent );
141
+ reading -> h0_reading *= pow (10 , multiplier_exponent );
142
+ } else if (multiplier_exponent < 0 ) {
143
+ reading -> current_reading /= pow (10 , - multiplier_exponent );
144
+ reading -> h0_reading /= pow (10 , - multiplier_exponent );
145
+ }
146
+
147
+ // Extract the measurement unit:
148
+ uint8_t unit_type = header_data [3 ] >> 3 ;
149
+ if (unit_type == 0x02 ) {
150
+ reading -> unit_type = VOLUME_CUBIC_METER ;
151
+ } else {
152
+ reading -> unit_type = UNKNOWN_UNIT ;
153
+ }
154
+
155
+ // Extract the date:
156
+ reading -> h0_year = ((decoded_data [10 ] & 0xF0 ) >> 1 ) + ((decoded_data [9 ] & 0xE0 ) >> 5 );
157
+ if (reading -> h0_year > 80 ) {
158
+ reading -> h0_year += 1900 ;
159
+ } else {
160
+ reading -> h0_year += 2000 ;
161
+ }
162
+ reading -> h0_month = decoded_data [10 ] & 0xF ;
163
+ reading -> h0_day = decoded_data [9 ] & 0x1F ;
164
+ }
165
+
166
+ /**
167
+ * @brief Print an entire IZAR reading to the standard output device, as CSV data.
168
+ * @param uint32_t A_Id The identifier of the device the reading was from.
169
+ * @param izar_reading *reading The reading to print
170
+ */
171
+ void printIZARReadingAsCSV (const uint32_t A_Id , const izar_reading * const reading ) {
172
+ printf (
173
+ "%.6x,%f,%f,%s,%.2d,%.2d,%.2d,%.1f,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n" ,
174
+ A_Id ,
175
+ reading -> current_reading ,
176
+ reading -> h0_reading ,
177
+ unit_displays [reading -> unit_type ],
178
+ reading -> h0_year ,
179
+ reading -> h0_month ,
180
+ reading -> h0_day ,
181
+ reading -> remaining_battery_life ,
182
+ reading -> radio_interval ,
183
+ reading -> random_generator ,
184
+ reading -> alarms .general_alarm ,
185
+ reading -> alarms .leakage_currently ,
186
+ reading -> alarms .leakage_previously ,
187
+ reading -> alarms .meter_blocked ,
188
+ reading -> alarms .back_flow ,
189
+ reading -> alarms .underflow ,
190
+ reading -> alarms .overflow ,
191
+ reading -> alarms .submarine ,
192
+ reading -> alarms .sensor_fraud_currently ,
193
+ reading -> alarms .sensor_fraud_previously ,
194
+ reading -> alarms .mechanical_fraud_currently ,
195
+ reading -> alarms .mechanical_fraud_previously
196
+ );
108
197
}
109
198
110
199
/**
111
200
* @brief Get the water metrics from a WMBus Prios frame.
112
201
* @param uint8_t *payload The location of the WMBus frame
113
- * @param uint32_t *total_consumption Where to store the current total
114
- consumption
115
- * @param uint32_t *last_month_total_consumption Where to store last month's
116
- consumption
202
+ * @param izar_reading *reading Where to store the extracted data
117
203
*/
118
- uint8_t getMetricsFromPRIOSWMBusFrame (uint8_t * frame , uint32_t * total_consumption , uint32_t * last_month_total_consumption ) {
204
+ uint8_t getMetricsFromPRIOSWMBusFrame (const uint8_t * const frame , izar_reading * const reading ) {
119
205
/* Decode the payload */
120
206
uint32_t key = preparePRIOSKey (PRIOS_DEFAULT_KEY1 );
121
207
uint8_t decodedPayload [32 ];
@@ -124,7 +210,9 @@ uint8_t getMetricsFromPRIOSWMBusFrame(uint8_t *frame, uint32_t *total_consumptio
124
210
return 0 ;
125
211
}
126
212
127
- /* Get the data from the decoded payload */
128
- getMetricsFromPriosPayload (decodedPayload , total_consumption , last_month_total_consumption );
213
+ /* Extract the data from the frame and the decoded data: */
214
+ parsePRIOSFrame (frame + 13 , decodedPayload , reading );
215
+
216
+ /* Return: */
129
217
return 1 ;
130
218
}
0 commit comments