MeterLogger
kmp.c
Go to the documentation of this file.
1 //
2 // KMP.m
3 // MeterLogger
4 //
5 // Created by stoffer on 28/05/14.
6 // Copyright (c) 2014 Johannes Gaardsted Jørgensen <johannesgj@gmail.com> + Kristoffer Ek <stoffer@skulp.net>. All rights reserved.
7 // This program is distributed under the terms of the GNU General Public License
8 
9 /*
10  self.registerIDTable = @{@1003: @"Current date (YYMMDD)",
11  @60: @"Heat energy", // @"Energy register 1: Heat energy"
12  @94: @"Energy register 2: Control energy",
13  @63: @"Energy register 3: Cooling energy",
14  @61: @"Energy register 4: Flow energy",
15  @62: @"Energy register 5: Return flow energy",
16  @95: @"Energy register 6: Tap water energy",
17  @96: @"Energy register 7: Heat energy Y",
18  @97: @"Energy register 8: [m3 • T1]",
19  @110: @"Energy register 9: [m3 • T2]",
20  @64: @"Tariff register 2",
21  @65: @"Tariff register 3",
22  @68: @"Volume register V1",
23  @69: @"Volume register V2",
24  @84: @"Input register VA",
25  @85: @"Input register VB",
26  @72: @"Mass register", // @"Mass register V1"
27  @73: @"Mass register V2",
28  @1004: @"Operational hour counter",
29  @113: @"Info-event counter",
30  @1002: @"Current time (hhmmss)",
31  @99: @"Infocode register, current",
32  @86: @"Current flow temperature",
33  @87: @"Current return flow temperature",
34  @88: @"Current temperature T3",
35  @122: @"Current temperature T4",
36  @89: @"Current temperature difference",
37  @91: @"Pressure in flow",
38  @92: @"Pressure in return flow",
39  @74: @"Current flow in flow",
40  @75: @"Current flow in return flow",
41  @80: @"Current power calculated on the basis of V1-T1-T2",
42  @123: @"Date for max. this year",
43  @124: @"Max. value this year",
44  @125: @"Date for min. this year",
45  @126: @"Min. value this year",
46  @127: @"Date for max. this year",
47  @128: @"Max. value this year",
48  @129: @"Date for min. this year",
49  @130: @"Min. value this year",
50  @138: @"Date for max. this year",
51  @139: @"Max. value this year",
52  @140: @"Date for min. this month",
53  @141: @"Min. value this month",
54  @142: @"Date for max. this month",
55  @143: @"Max. value this month",
56  @144: @"Date for min. this month",
57  @145: @"Min. value this month",
58  @146: @"Year-to-date average for T1",
59  @147: @"Year-to-date average for T2",
60  @149: @"Month-to-date average for T1",
61  @150: @"Month-to-date average for T2",
62  @66: @"Tariff limit 2",
63  @67: @"Tariff limit 3",
64  @98: @"Target date (reading date)",
65  @152: @"Program no. ABCCCCCC",
66  @153: @"Config no. DDDEE",
67  @168: @"Config no. FFGGMN",
68  @1001: @"Serial no.", // @"Serial no. (unique number for each meter)"
69  @112: @"Customer number (8 most important digits)",
70  @1010: @"Customer number (8 less important digits)",
71  @114: @"Meter no. for VA",
72  @104: @"Meter no. for VB",
73  @1005: @"Software edition",
74  @154: @"Software check sum",
75  @155: @"High-resolution energy register for testing purposes",
76  @157: @"ID number for top module ( only mc 601 )",
77  @158: @"ID number for base module",
78  @175: @"Error hour counter",
79  @234: @"Liter/imp value for input A",
80  @235: @"Liter/imp value for input B"};
81  */
82 
83 #include <esp8266.h>
84 #include <utils.h>
85 
86 #include "tinyprintf.h"
87 #include "kmp.h"
88 
89 #define KMP_START_BYTE_IDX 0
90 #define KMP_DST_IDX 1
91 #define KMP_CID_IDX 2
92 #define KMP_DATA_IDX 3
93 
94 unsigned char *kmp_frame;
95 unsigned int kmp_frame_length;
96 unsigned int kmp_data_length;
97 
98 // kmp response struct
100 
101 
103 unsigned int kmp_get_type(unsigned char *frame) {
104  uint16_t crc16;
105 
106  // clear frame
107  kmp_frame = frame;
108  memset(kmp_frame, 0x00, KMP_FRAME_L);
109  kmp_frame_length = 0;
110  kmp_data_length = 0;
111 
112  // start byte
114 
115  // data
116  kmp_frame[KMP_DST_IDX] = 0x3f;
117  kmp_frame[KMP_CID_IDX] = 0x01;
118  kmp_frame_length = 3;
119  kmp_data_length = 2;
120 
121  // put crc 16 in frame
122  crc16 = kmp_crc16();
123  kmp_frame[kmp_frame_length] = crc16 >> 8; // high bits of crc16
124  kmp_frame[kmp_frame_length + 1] = crc16 & 0xff; // low bits
125  kmp_frame_length += 2;
126  kmp_data_length +=2;
127 
128  // stuff data
129  kmp_byte_stuff();
130 
131  // stop byte
132  kmp_frame[kmp_frame_length] = 0x0d;
134 
135  return kmp_frame_length;
136 }
137 
139 unsigned int kmp_get_serial(unsigned char *frame) {
140  uint16_t crc16;
141 
142  // clear frame
143  kmp_frame = frame;
144  memset(kmp_frame, 0x00, KMP_FRAME_L);
145  kmp_frame_length = 0;
146  kmp_data_length = 0;
147 
148  // start byte
150 
151  // data
152  kmp_frame[KMP_DST_IDX] = 0x3f;
153  kmp_frame[KMP_CID_IDX] = 0x02;
154  kmp_frame_length = 3;
155  kmp_data_length = 2;
156 
157  // put crc 16 in frame
158  crc16 = kmp_crc16();
159  kmp_frame[kmp_frame_length] = crc16 >> 8; // high bits of crc16
160  kmp_frame[kmp_frame_length + 1] = crc16 & 0xff; // low bits
161  kmp_frame_length += 2;
162  kmp_data_length += 2;
163 
164  // stuff data
165  kmp_byte_stuff();
166 
167  // stop byte
168  kmp_frame[kmp_frame_length] = 0x0d;
170 
171  return kmp_frame_length;
172 }
173 
175 unsigned int kmp_set_clock(unsigned char *frame, uint64_t unix_time) {
176  // DEBUG: not implemented
177  return 0;
178 }
179 
181 unsigned int kmp_get_register(unsigned char *frame, uint16_t *register_list, uint16_t register_list_length) {
182  unsigned int i;
183  uint8_t register_high;
184  uint8_t register_low;
185  uint16_t crc16;
186 
187  if (register_list_length > 8) {
188  // maximal number of 8 registers can be read with one request, last ones ommitted
189  register_list_length = 8;
190  }
191 
192  // clear frame
193  kmp_frame = frame;
194  memset(kmp_frame, 0x00, KMP_FRAME_L);
195  kmp_frame_length = 0;
196  kmp_data_length = 0;
197 
198  // start byte
200 
201  // data
202  kmp_frame[KMP_DST_IDX] = 0x3f;
203  kmp_frame[KMP_CID_IDX] = 0x10;
204 
205  // number of registers
206  kmp_frame[KMP_DATA_IDX] = register_list_length;
207  kmp_frame_length = 4;
208  kmp_data_length = 3;
209 
210  // registers
211  for (i = 0; i < register_list_length; i++) {
212  register_high = (uint8_t)(register_list[i] >> 8);
213  register_low = (uint8_t)(register_list[i] & 0xff);
214  kmp_frame[KMP_DATA_IDX + 2 * i + 1] = register_high;
215  kmp_frame[KMP_DATA_IDX + 2 * i + 2] = register_low;
216  }
217  kmp_frame_length += 2 * i;
218  kmp_data_length += 2 * i;
219 
220  // put crc 16 in frame
221  crc16 = kmp_crc16();
222  kmp_frame[kmp_frame_length] = crc16 >> 8; // high bits of crc16
223  kmp_frame[kmp_frame_length + 1] = crc16 & 0xff; // low bits
224  kmp_frame_length += 2;
225 
226  // stuff data
227  kmp_byte_stuff();
228 
229  // stop byte
230  kmp_frame[kmp_frame_length] = 0x0d;
232 
233  return kmp_frame_length;
234 }
235 
236 #pragma mark - KMP Decoder
237 
239 int kmp_decode_frame(unsigned char *frame, unsigned char frame_length, kmp_response_t *response) {
240  uint16_t kmp_frame_crc16;
241  uint16_t crc16;
242  unsigned int i;
243  unsigned int kmp_register_idx;
244 
245  kmp_frame = frame;
247 
248  kmp_response = response;
249  memset(kmp_response, 0x00, sizeof(kmp_response_t));
250 
251  if (kmp_frame_length == 0) {
252  return 0;
253  }
254 
255  if (kmp_frame_length == 1) {
256  // no data returned from Kamstrup meter
257  if (kmp_frame[kmp_frame_length - 1] == 0x06) {
258  return 0;
259  }
260  else {
261  // Kamstrup: device said: no valid reply from kamstrup meter
262  return 0;
263  }
264  }
265 
266  if (kmp_frame_length < 6) {
267  // valid packets are at least 1) start byte, 2) dst, 3) cid, 4) crc high, 5) crc low and 6) stop byte
268  return 0;
269  }
270 
271  if (kmp_frame[kmp_frame_length - 1] == 0x0d) {
272  // end of data - get params from frame
273  // no need to set start and stop bytes
274 
275  // unstuff data
277 
278  // calculate crc
279  kmp_data_length = kmp_frame_length - 4; // not included 1) start_byte, 2) crc high, 3) crc low and 4) stop byte
280  crc16 = kmp_crc16();
281 
282  // get crc from frame
283  kmp_frame_crc16 = (kmp_frame[kmp_frame_length - 2] | kmp_frame[kmp_frame_length - 3] << 8);
284  if (kmp_frame_crc16 == crc16) {
285  //crc ok
286  }
287  else {
288  //crc error
289  return -1;
290  }
291 
292  // decode application layer
293 
294  if (kmp_frame[KMP_CID_IDX] == 0x01) {
295  // kmp_get_type
296  kmp_response->kmp_response_meter_type = (kmp_frame[KMP_DATA_IDX + 0] << 8) + kmp_frame[KMP_DATA_IDX + 1];
297  kmp_response->kmp_response_sw_revision = (kmp_frame[KMP_DATA_IDX + 2] << 8) + kmp_frame[KMP_DATA_IDX + 3];
298  return 1;
299  }
300  else if (kmp_frame[KMP_CID_IDX] == 0x02) {
301  // kmp_get_serial
302  kmp_response->kmp_response_serial = (kmp_frame[KMP_DATA_IDX + 0] << 24) + (kmp_frame[KMP_DATA_IDX + 1] << 16) + (kmp_frame[KMP_DATA_IDX + 2] << 8) + kmp_frame[KMP_DATA_IDX + 3];
303  return 1;
304  }
305  else if (kmp_frame[KMP_CID_IDX] == 0x10) {
306  // kmp_get_register
307  if (kmp_data_length > 2) {
308  for (i = 0; i < ((kmp_data_length - 2) / 9); i++) { // 9 bytes per register. BUG here if length != 4?
309  kmp_register_idx = 9 * i + KMP_DATA_IDX;
310 
311  // rid
312  kmp_response->kmp_response_register_list[i].rid = (kmp_frame[kmp_register_idx + 0] << 8) + kmp_frame[kmp_register_idx + 1];
313 
314  // unit
315  kmp_response->kmp_response_register_list[i].unit = kmp_frame[kmp_register_idx + 2];
316 
317  // length
318  kmp_response->kmp_response_register_list[i].length = kmp_frame[kmp_register_idx + 3];
319 
320  // si_ex
321  kmp_response->kmp_response_register_list[i].si_ex = kmp_frame[kmp_register_idx + 4];
322 
323  // value
324  kmp_response->kmp_response_register_list[i].value = (kmp_frame[kmp_register_idx + 5] << 24) + (kmp_frame[kmp_register_idx + 6] << 16) + (kmp_frame[kmp_register_idx + 7] << 8) + kmp_frame[kmp_register_idx + 8];
325  }
326  return 1;
327  }
328  else {
329  // No registers in reply
330  return 0;
331  }
332  }
333  else if (kmp_frame[KMP_CID_IDX] == 0x11) {
334  // kmp_put_register
335  //range = NSMakeRange(2, data.length - 2);
336  //NSLog(@"%@", [data subdataWithRange:range]);
337  return 1;
338  }
339 //
340  }
341  else if (kmp_frame[kmp_frame_length - 1] == 0x06) {
342  // kmp_set_clock no CRC
343  return 0;
344  }
345 
346  return 0;
347 }
348 
349 #pragma mark - Helper methods
350 
352 uint16_t kmp_crc16() {
353  //uint16_t crc16;
354  //int i;
355 
356  //crc16 = 0;
357  //for (i = KMP_DST_IDX; i < (kmp_data_length + KMP_DST_IDX); i++) {
358  // crc16 = (crc16 << 8) ^ kmp_crc16_table[((crc16 >> 8) ^ kmp_frame[i]) & 0x00FF];
359  //}
360  //return crc16;
361 
362  return ccit_crc16(0x0000, kmp_frame + KMP_DST_IDX, kmp_data_length);
363 }
364 
366 double kmp_value_to_double(int32_t value, uint8_t si_ex) {
367  double result;
368  int8_t sign_i = (si_ex & 0x80) >> 7;
369  int8_t sign_e = (si_ex & 0x40) >> 6;
370  int8_t exponent = (si_ex & 0x3f);
371 
372  // powf(-1, (double)sign_i) * value * powf(10, (powf(-1, (double)sign_e) * exponent));
373  if (sign_i) {
374  if (sign_e) {
375  result = -1 * value / int_pow(10, exponent);
376  }
377  else {
378  result = -1 * value * int_pow(10, exponent);
379  }
380  }
381  else {
382  if (sign_e) {
383  result = value / (double)int_pow(10, exponent);
384  }
385  else {
386  result = value * (double)int_pow(10, exponent);
387  }
388  }
389 
390  return result;
391 }
392 
394 void kmp_value_to_string(int32_t value, uint8_t si_ex, unsigned char *value_string) {
395  double result;
396  uint32_t result_int, result_frac;
397  int8_t sign_i = (si_ex & 0x80) >> 7;
398  int8_t sign_e = (si_ex & 0x40) >> 6;
399  int8_t exponent = (si_ex & 0x3f);
400  uint32_t factor;
401  unsigned char leading_zeroes[16];
402  unsigned int i;
403 
404  factor = int_pow(10, exponent);
405  if (sign_i) {
406  if (sign_e) {
407  result = value / int_pow(10, exponent);
408  result_int = (int32_t)result;
409  result_frac = value - result_int * int_pow(10, exponent);
410 
411  // prepare decimal string
412  strcpy(leading_zeroes, "");
413  for (i = 0; i < (exponent - decimal_number_length(result_frac)); i++) {
414  strcat(leading_zeroes, "0");
415  }
416  tfp_snprintf(value_string, 23 + i, "-%u.%s%u", result_int, leading_zeroes, result_frac);
417  }
418  else {
419  tfp_snprintf(value_string, 12, "-%u", value * factor);
420  }
421  }
422  else {
423  if (sign_e) {
424  result = value / int_pow(10, exponent);
425  result_int = (int32_t)result;
426  result_frac = value - result_int * int_pow(10, exponent);
427 
428  // prepare decimal string
429  strcpy(leading_zeroes, "");
430  for (i = 0; i < (exponent - decimal_number_length(result_frac)); i++) {
431  strcat(leading_zeroes, "0");
432  }
433  tfp_snprintf(value_string, 22 + i, "%u.%s%u", result_int, leading_zeroes, result_frac);
434  }
435  else {
436  tfp_snprintf(value_string, 11, "%u", value * factor);
437  }
438  }
439 }
440 
442 void kmp_unit_to_string(uint8_t unit, unsigned char *unit_string) {
443  switch (unit) {
444  case 0x01:
445  strcpy(unit_string, "Wh");
446  break;
447  case 0x02:
448  strcpy(unit_string, "kWh");
449  break;
450  case 0x03:
451  strcpy(unit_string, "MWh");
452  break;
453  case 0x08:
454  strcpy(unit_string, "Gj");
455  break;
456  case 0x0c:
457  strcpy(unit_string, "Gcal");
458  break;
459  case 0x16:
460  strcpy(unit_string, "kW");
461  break;
462  case 0x17:
463  strcpy(unit_string, "MW");
464  break;
465  case 0x25:
466  strcpy(unit_string, "C");
467  break;
468  case 0x26:
469  strcpy(unit_string, "K");
470  break;
471  case 0x27:
472  strcpy(unit_string, "l");
473  break;
474  case 0x28:
475  strcpy(unit_string, "m3");
476  break;
477  case 0x29:
478  strcpy(unit_string, "l/h");
479  break;
480  case 0x2a:
481  strcpy(unit_string, "m3/h");
482  break;
483  case 0x2b:
484  strcpy(unit_string, "m3xC");
485  break;
486  case 0x2c:
487  strcpy(unit_string, "ton");
488  break;
489  case 0x2d:
490  strcpy(unit_string, "ton/h");
491  break;
492  case 0x2e:
493  strcpy(unit_string, "h");
494  break;
495  case 0x2f:
496  strcpy(unit_string, "clock");
497  break;
498  case 0x30:
499  strcpy(unit_string, "date1");
500  break;
501  case 0x32:
502  strcpy(unit_string, "date3");
503  break;
504  case 0x33:
505  strcpy(unit_string, "number");
506  break;
507  case 0x34:
508  strcpy(unit_string, "bar");
509  break;
510 
511  default:
512  break;
513  }
514 }
515 
516 /*
517 -(NSData *)kmpDateWithDate:(NSDate *)theDate {
518  NSCalendar* calendar = [NSCalendar currentCalendar];
519  NSDateComponents* components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:theDate];
520 
521  unsigned int year = (int)(components.year - 2000);
522  unsigned int month = (int)(components.month);
523  unsigned int day = (int)(components.day);
524 
525  NSString *dateString = [NSString stringWithFormat:@"%02d%02d%02d", year, month, day];
526  NSString *hexDate = [NSString stringWithFormat:@"%08x", dateString.intValue];
527  NSLog(@"%@", hexDate);
528 
529  NSMutableData *result = [[NSMutableData alloc] init];
530  unsigned int i;
531  for (i = 0; i < 4; i++) {
532  NSRange range = NSMakeRange(2 * i, 2);
533  NSString* hexValue = [hexDate substringWithRange:range];
534  NSScanner* scanner = [NSScanner scannerWithString:hexValue];
535  unsigned int intValue;
536  [scanner scanHexInt:&intValue];
537  unsigned char uc = (unsigned char) intValue;
538  [result appendBytes:&uc length:1];
539  }
540  return result;
541 }
542 
543 -(NSData *)kmpTimeWithDate:(NSDate *)theDate {
544  NSCalendar* calendar = [NSCalendar currentCalendar];
545  NSDateComponents* components = [calendar components:NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:theDate];
546 
547  unsigned int hour = (int)(components.hour);
548  unsigned int minute = (int)(components.minute);
549  unsigned int second = (int)(components.second);
550 
551  NSString *dateString = [NSString stringWithFormat:@"%02d%02d%02d", hour, minute, second];
552  NSString *hexDate = [NSString stringWithFormat:@"%08x", dateString.intValue];
553  NSLog(@"%@", hexDate);
554 
555  NSMutableData *result = [[NSMutableData alloc] init];
556  unsigned int i;
557  for (i = 0; i < 4; i++) {
558  NSRange range = NSMakeRange(2 * i, 2);
559  NSString* hexValue = [hexDate substringWithRange:range];
560  NSScanner* scanner = [NSScanner scannerWithString:hexValue];
561  unsigned int intValue;
562  [scanner scanHexInt:&intValue];
563  unsigned char uc = (unsigned char) intValue;
564  [result appendBytes:&uc length:1];
565  }
566  return result;
567 }
568 
569 -(NSDate *)dateWithKmpDate:(NSData *)theData {
570  return [NSDate date];
571 }
572 
573 -(NSDate *)dateWithKmpTime:(NSData *)theData {
574  return [NSDate date];
575 }
576 
577 */
578 
581  unsigned char stuffed_data[KMP_FRAME_L];
582  unsigned int i;
583  unsigned int j = 0;
584 
585  memset(stuffed_data, 0x00, KMP_FRAME_L);
586 
587  for (i = KMP_DST_IDX; i < (kmp_frame_length); i++) {
588  if ((kmp_frame[i] == 0x80) || (kmp_frame[i] == 0x40) || (kmp_frame[i] == 0x0d) || (kmp_frame[i] == 0x06) || (kmp_frame[i] == 0x1b)) {
589  stuffed_data[j++] = 0x1b;
590  stuffed_data[j++] = kmp_frame[i] ^ 0xff;
591  }
592  else {
593  stuffed_data[j++] = kmp_frame[i];
594  }
595  }
596  memcpy(kmp_frame + KMP_DST_IDX, stuffed_data, j);
598  kmp_data_length = j;
599 }
600 
603  unsigned char unstuffed_data[KMP_FRAME_L];
604  unsigned int i;
605  unsigned int j = 0;
606 
607  for (i = KMP_DST_IDX; i < kmp_frame_length; i++) {
608  if (kmp_frame[i] == 0x1b) { // byte unstuffing special char
609  unstuffed_data[j++] = kmp_frame[i + 1] ^ 0xff;
610  i++;
611  }
612  else {
613  unstuffed_data[j++] = kmp_frame[i];
614  }
615  }
616  memcpy(kmp_frame + KMP_DST_IDX, unstuffed_data, j);
617  kmp_frame_length = j + KMP_DST_IDX;
618  kmp_data_length = j;
619 }
ICACHE_FLASH_ATTR int kmp_decode_frame(unsigned char *frame, unsigned char frame_length, kmp_response_t *response)
Definition: kmp.c:239
unsigned int frame_length
#define KMP_CID_IDX
Definition: kmp.c:91
#define memset(x, a, b)
Definition: platform.h:21
char frame[EN61107_FRAME_L]
unsigned int kmp_frame_length
Definition: kmp.c:95
unsigned int kmp_response_meter_type
Definition: kmp.h:16
ICACHE_FLASH_ATTR unsigned int kmp_set_clock(unsigned char *frame, uint64_t unix_time)
Definition: kmp.c:175
en61107_response_t response
#define strcat(a, b)
Definition: platform.h:23
ICACHE_FLASH_ATTR unsigned int decimal_number_length(int n)
Definition: utils.c:191
ICACHE_FLASH_ATTR uint16_t kmp_crc16()
Definition: kmp.c:352
#define ICACHE_FLASH_ATTR
Definition: c_types.h:99
#define KMP_DST_IDX
Definition: kmp.c:90
ICACHE_FLASH_ATTR void kmp_unit_to_string(uint8_t unit, unsigned char *unit_string)
Definition: kmp.c:442
unsigned int kmp_response_sw_revision
Definition: kmp.h:17
ICACHE_FLASH_ATTR void kmp_byte_unstuff()
Definition: kmp.c:602
ICACHE_FLASH_ATTR double kmp_value_to_double(int32_t value, uint8_t si_ex)
Definition: kmp.c:366
unsigned int kmp_data_length
Definition: kmp.c:96
ICACHE_FLASH_ATTR void kmp_value_to_string(int32_t value, uint8_t si_ex, unsigned char *value_string)
Definition: kmp.c:394
ICACHE_FLASH_ATTR void kmp_byte_stuff()
Definition: kmp.c:580
ICACHE_FLASH_ATTR uint16_t ccit_crc16(uint16_t crc16, uint8_t *data_p, unsigned int length)
Definition: utils.c:47
kmp_response_register_list_t kmp_response_register_list
Definition: kmp.h:18
kmp_response_t * kmp_response
Definition: kmp.c:99
unsigned char * kmp_frame
Definition: kmp.c:94
#define KMP_DATA_IDX
Definition: kmp.c:92
#define KMP_START_BYTE_IDX
Definition: kmp.c:89
ICACHE_FLASH_ATTR int tfp_snprintf(char *str, size_t size, const char *format,...)
Definition: tinyprintf.c:480
uint16_t register_list[8]
Definition: kmp_request.c:27
#define strcpy(a, b)
Definition: platform.h:15
#define memcpy(x, a, b)
Definition: platform.h:22
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 KMP_FRAME_L
Definition: kmp.h:2
ICACHE_FLASH_ATTR int int_pow(int x, int y)
Definition: utils.c:203
ICACHE_FLASH_ATTR unsigned int kmp_get_type(unsigned char *frame)
Definition: kmp.c:103
unsigned int kmp_response_serial
Definition: kmp.h:15