MeterLogger
en61107_request.c
Go to the documentation of this file.
1 #include <esp8266.h>
2 #include "driver/uart.h"
3 
4 #include "unix_time.h"
5 #include "mqtt.h"
6 #include "tinyprintf.h"
7 #include "en61107_request.h"
8 #include "en61107.h"
9 #include "config.h"
10 #include "crypto/crypto.h"
11 #include "crypto/aes.h"
12 #include "utils.h"
13 #include "led.h"
14 
15 #define QUEUE_SIZE 256
16 
17 uint32_t en61107_serial = 0;
18 bool en61107_serial_set = false;
22 //unsigned int mqtt_lwt_flag = 0;
23 
24 // fifo
25 volatile unsigned int fifo_head, fifo_tail;
26 volatile unsigned char fifo_buffer[QUEUE_SIZE];
27 
28 // allocate frame to send
30 unsigned int frame_length;
31 
32 // allocate struct for response
34 
35 static MQTT_Client *mqtt_client = NULL; // initialize to NULL
36 
40 
42 
44 
45 volatile uint8_t en61107_eod;
47 
50  led_blink(); // DEBUG
51  // if no reply received, retransmit
52 
53  if (en61107_request_num <= 2) {
54  switch (en61107_uart_state) {
57  break;
58  case UART_STATE_EN61107:
59  en61107_uart_send_en61107_ident(); // restart en61107 request from state UART_STATE_EN61107_IDENT
60  break;
61 #ifndef MC_66B
64  break;
65 #endif
68  break;
71  break;
72  }
73  }
74  else {
75  // too many errors, wait for meter to be clear and start over
78  os_timer_arm(&en61107_meter_wake_up_timer, 60000, 0); // wait 60 seconds
79  }
80 }
81 
84  uart_set_baudrate(UART0, uart_settings->baut_rate);
85 // uart_set_word_length(UART0, SEVEN_BITS);
86 // uart_set_parity(UART0, EVEN_BITS);
87  uart_set_stop_bits(UART0, uart_settings->stop_bits);
88 }
89 
92 // en61107_uart_state = UART_STATE_EN61107_IDENT;
94 }
95 
96 // define en61107_received_task() first
98 static void en61107_received_task(os_event_t *events) {
99  unsigned char c;
100  unsigned int i;
101  uint64_t current_unix_time;
102  char current_unix_time_string[64]; // BUGFIX var
103  char key_value[128];
104  unsigned char topic[MQTT_TOPIC_L];
105  unsigned char message[EN61107_FRAME_L];
106  int message_l;
107 
108  // vars for aes encryption
109  uint8_t cleartext[EN61107_FRAME_L];
110 
111  switch (en61107_uart_state) {
113  // dont en61107_fifo_get() anything here, we get it in next state
115  break;
116  case UART_STATE_EN61107:
117  // get message buffer
118  memset(message, 0, sizeof(message));
119  i = 0;
120  while (en61107_fifo_get(&c) && (i <= EN61107_FRAME_L)) {
121  message[i++] = c;
122  }
123  message_l = i;
124 
125  if (parse_en61107_frame(&response, message, message_l) == false) {
126  // timeout if we cant parse response
127  break;
128  }
129 
130  en61107_serial = atoi(response.customer_no);
131  en61107_serial_set = true;
132 
133 #ifndef MC_66B
135 #else
137 #endif
138  break;
139 #ifndef MC_66B
141  // get message buffer
142  memset(message, 0, sizeof(message));
143  i = 0;
144  while (en61107_fifo_get(&c) && (i <= EN61107_FRAME_L)) {
145  message[i++] = c;
146  }
147  message_l = i;
148 
149  if (parse_mc66cde_inst_values_frame(&response, message, message_l) == false) {
150  // timeout if we cant parse response
151  break;
152  }
153 
155  break;
156 #endif
158  // get message buffer
159  memset(message, 0, sizeof(message));
160  i = 0;
161  while (en61107_fifo_get(&c) && (i <= EN61107_FRAME_L)) {
162  message[i++] = c;
163  }
164  message_l = i;
165  message[message_l - 2] = 0; // remove last two chars and null terminate
166 
167  if (parse_mc66cde_standard_data_1_frame(&response, message, message_l)) {
168  // if we can parse, send next request to meter
170  break;
171  }
172 
173  break;
175  // get message buffer
176  memset(message, 0, sizeof(message));
177  i = 0;
178  while (en61107_fifo_get(&c) && (i <= EN61107_FRAME_L)) {
179  message[i++] = c;
180  }
181  message_l = i;
182  message[message_l - 2] = 0; // remove last two chars and null terminate
183 
184  if (parse_mc66cde_standard_data_2_frame(&response, message, message_l)) {
185  // tell user_main we got a serial
189  }
190 #ifdef DEBUG
191  else {
192  os_printf("tried to call en61107_meter_is_ready_cb() before it was set - should not happen\n");
193  }
194 #endif
195  current_unix_time = (uint32)(get_unix_time()); // TODO before 2038 ,-)
196  if (current_unix_time) { // only send mqtt if we got current time via ntp
197  // format /sample/v2/serial/unix_time => val1=23&val2=val3&baz=blah
198  memset(topic, 0, sizeof(topic)); // clear it
199  tfp_snprintf(current_unix_time_string, 64, "%u", (uint32_t)current_unix_time);
200  tfp_snprintf(topic, MQTT_TOPIC_L, "/sample/v2/%07u/%s", en61107_serial, current_unix_time_string);
201 
202  memset(message, 0, sizeof(message)); // clear it
203 
204  // heap size
205  tfp_snprintf(key_value, MQTT_TOPIC_L, "heap=%u&", system_get_free_heap_size());
206  strcat(message, key_value);
207 
208  // meter program
209  tfp_snprintf(key_value, MQTT_TOPIC_L, "program=%01u%01u%03u%02u%01u%02u%02u&",
210  response.meter_program.a,
211  response.meter_program.b,
212  response.meter_program.ccc,
213  response.meter_program.dd,
214  response.meter_program.e,
215  response.meter_program.ff,
216  response.meter_program.gg
217  );
218  strcat(message, key_value);
219 
220  // heating meter specific
221  // flow temperature
222  tfp_snprintf(key_value, MQTT_TOPIC_L, "t1=%s %s&", response.t1.value, response.t1.unit);
223  strcat(message, key_value);
224 
225  // return flow temperature
226  tfp_snprintf(key_value, MQTT_TOPIC_L, "t2=%s %s&", response.t2.value, response.t2.unit);
227  strcat(message, key_value);
228 
229 #ifndef MC_66B
230  // t3 temperature
231  tfp_snprintf(key_value, MQTT_TOPIC_L, "t3=%s %s&", response.t3.value, response.t3.unit);
232  strcat(message, key_value);
233 #endif
234 
235  // calculated temperature difference
236  tfp_snprintf(key_value, MQTT_TOPIC_L, "tdif=%s %s&", response.tdif.value, response.tdif.unit);
237  strcat(message, key_value);
238 
239  // flow
240  tfp_snprintf(key_value, MQTT_TOPIC_L, "flow1=%s %s&", response.flow1.value, response.flow1.unit);
241  strcat(message, key_value);
242 
243  // current power
244  tfp_snprintf(key_value, MQTT_TOPIC_L, "effect1=%s %s&", response.effect1.value, response.effect1.unit);
245  strcat(message, key_value);
246 
247  // hours
248  tfp_snprintf(key_value, MQTT_TOPIC_L, "hr=%s %s&", response.hr.value, response.hr.unit);
249  strcat(message, key_value);
250 
251  // volume
252  tfp_snprintf(key_value, MQTT_TOPIC_L, "v1=%s %s&", response.v1.value, response.v1.unit);
253  strcat(message, key_value);
254 
255  // power
256  tfp_snprintf(key_value, MQTT_TOPIC_L, "e1=%s %s&", response.e1.value, response.e1.unit);
257  strcat(message, key_value);
258 
259  memset(cleartext, 0, sizeof(cleartext));
260  os_strncpy(cleartext, message, sizeof(message)); // make a copy of message for later use
261  os_memset(message, 0, sizeof(message)); // ...and clear it
262 
263  // encrypt and send
264  message_l = encrypt_aes_hmac_combined(message, topic, strlen(topic), cleartext, strlen(cleartext) + 1);
265 
266  // and stop retransmission timeout timer, last data from meter
268 
269  if (mqtt_client) {
270  // if mqtt_client is initialized
271  if (message_l > 1) {
272  MQTT_Publish(mqtt_client, topic, message, message_l, 2, 0); // QoS level 2
273  }
274  }
275 
276  // change to last state - idle state
279  }
280  }
281  break;
282  }
283 }
284 
287  fifo_head = 0;
288  fifo_tail = 0;
289 
290  memset(&response, 0, sizeof(response)); // zero response before use
291 
293 
295 
296  en61107_etx_received = false;
297 
299 }
300 
301 // helper function to pass mqtt client struct from user_main.c to here
304  mqtt_client = client;
305 }
306 
310 }
311 
312 // helper function to pass received kmp_serial to user_main.c
315  return en61107_serial;
316 }
317 
318 // helper function to pass energy to user_main.c
321  char e1_kwh[EN61107_VALUE_L];
322 
323  if (strncmp(response.e1.unit, "MWh", EN61107_UNIT_L) == 0) {
324  mw_to_w_str(response.e1.value, e1_kwh);
325  }
326  return atoi(e1_kwh);
327 }
328 
329 //ICACHE_FLASH_ATTR
330 inline bool en61107_is_eod_char(uint8_t c) {
331  if (en61107_eod == 0x03) {
332  // we need to get the next char as well for this protocol
333  if (c == en61107_eod) {
334  en61107_etx_received = true;
335  return false;
336  }
337  else if (en61107_etx_received) {
338 // en61107_etx_received = false; // DEBUG: should really be here
339  return true;
340  }
341  }
342  else {
343  if (c == en61107_eod) {
344  return true;
345  }
346  else {
347  return false;
348  }
349  }
350  return false;
351 }
352 
355 #ifndef DEBUG_NO_METER
356  unsigned char c;
357  unsigned int i;
358 
359  // clear message buffer
360  i = 0;
361  while (en61107_fifo_get(&c) && (i <= EN61107_FRAME_L)) {
362  i++;
363  }
364 
367  // give the meter some time between retransmissions
370  os_timer_arm(&en61107_meter_wake_up_timer, 6000, 0); // 6 seconds for meter to wake up
371  }
372 
373 #else
374  unsigned char topic[128];
375  unsigned char cleartext[EN61107_FRAME_L];
376  unsigned char message[EN61107_FRAME_L];
377  int topic_l;
378  int message_l;
379 
380  // fake serial for testing without meter
381  en61107_serial = 4615611;
382 
383  topic_l = os_sprintf(topic, "/sample/v2/%u/%u", en61107_serial, get_unix_time());
384  message_l = os_sprintf(message, "heap=20000&t1=25.00 C&t2=15.00 C&tdif=10.00 K&flow1=0 l/h&effect1=0.0 kW&hr=0 h&v1=0.00 m3&e1=0 kWh&");
385  memset(cleartext, 0, sizeof(cleartext));
386  os_strncpy(cleartext, message, sizeof(message)); // make a copy of message for later use
387  os_memset(message, 0, sizeof(message)); // ...and clear it
388 
389  message_l = encrypt_aes_hmac_combined(message, topic, strlen(topic), cleartext, strlen(cleartext) + 1);
390 
391  if (mqtt_client) {
392  // if mqtt_client is initialized
393  if (message_l > 1) {
394  MQTT_Publish(mqtt_client, topic, message, message_l, 2, 0); // QoS level 2
395  }
396  }
397 
398 #endif
399 }
400 
403  en61107_eod = '\n';
404  en61107_etx_received = false; // DEBUG: should be in en61107_is_eod_char() but does not work
405 
406  // 300 bps
411 
412  // change to next state
414 
415  // and start retransmission timeout timer
418  os_timer_arm(&en61107_receive_timeout_timer, 3000, 0); // 3 seconds for UART_STATE_EN61107_IDENT
419 
420  strcpy(frame, "/?!\r\n");
422  uart0_tx_buffer(frame, frame_length); // send request
423 
424  // reply is 300 bps, 7e1, not 7e2 as stated in standard
425  uart_settings.baut_rate = BIT_RATE_300;
426  uart_settings.stop_bits = ONE_STOP_BIT;
427  // change uart settings after the data has been sent
431 }
432 
435  en61107_eod = 0x03;
436  en61107_etx_received = false; // DEBUG: should be in en61107_is_eod_char() but does not work
437 
438  // 300 bps
443 
444  // change to next state
446 
447  // and start retransmission timeout timer
450  os_timer_arm(&en61107_receive_timeout_timer, 8000, 0); // 8 seconds for UART_STATE_EN61107
451 
452  strcpy(frame, "\006000\r\n");
454  uart0_tx_buffer(frame, frame_length); // send request
455 
456  // reply is 300 bps, 7e1, not 7e2 as stated in standard
457  uart_settings.baut_rate = BIT_RATE_300;
458  uart_settings.stop_bits = ONE_STOP_BIT;
459  // change uart settings after the data has been sent
463 }
464 
467  en61107_eod = '\r';
468 
469  // 300 bps
474 
475  // change to next state
477 
478  // and start retransmission timeout timer
481  os_timer_arm(&en61107_receive_timeout_timer, 3000, 0); // 3 seconds for UART_STATE_STANDARD_DATA_1
482 
483  strcpy(frame, "/#1\r");
485  uart0_tx_buffer(frame, frame_length); // send request
486 
487  // reply is 1200 bps, 7e1, not 7e2 as stated in standard
488  uart_settings.baut_rate = BIT_RATE_1200;
489  uart_settings.stop_bits = ONE_STOP_BIT;
490  // change uart settings after the data has been sent
494 }
495 
498  en61107_eod = '\r';
499 
500  // 300 bps
505 
506  // change to next state
508 
509  // and start retransmission timeout timer
512  os_timer_arm(&en61107_receive_timeout_timer, 3000, 0); // 3 seconds for UART_STATE_STANDARD_DATA_2
513 
514  strcpy(frame, "/#2\r");
516  uart0_tx_buffer(frame, frame_length); // send request
517 
518  // reply is 1200 bps, 7e1, not 7e2 as stated in standard
519  uart_settings.baut_rate = BIT_RATE_1200;
520  uart_settings.stop_bits = ONE_STOP_BIT;
521  // change uart settings after the data has been sent
525 }
526 
527 #ifndef MC_66B
530  en61107_eod = '\r';
531 
532  // 300 bps
537 
538  // change to next state
540 
541  // and start retransmission timeout timer
544  os_timer_arm(&en61107_receive_timeout_timer, 3200, 0); // 3.2 seconds for UART_STATE_INST_VALUES
545 
546  strcpy(frame, "/#C");
548  uart0_tx_buffer(frame, frame_length); // send request
549 
550  // reply is 1200 bps, 7e1, not 7e2 as stated in standard
551  uart_settings.baut_rate = BIT_RATE_1200;
552  uart_settings.stop_bits = ONE_STOP_BIT;
553  // change uart settings after the data has been sent
557 }
558 #endif
559 
560 
561 // fifo
563 unsigned int en61107_fifo_in_use() {
564  return fifo_head - fifo_tail;
565 }
566 
567 //ICACHE_FLASH_ATTR
568 inline unsigned char en61107_fifo_put(unsigned char c) {
569  if (en61107_fifo_in_use() != QUEUE_SIZE) {
571  // wrap
572  if (fifo_head == QUEUE_SIZE) {
573  fifo_head = 0;
574  }
575  return 1;
576  }
577  else {
578  return 0;
579  }
580 }
581 
583 unsigned char en61107_fifo_get(unsigned char *c) {
584  if (en61107_fifo_in_use() != 0) {
585  *c = fifo_buffer[fifo_tail++ % QUEUE_SIZE];
586  // wrap
587  if (fifo_tail == QUEUE_SIZE) {
588  fifo_tail = 0;
589  }
590  return 1;
591  }
592  else {
593  return 0;
594  }
595 }
596 
598 unsigned char en61107_fifo_snoop(unsigned char *c, unsigned int pos) {
599  if (en61107_fifo_in_use() > (pos)) {
600  *c = fifo_buffer[(fifo_tail + pos) % QUEUE_SIZE];
601  return 1;
602  }
603  else {
604  return 0;
605  }
606 }
607 
#define os_sprintf
Definition: osapi.h:54
en61107_response_register_t effect1
Definition: en61107.h:38
unsigned int frame_length
bool meter_is_ready_cb_called
en61107_response_register_t hr
Definition: en61107.h:39
en61107_response_register_t e1
Definition: en61107.h:41
ICACHE_FLASH_ATTR uint32_t get_unix_time(void)
Definition: unix_time.c:55
Definition: uart.h:57
void ICACHE_FLASH_ATTR uart_set_parity(uint8_t uart_no, UartParityMode Parity_mode)
Definition: uart.c:327
#define MQTT_TOPIC_L
Definition: user_config.h:25
#define os_timer_t
Definition: os_type.h:34
ICACHE_FLASH_ATTR unsigned char en61107_fifo_get(unsigned char *c)
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
char frame[EN61107_FRAME_L]
ICACHE_FLASH_ATTR bool parse_mc66cde_standard_data_1_frame(en61107_response_t *response, char *frame, unsigned int frame_length)
Definition: en61107.c:172
ICACHE_FLASH_ATTR void en61107_uart_send_inst_values()
uint32 system_get_free_heap_size(void)
#define QUEUE_SIZE
#define NULL
Definition: def.h:47
en61107_response_t response
#define strcat(a, b)
Definition: platform.h:23
ICACHE_FLASH_ATTR void en61107_uart_send_standard_data_2()
ICACHE_FLASH_ATTR bool parse_mc66cde_inst_values_frame(en61107_response_t *response, char *frame, unsigned int frame_length)
Definition: en61107.c:269
UartBautRate baut_rate
Definition: uart.h:135
bool en61107_etx_received
ICACHE_FLASH_ATTR void en61107_request_send()
en61107_response_register_t flow1
Definition: en61107.h:37
#define ICACHE_FLASH_ATTR
Definition: c_types.h:99
char value[EN61107_VALUE_L]
Definition: en61107.h:13
ICACHE_FLASH_ATTR void en61107_uart_send_en61107_ident()
#define os_timer_func_t
Definition: os_type.h:35
ICACHE_FLASH_ATTR void en61107_meter_wake_up_timer_func(void *arg)
volatile unsigned int fifo_head
ICACHE_FLASH_ATTR void en61107_receive_timeout_timer_func(void *arg)
#define EN61107_UNIT_L
Definition: en61107.h:7
ICACHE_FLASH_ATTR void en61107_set_mqtt_client(MQTT_Client *client)
static os_timer_t en61107_receive_timeout_timer
#define os_printf
Definition: osapi.h:62
#define os_timer_setfn
Definition: osapi.h:52
#define EN61107_VALUE_L
Definition: en61107.h:8
#define os_event_t
Definition: os_type.h:32
ICACHE_FLASH_ATTR void en61107_request_init()
bool system_os_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen)
volatile unsigned char fifo_buffer[QUEUE_SIZE]
ICACHE_FLASH_ATTR unsigned char en61107_fifo_snoop(unsigned char *c, unsigned int pos)
static ICACHE_FLASH_ATTR void en61107_received_task(os_event_t *events)
ICACHE_FLASH_ATTR uint32_t en61107_get_received_serial()
#define os_memset
Definition: osapi.h:38
#define os_strncpy
Definition: osapi.h:45
static os_timer_t en61107_meter_wake_up_timer
en61107_response_register_t t3
Definition: en61107.h:34
en61107_response_register_t t1
Definition: en61107.h:31
en61107_response_register_t v1
Definition: en61107.h:40
os_event_t en61107_received_task_queue[en61107_received_task_queue_length]
en61107_response_register_t t2
Definition: en61107.h:32
void(* meter_is_ready_cb)(void)
ICACHE_FLASH_ATTR bool parse_mc66cde_standard_data_2_frame(en61107_response_t *response, char *frame, unsigned int frame_length)
Definition: en61107.c:214
ICACHE_FLASH_ATTR unsigned int en61107_fifo_in_use()
char unit[EN61107_UNIT_L]
Definition: en61107.h:14
en61107_response_register_t tdif
Definition: en61107.h:36
#define strncmp(a, b, c)
Definition: platform.h:18
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
unsigned char en61107_fifo_put(unsigned char c)
ICACHE_FLASH_ATTR void led_blink(void)
Definition: led.c:99
int8_t en61107_request_num
void ICACHE_FLASH_ATTR uart_set_stop_bits(uint8_t uart_no, UartStopBitsNum bit_num)
Definition: uart.c:316
unsigned int uint32
Definition: c_types.h:54
ICACHE_FLASH_ATTR unsigned int en61107_get_received_energy_kwh()
void ICACHE_FLASH_ATTR uart_set_word_length(uint8_t uart_no, UartBitsNum4Char len)
Definition: uart.c:311
ICACHE_FLASH_ATTR int tfp_snprintf(char *str, size_t size, const char *format,...)
Definition: tinyprintf.c:480
uint32_t en61107_serial
en61107_meter_program_t meter_program
Definition: en61107.h:30
static MQTT_Client * mqtt_client
bool en61107_serial_set
en61107_uart_state_t
Definition: en61107.h:44
ICACHE_FLASH_ATTR void en61107_register_meter_is_ready_cb(meter_is_ready_cb cb)
void ICACHE_FLASH_ATTR uart_set_baudrate(uint8 uart_no, uint32_t baud_rate)
Definition: uart.c:337
UartStopBitsNum stop_bits
Definition: uart.h:139
#define strcpy(a, b)
Definition: platform.h:15
ICACHE_FLASH_ATTR bool parse_en61107_frame(en61107_response_t *response, char *frame, unsigned int frame_length)
Definition: en61107.c:11
bool en61107_is_eod_char(uint8_t c)
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 UART0
Definition: uart.h:37
#define os_timer_arm(a, b, c)
Definition: osapi.h:50
ICACHE_FLASH_ATTR void mw_to_w_str(char *mw, char *w)
Definition: utils.c:105
ICACHE_FLASH_ATTR void en61107_delayed_uart_change_setting_timer_func(UartDevice *uart_settings)
ICACHE_FLASH_ATTR void en61107_uart_send_standard_data_1()
volatile uint8_t en61107_eod
UartDevice uart_settings
ICACHE_FLASH_ATTR void en61107_uart_send_en61107()
#define EN61107_FRAME_L
Definition: en61107.h:4
static os_timer_t en61107_delayed_uart_change_setting_timer
volatile unsigned int fifo_tail
#define en61107_received_task_queue_length
#define strlen(a)
Definition: platform.h:25
char customer_no[EN61107_CUSTOMER_NO_L]
Definition: en61107.h:28
volatile en61107_uart_state_t en61107_uart_state
meter_is_ready_cb en61107_meter_is_ready_cb
uint32_t current_unix_time
Definition: unix_time.c:9
#define en61107_received_task_prio