MeterLogger
kmp_request.c
Go to the documentation of this file.
1 #include <esp8266.h>
2 #include "driver/uart.h"
3 #include "unix_time.h"
4 #include "mqtt.h"
5 #include "tinyprintf.h"
6 #include "utils.h"
7 #include "kmp.h"
8 #include "kmp_request.h"
9 #include "config.h"
10 #include "crypto/crypto.h"
11 #include "crypto/aes.h"
12 
13 #define QUEUE_SIZE 256
14 
15 uint32_t kmp_serial = 0;
18 //unsigned int mqtt_lwt_flag = 0;
19 
20 // fifo
21 volatile unsigned int fifo_head, fifo_tail;
22 volatile unsigned char fifo_buffer[QUEUE_SIZE];
23 
24 // allocate frame to send
25 unsigned char frame[KMP_FRAME_L];
26 unsigned int frame_length;
27 uint16_t register_list[8];
28 
29 // allocate struct for response
31 
32 static MQTT_Client *mqtt_client = NULL; // initialize to NULL
33 
36 
38 
39 unsigned int kmp_requests_sent;
40 
41 #ifdef DEBUG_NO_METER
42 uint8_t pseudo_data_debug_no_meter = 0;
43 #endif
44 
46 static void kmp_received_task(os_event_t *events) {
47  unsigned char c;
48  unsigned int i;
49  uint64_t current_unix_time;
50  char current_unix_time_string[64]; // BUGFIX var
51  char key_value[256];
52  unsigned char topic[MQTT_TOPIC_L];
53  unsigned char message[KMP_FRAME_L];
54  int message_l;
55 
56  // vars for aes encryption
57  uint8_t cleartext[KMP_FRAME_L];
58 
59  // allocate struct for response
61  unsigned char kmp_unit_string[16];
62  unsigned char kmp_value_string[64];
63 
64  //ETS_UART_INTR_DISABLE();
65 
66  memset(message, 0x00, KMP_FRAME_L); // clear message buffer
67  i = 0;
68  while (kmp_fifo_get(&c) && (i <= KMP_FRAME_L)) {
69  message[i++] = c;
70  }
71  message_l = i;
72 
73  // decode kmp frame
74  if (kmp_decode_frame(message, message_l, &response) > 0) {
75  // tell user_main we got a serial
79  }
80 #ifdef DEBUG
81  else {
82  os_printf("tried to call kmp_meter_is_ready_cb() before it was set - should not happen\n");
83  }
84 #endif
85  message_l = 0; // zero it so we can reuse it for mqtt string
86  current_unix_time = (uint32)(get_unix_time()); // TODO before 2038 ,-)
87 
88  if (response.kmp_response_serial) {
89  kmp_serial = response.kmp_response_serial; // save it for later use
90  }
91  else if (current_unix_time) { // only send mqtt if we got current time via ntp
92  // prepare for mqtt transmission if we got serial number from meter
93 
94  // format /sample/v1/serial/unix_time => val1=23&val2=val3&baz=blah
95  //topic_l = os_sprintf(topic, "/sample/v1/%lu/%lu", kmp_serial, current_unix_time);
96  // BUG here. returns 0 -^
97  // this is a fix
98  memset(topic, 0, sizeof(topic)); // clear it
99  tfp_snprintf(current_unix_time_string, 64, "%u", (uint32_t)current_unix_time);
100  tfp_snprintf(topic, MQTT_TOPIC_L, "/sample/v2/%u/%s", kmp_serial, current_unix_time_string);
101 
102  memset(message, 0, sizeof(message)); // clear it
103 
104  // heap size
105  tfp_snprintf(key_value, MQTT_TOPIC_L, "heap=%u&", system_get_free_heap_size());
106  strcat(message, key_value);
107 
108  // heating meter specific
109  // flow temperature
110  kmp_value_to_string(response.kmp_response_register_list[3].value, response.kmp_response_register_list[3].si_ex, kmp_value_string);
111  kmp_unit_to_string(response.kmp_response_register_list[3].unit, kmp_unit_string);
112  tfp_snprintf(key_value, MQTT_TOPIC_L, "t1=%s %s&", kmp_value_string, kmp_unit_string);
113  strcat(message, key_value);
114 
115  // return flow temperature
116  kmp_value_to_string(response.kmp_response_register_list[4].value, response.kmp_response_register_list[4].si_ex, kmp_value_string);
117  kmp_unit_to_string(response.kmp_response_register_list[4].unit, kmp_unit_string);
118  tfp_snprintf(key_value, MQTT_TOPIC_L, "t2=%s %s&", kmp_value_string, kmp_unit_string);
119  strcat(message, key_value);
120 
121  // temperature difference
122  kmp_value_to_string(response.kmp_response_register_list[5].value, response.kmp_response_register_list[5].si_ex, kmp_value_string);
123  kmp_unit_to_string(response.kmp_response_register_list[5].unit, kmp_unit_string);
124  tfp_snprintf(key_value, MQTT_TOPIC_L, "tdif=%s %s&", kmp_value_string, kmp_unit_string);
125  strcat(message, key_value);
126 
127  // flow
128  kmp_value_to_string(response.kmp_response_register_list[6].value, response.kmp_response_register_list[6].si_ex, kmp_value_string);
129  kmp_unit_to_string(response.kmp_response_register_list[6].unit, kmp_unit_string);
130  tfp_snprintf(key_value, MQTT_TOPIC_L, "flow1=%s %s&", kmp_value_string, kmp_unit_string);
131  strcat(message, key_value);
132 
133  // current power
134  kmp_value_to_string(response.kmp_response_register_list[7].value, response.kmp_response_register_list[7].si_ex, kmp_value_string);
135  kmp_unit_to_string(response.kmp_response_register_list[7].unit, kmp_unit_string);
136  tfp_snprintf(key_value, MQTT_TOPIC_L, "effect1=%s %s&", kmp_value_string, kmp_unit_string);
137  strcat(message, key_value);
138 
139  // hours
140  kmp_value_to_string(response.kmp_response_register_list[2].value, response.kmp_response_register_list[2].si_ex, kmp_value_string);
141  kmp_unit_to_string(response.kmp_response_register_list[2].unit, kmp_unit_string);
142  tfp_snprintf(key_value, MQTT_TOPIC_L, "hr=%s %s&", kmp_value_string, kmp_unit_string);
143  strcat(message, key_value);
144 
145  // volume
146  kmp_value_to_string(response.kmp_response_register_list[1].value, response.kmp_response_register_list[1].si_ex, kmp_value_string);
147  kmp_unit_to_string(response.kmp_response_register_list[1].unit, kmp_unit_string);
148  tfp_snprintf(key_value, MQTT_TOPIC_L, "v1=%s %s&", kmp_value_string, kmp_unit_string);
149  strcat(message, key_value);
150 
151  // power
152  kmp_value_to_string(response.kmp_response_register_list[0].value, response.kmp_response_register_list[0].si_ex, kmp_value_string);
153  kmp_unit_to_string(response.kmp_response_register_list[0].unit, kmp_unit_string);
154  tfp_snprintf(key_value, MQTT_TOPIC_L, "e1=%s %s&", kmp_value_string, kmp_unit_string);
155  strcat(message, key_value);
156 
157  memset(cleartext, 0, sizeof(message));
158  os_strncpy(cleartext, message, sizeof(message)); // make a copy of message for later use
159  os_memset(message, 0, sizeof(message)); // ...and clear it
160 
161  // encrypt and send
162  message_l = encrypt_aes_hmac_combined(message, topic, strlen(topic), cleartext, strlen(cleartext) + 1);
163 
164  if (mqtt_client) {
165  // if mqtt_client is initialized
166  if (kmp_serial && (message_l > 1)) {
167  // if we received both serial and registers send it
168  MQTT_Publish(mqtt_client, topic, message, message_l, 2, 0); // QoS level 2
169  }
170  }
171  kmp_requests_sent = 0; // reset retry counter
172  // disable timer
173  }
174  }
175  else {
176  // error decoding frame - kmp_receive_timeout_timer_func() retransmits after timeout
177  }
178 }
179 
182  fifo_head = 0;
183  fifo_tail = 0;
184 
185  kmp_requests_sent = 0;
186 
188 }
189 
190 // helper function to pass mqtt client struct from user_main.c to here
193  mqtt_client = client;
194 }
195 
199 }
200 
201 // helper function to pass received kmp_serial to user_main.c
203 unsigned int kmp_get_received_serial() {
204  return kmp_serial;
205 }
206 
207 // helper function to pass energy to user_main.c
210  unsigned char kmp_unit_string[16];
211  unsigned char kmp_value_string[64];
212 
213  char e1_kwh[64];
214 
215  kmp_value_to_string(response.kmp_response_register_list[0].value, response.kmp_response_register_list[0].si_ex, kmp_value_string);
216  kmp_unit_to_string(response.kmp_response_register_list[0].unit, kmp_unit_string);
217 
218  if (strncmp(kmp_unit_string, "MWh", 16) == 0) {
219  mw_to_w_str(kmp_unit_string, e1_kwh);
220  }
221 
222 #ifdef DEBUG_NO_METER
223  return pseudo_data_debug_no_meter;
224 #else
225  return atoi(e1_kwh);
226 #endif
227 }
228 
231  // get serial
232  // prepare frame
234  uart0_tx_buffer(frame, frame_length); // send kmp request
235 }
236 
239  // get registers
240  // prepare frame
241  register_list[0] = 0x3c; // heat energy (E1)
242  register_list[1] = 0x44; // volume register (V1)
243  register_list[2] = 0x3EC; // operational hour counter (HR)
244  register_list[3] = 0x56; // current flow temperature (T1)
245  register_list[4] = 0x57; // current return flow temperature (T2)
246  register_list[5] = 0x59; // current temperature difference (T1-T2)
247  register_list[6] = 0x4A; // current flow in flow (FLOW1)
248  register_list[7] = 0x50; // current power calculated on the basis of V1-T1-T2 (EFFEKT1)
250 
251  // send frame
252  uart0_tx_buffer(frame, frame_length); // send kmp request
253 }
254 
257  if (kmp_requests_sent > 0) {
258  // if no reply received, retransmit
260  }
261 }
262 
265 #ifdef DEBUG_NO_METER
266  char cleartext[MQTT_MESSAGE_L];
267  char topic[MQTT_TOPIC_L];
268  char message[KMP_FRAME_L];
269  int message_l;
270 
271  uint8_t sine_wave[256] = {
272  0x80, 0x83, 0x86, 0x89, 0x8C, 0x90, 0x93, 0x96,
273  0x99, 0x9C, 0x9F, 0xA2, 0xA5, 0xA8, 0xAB, 0xAE,
274  0xB1, 0xB3, 0xB6, 0xB9, 0xBC, 0xBF, 0xC1, 0xC4,
275  0xC7, 0xC9, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD8,
276  0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8,
277  0xEA, 0xEB, 0xED, 0xEF, 0xF0, 0xF1, 0xF3, 0xF4,
278  0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC,
279  0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF,
280  0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD,
281  0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF8, 0xF6,
282  0xF5, 0xF4, 0xF3, 0xF1, 0xF0, 0xEF, 0xED, 0xEB,
283  0xEA, 0xE8, 0xE6, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC,
284  0xDA, 0xD8, 0xD5, 0xD3, 0xD1, 0xCE, 0xCC, 0xC9,
285  0xC7, 0xC4, 0xC1, 0xBF, 0xBC, 0xB9, 0xB6, 0xB3,
286  0xB1, 0xAE, 0xAB, 0xA8, 0xA5, 0xA2, 0x9F, 0x9C,
287  0x99, 0x96, 0x93, 0x90, 0x8C, 0x89, 0x86, 0x83,
288  0x80, 0x7D, 0x7A, 0x77, 0x74, 0x70, 0x6D, 0x6A,
289  0x67, 0x64, 0x61, 0x5E, 0x5B, 0x58, 0x55, 0x52,
290  0x4F, 0x4D, 0x4A, 0x47, 0x44, 0x41, 0x3F, 0x3C,
291  0x39, 0x37, 0x34, 0x32, 0x2F, 0x2D, 0x2B, 0x28,
292  0x26, 0x24, 0x22, 0x20, 0x1E, 0x1C, 0x1A, 0x18,
293  0x16, 0x15, 0x13, 0x11, 0x10, 0x0F, 0x0D, 0x0C,
294  0x0B, 0x0A, 0x08, 0x07, 0x06, 0x06, 0x05, 0x04,
295  0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
296  0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03,
297  0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x0A,
298  0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x13, 0x15,
299  0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24,
300  0x26, 0x28, 0x2B, 0x2D, 0x2F, 0x32, 0x34, 0x37,
301  0x39, 0x3C, 0x3F, 0x41, 0x44, 0x47, 0x4A, 0x4D,
302  0x4F, 0x52, 0x55, 0x58, 0x5B, 0x5E, 0x61, 0x64,
303  0x67, 0x6A, 0x6D, 0x70, 0x74, 0x77, 0x7A, 0x7D
304  };
305 #endif
308  os_timer_arm(&kmp_get_serial_timer, 0, 0); // now
309 
312  os_timer_arm(&kmp_get_register_timer, 400, 0); // after 0.4 seconds
313 
314  // start retransmission timeout timer
317  os_timer_arm(&kmp_receive_timeout_timer, 2000, 0); // after 2 seconds
318 
320 #ifdef DEBUG_NO_METER
321  // clear data
322  memset(message, 0, sizeof(message));
323  memset(topic, 0, sizeof(topic));
324 
325  // fake serial for testing without meter
327 
328  tfp_snprintf(topic, MQTT_TOPIC_L, "/sample/v2/%u/%u", kmp_serial, get_unix_time());
329  memset(cleartext, 0, sizeof(cleartext));
330  tfp_snprintf(cleartext, KMP_FRAME_L, "heap=%u&t1=%u.00 C&t2=%u.00 C&tdif=%u.00 K&flow1=%u l/h&effect1=%u.0 kW&hr=%u h&v1=%u.00 m3&e1=%u kWh&",
332  65 + ((sine_wave[pseudo_data_debug_no_meter] * 10) >> 8), // t1
333  45 + ((sine_wave[(pseudo_data_debug_no_meter + 128) & 0xff] * 10) >> 8), // t2
334  10, // tdif
335  100 + ((sine_wave[((pseudo_data_debug_no_meter + 64) * 7) & 0xff] * 50) >> 8), // flow
336  15 + ((sine_wave[pseudo_data_debug_no_meter] * 10) >> 8), // effect1
337  pseudo_data_debug_no_meter / 60, // hr
338  pseudo_data_debug_no_meter, // v1
339  pseudo_data_debug_no_meter // e1
340  );
341  pseudo_data_debug_no_meter++; // let it wrap around
342 
343  // tell user_main we got a serial
347  }
348 #ifdef DEBUG
349  else {
350  os_printf("tried to call kmp_meter_is_ready_cb() before it was set - should not happen\n");
351  }
352 #endif
353 
354  // encrypt and send
355  message_l = encrypt_aes_hmac_combined(message, topic, strlen(topic), cleartext, strlen(cleartext) + 1);
356 
357  if (mqtt_client) {
358  // if mqtt_client is initialized
359  MQTT_Publish(mqtt_client, topic, message, message_l, 2, 0); // QoS level 2
360  }
361 #endif
362  kmp_requests_sent = 0; // reset retry counter
363 }
364 
365 // fifo
366 unsigned int kmp_fifo_in_use() {
367  return fifo_head - fifo_tail;
368 }
369 
370 unsigned char kmp_fifo_put(unsigned char c) {
371  if (kmp_fifo_in_use() != QUEUE_SIZE) {
373  // wrap
374  if (fifo_head == QUEUE_SIZE) {
375  fifo_head = 0;
376  }
377  return 1;
378  }
379  else {
380  return 0;
381  }
382 }
383 
384 unsigned char kmp_fifo_get(unsigned char *c) {
385  if (kmp_fifo_in_use() != 0) {
386  *c = fifo_buffer[fifo_tail++ % QUEUE_SIZE];
387  // wrap
388  if (fifo_tail == QUEUE_SIZE) {
389  fifo_tail = 0;
390  }
391  return 1;
392  }
393  else {
394  return 0;
395  }
396 }
397 
398 unsigned char kmp_fifo_snoop(unsigned char *c, unsigned int pos) {
399  if (kmp_fifo_in_use() > (pos)) {
400  *c = fifo_buffer[(fifo_tail + pos) % QUEUE_SIZE];
401  return 1;
402  }
403  else {
404  return 0;
405  }
406 }
ICACHE_FLASH_ATTR int kmp_decode_frame(unsigned char *frame, unsigned char frame_length, kmp_response_t *response)
Definition: kmp.c:239
static MQTT_Client * mqtt_client
Definition: kmp_request.c:32
ICACHE_FLASH_ATTR uint32_t get_unix_time(void)
Definition: unix_time.c:55
#define MQTT_TOPIC_L
Definition: user_config.h:25
ICACHE_FLASH_ATTR static void kmp_receive_timeout_timer_func()
Definition: kmp_request.c:256
#define os_timer_t
Definition: os_type.h:34
void ICACHE_FLASH_ATTR uart0_tx_buffer(uint8 *buf, uint16 len)
Definition: uart.c:163
#define os_timer_disarm
Definition: osapi.h:51
#define memset(x, a, b)
Definition: platform.h:21
uint32 system_get_free_heap_size(void)
#define NULL
Definition: def.h:47
ICACHE_FLASH_ATTR void kmp_request_send()
Definition: kmp_request.c:264
#define strcat(a, b)
Definition: platform.h:23
unsigned int kmp_fifo_in_use()
Definition: kmp_request.c:366
#define ICACHE_FLASH_ATTR
Definition: c_types.h:99
ICACHE_FLASH_ATTR void kmp_set_mqtt_client(MQTT_Client *client)
Definition: kmp_request.c:192
#define os_timer_func_t
Definition: os_type.h:35
ICACHE_FLASH_ATTR static void kmp_get_serial_timer_func()
Definition: kmp_request.c:230
#define os_printf
Definition: osapi.h:62
#define os_timer_setfn
Definition: osapi.h:52
ICACHE_FLASH_ATTR void kmp_unit_to_string(uint8_t unit, unsigned char *unit_string)
Definition: kmp.c:442
#define os_event_t
Definition: os_type.h:32
bool system_os_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen)
ICACHE_FLASH_ATTR static void kmp_get_register_timer_func()
Definition: kmp_request.c:238
#define DEFAULT_METER_SERIAL
Definition: user_config.h:21
ICACHE_FLASH_ATTR void kmp_request_init()
Definition: kmp_request.c:181
#define os_memset
Definition: osapi.h:38
#define os_strncpy
Definition: osapi.h:45
uint32_t kmp_serial
Definition: kmp_request.c:15
ICACHE_FLASH_ATTR void kmp_value_to_string(int32_t value, uint8_t si_ex, unsigned char *value_string)
Definition: kmp.c:394
void(* meter_is_ready_cb)(void)
bool meter_is_ready_cb_called
Definition: kmp_request.c:17
kmp_response_register_list_t kmp_response_register_list
Definition: kmp.h:18
volatile unsigned char fifo_buffer[QUEUE_SIZE]
Definition: kmp_request.c:22
ICACHE_FLASH_ATTR unsigned int kmp_get_received_serial()
Definition: kmp_request.c:203
volatile unsigned int fifo_tail
Definition: kmp_request.c:21
#define strncmp(a, b, c)
Definition: platform.h:18
unsigned int kmp_requests_sent
Definition: kmp_request.c:39
static os_timer_t kmp_receive_timeout_timer
Definition: kmp_request.c:37
ICACHE_FLASH_ATTR size_t encrypt_aes_hmac_combined(uint8_t *dst, uint8_t *topic, size_t topic_l, uint8_t *message, size_t message_l)
Definition: crypto.c:68
meter_is_ready_cb kmp_meter_is_ready_cb
Definition: kmp_request.c:16
unsigned int frame_length
Definition: kmp_request.c:26
unsigned char frame[KMP_FRAME_L]
Definition: kmp_request.c:25
static ICACHE_FLASH_ATTR void kmp_received_task(os_event_t *events)
Definition: kmp_request.c:46
unsigned int uint32
Definition: c_types.h:54
ICACHE_FLASH_ATTR void kmp_register_meter_is_ready_cb(meter_is_ready_cb cb)
Definition: kmp_request.c:197
ICACHE_FLASH_ATTR int tfp_snprintf(char *str, size_t size, const char *format,...)
Definition: tinyprintf.c:480
static os_timer_t kmp_get_register_timer
Definition: kmp_request.c:35
uint16_t register_list[8]
Definition: kmp_request.c:27
BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char *topic, const char *data, int data_length, int qos, int retain)
MQTT publish function.
Definition: mqtt.c:591
#define os_timer_arm(a, b, c)
Definition: osapi.h:50
static os_timer_t kmp_get_serial_timer
Definition: kmp_request.c:34
#define kmp_received_task_queue_length
Definition: kmp_request.h:4
ICACHE_FLASH_ATTR void mw_to_w_str(char *mw, char *w)
Definition: utils.c:105
unsigned char kmp_fifo_snoop(unsigned char *c, unsigned int pos)
Definition: kmp_request.c:398
os_event_t kmp_received_task_queue[kmp_received_task_queue_length]
Definition: kmp_request.h:8
unsigned char kmp_fifo_get(unsigned char *c)
Definition: kmp_request.c:384
ICACHE_FLASH_ATTR unsigned int kmp_get_serial(unsigned char *frame)
Definition: kmp.c:139
ICACHE_FLASH_ATTR unsigned int kmp_get_register(unsigned char *frame, uint16_t *register_list, uint16_t register_list_length)
Definition: kmp.c:181
#define MQTT_MESSAGE_L
Definition: user_config.h:26
#define KMP_FRAME_L
Definition: kmp.h:2
ICACHE_FLASH_ATTR unsigned int kmp_get_received_energy_kwh()
Definition: kmp_request.c:209
#define strlen(a)
Definition: platform.h:25
#define kmp_received_task_prio
Definition: kmp_request.h:3
unsigned char kmp_fifo_put(unsigned char c)
Definition: kmp_request.c:370
volatile unsigned int fifo_head
Definition: kmp_request.c:21
kmp_response_t response
Definition: kmp_request.c:30
uint32_t current_unix_time
Definition: unix_time.c:9
#define QUEUE_SIZE
Definition: kmp_request.c:13
unsigned int kmp_response_serial
Definition: kmp.h:15