1 /* Maverick ET-73x BBQ Sensor
3 * Copyright © 2016 gismo2004
4 * Credits to all users of mentioned forum below!
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 /**The thermometer transmits 4 identical messages every 12 seconds at 433.92 MHz,
13 * using on-off keying and 2000bps Manchester encoding,
14 * with each message preceded by 8 carrier pulses 230uS wide and 5ms apart.
16 * Each message consists of 26 nibbles (104 bits total),
17 * which can each only have the value of 0x5, 0x6, 0x9, or 0xA.
18 * For nibble 24 some devices are sending 0x1 or 0x2
20 * Assuming MSB first and falling edge = 1.
22 * quarternary conversion of message needed:
28 * Message looks like this:
29 * a = Header (0xAA9995)
30 * b = device state (2=default; 7=init)
31 * c = temp1 (need to substract 532)
32 * d = temp2 (need to substract 532)
33 * e = checksum (the checksum gets renewed on a device reset, and represents a kind of session_id)
35 * nibble: 0 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
36 * msg: a a a a a a b b c c c c c d d d d d e e e e e e e e
38 * further information can be found here: https://forums.adafruit.com/viewtopic.php?f=8&t=25414
45 #define MAV_MESSAGE_LENGTH 104
46 #define TEMPERATURE_START_POSITION_S1 8
47 #define TEMPERATURE_START_POSITION_S2 13
48 #define TEMPERATURE_NIBBLE_COUNT 5
51 //here we extract bitbuffer values, for easy data handling
52 static void convert_bitbuffer(bitbuffer_t *bitbuffer, unsigned int *msg_converted, char *msg_hex_combined) {
53 //quarternary convertion
54 unsigned int quart_convert[16] ={0,0,0,0,0,0,1,0,0,2,3,0,0,0,0,0};
56 for(i = 0; i < 13; i++) {
58 sprintf(temp, "%02x", bitbuffer->bb[0][i]);
59 msg_hex_combined[i*2] = temp[0];
60 msg_hex_combined[i*2+1] = temp[1];
62 msg_converted[i*2] = quart_convert[bitbuffer->bb[0][i] >> 4];
63 msg_converted[i*2+1] = quart_convert[bitbuffer->bb[0][i] & 0xF];
66 msg_hex_combined[26]='\0';
68 fprintf(stderr, "final hex string: %s\n",msg_hex_combined);
69 fprintf(stderr, "final converted message: ");
70 for(i = 0; i <= 25; i++) fprintf(stderr, "%d",msg_converted[i]);
71 fprintf(stderr, "\n");
75 static float get_temperature(unsigned int *msg_converted, unsigned int temp_start_index){
77 float temp_c = -532.0;
80 for(i=0; i < TEMPERATURE_NIBBLE_COUNT; i++) {
81 temp_c += msg_converted[temp_start_index+i] * (1<<(2*(4-i)));
88 //changes when thermometer reset button is pushed or powered on.
89 static char* get_status(unsigned int *msg_converted) {
91 char* retval = "unknown";
93 //nibble 6 - 7 used for status
94 stat += msg_converted[6] * (1<<(2*(1)));
95 stat += msg_converted[7] * (1<<(2*(0)));
104 fprintf(stderr, "device status: \"%s\" (%d)\n", retval, stat);
110 static uint32_t checksum_data(unsigned int *msg_converted) {
111 int32_t checksum = 0;
114 //nibble 6 - 17 used for checksum
115 for(i=0; i<=11; i++) {
116 checksum |= msg_converted[6+i] << (22 - 2*i);
120 fprintf(stderr, "checksum data = %x\n", checksum);
126 static uint32_t checksum_received(unsigned int *msg_converted, char *msg_hex_combined) {
127 uint32_t checksum = 0;
130 //nibble 18 - 25 checksum info from device
131 for(i=0; i<=7; i++) {
132 checksum |= msg_converted[18+i] << (14 - 2*i);
135 if(msg_hex_combined[24]=='1' || msg_hex_combined[24]=='2') {
136 checksum |= (msg_converted[25]&1) << 3;
137 checksum |= (msg_converted[25]&2) << 1;
139 if(msg_hex_combined[24]=='2')
143 checksum |= msg_converted[24] << 2;
144 checksum |= msg_converted[25];
148 fprintf(stderr, "checksum received= %x\n", checksum);
153 static uint16_t shiftreg(uint16_t currentValue) {
154 uint8_t msb = (currentValue >> 15) & 1;
157 // Toggle pattern for feedback bits
158 // Toggle, if MSB is 1
160 currentValue ^= 0x1021;
165 static uint16_t calculate_checksum(uint32_t data) {
166 //initial value of linear feedback shift register
167 uint16_t mask = 0x3331;
170 for(i = 0; i < 24; ++i) {
171 //data bit at current position is "1"
173 if((data >> i) & 0x01)
176 mask = shiftreg(mask);
181 static int maverick_et73x_callback(bitbuffer_t *bitbuffer) {
183 char time_str[LOCAL_TIME_BUFLEN];
185 char msg_hex_combined[27];
186 unsigned int msg_converted[26];
188 //we need an inverted bitbuffer
189 bitbuffer_invert(bitbuffer);
191 if(bitbuffer->num_rows != 1)
194 //check correct data length
195 if(bitbuffer->bits_per_row[0] != MAV_MESSAGE_LENGTH)
198 //check for correct header (0xAA9995)
199 if((bitbuffer->bb[0][0] != 0xAA || bitbuffer->bb[0][0] != 0xaa ) || bitbuffer->bb[0][1] != 0x99 || bitbuffer->bb[0][2] != 0x95)
202 //convert hex values into quardinary values
203 convert_bitbuffer(bitbuffer, msg_converted, msg_hex_combined);
205 //checksum is used to represent a session. This means, we get a new session_id if a reset or battery exchange is done.
206 session_id = (calculate_checksum(checksum_data(msg_converted)) & 0xffff) ^ checksum_received(msg_converted, msg_hex_combined);
209 fprintf(stderr, "checksum xor: %x\n", session_id);
211 local_time_str(0, time_str);
213 data = data_make("time", "", DATA_STRING, time_str,
214 "brand", "", DATA_STRING, "Maverick",
215 "model", "", DATA_STRING, "ET-732/ET-733",
216 "id", "Session_ID", DATA_INT, session_id,
217 "status", "Status", DATA_STRING, get_status(msg_converted),
218 "temperature_C1", "TemperatureSensor1", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_temperature(msg_converted,TEMPERATURE_START_POSITION_S1),
219 "temperature_C2", "TemperatureSensor2", DATA_FORMAT, "%.02f C", DATA_DOUBLE, get_temperature(msg_converted,TEMPERATURE_START_POSITION_S2),
220 "crc", "", DATA_STRING, "ok",
222 data_acquired_handler(data);
224 return bitbuffer->num_rows;
227 static char *output_fields[] = {
240 r_device maverick_et73x = {
241 .name = "Maverick ET-732/733 BBQ Sensor",
242 .modulation = OOK_PULSE_MANCHESTER_ZEROBIT,
244 .long_limit = 0, //not used
246 .json_callback = &maverick_et73x_callback,
249 .fields = output_fields