Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / devices / maverick_et73x.c
1 /* Maverick ET-73x BBQ Sensor
2  *
3  * Copyright © 2016 gismo2004
4  * Credits to all users of mentioned forum below!
5  *
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.
10 */
11
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.
15  *
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
19  *
20  * Assuming MSB first and falling edge = 1.
21  *
22  * quarternary conversion of message needed:
23  * 0x05 = 0
24  * 0x06 = 1
25  * 0x09 = 2
26  * 0x0A = 3
27  *
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)
34  *
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
37  *
38  * further information can be found here: https://forums.adafruit.com/viewtopic.php?f=8&t=25414
39 **/
40
41
42 #include "rtl_433.h"
43 #include "util.h"
44
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
49
50
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};
55     int i;
56     for(i = 0; i < 13; i++) {
57         char temp[2];
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];
61         
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];
64     }
65
66     msg_hex_combined[26]='\0';
67     if(debug_output) {
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");
72     }
73 }
74
75 static float get_temperature(unsigned int *msg_converted, unsigned int temp_start_index){
76     //default offset
77     float temp_c = -532.0;
78     int i;
79
80     for(i=0; i < TEMPERATURE_NIBBLE_COUNT; i++) {
81         temp_c += msg_converted[temp_start_index+i] * (1<<(2*(4-i)));
82     }
83
84     return temp_c;
85 }
86
87
88 //changes when thermometer reset button is pushed or powered on.
89 static char* get_status(unsigned int *msg_converted) {
90     int stat = 0;
91     char* retval = "unknown";
92     
93     //nibble 6 - 7 used for status
94     stat += msg_converted[6] * (1<<(2*(1)));
95     stat += msg_converted[7] * (1<<(2*(0)));
96
97     if(stat == 2)
98         retval = "default";
99
100     if(stat == 7)
101         retval = "init";
102
103     if(debug_output)
104         fprintf(stderr, "device status: \"%s\" (%d)\n", retval, stat);
105     
106     return retval;
107 }
108
109
110 static uint32_t checksum_data(unsigned int *msg_converted) {
111     int32_t checksum = 0;
112     int i;
113
114     //nibble 6 - 17 used for checksum
115     for(i=0; i<=11; i++) {
116         checksum |= msg_converted[6+i] << (22 - 2*i);
117     }
118
119     if(debug_output)
120         fprintf(stderr, "checksum data = %x\n", checksum);
121
122     return checksum;
123 }
124
125
126 static uint32_t checksum_received(unsigned int *msg_converted, char *msg_hex_combined) {
127     uint32_t checksum = 0;
128     int i;
129     
130     //nibble 18 - 25 checksum info from device
131     for(i=0; i<=7; i++) {
132          checksum |= msg_converted[18+i] << (14 - 2*i);
133     }
134
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;
138
139         if(msg_hex_combined[24]=='2')
140             checksum |= 0x02;
141     }
142     else {
143         checksum |= msg_converted[24] << 2;
144         checksum |= msg_converted[25];
145     }
146
147     if(debug_output)
148         fprintf(stderr, "checksum received= %x\n", checksum);
149
150     return checksum;
151 }
152
153 static uint16_t shiftreg(uint16_t currentValue) {
154     uint8_t msb = (currentValue >> 15) & 1;
155     currentValue <<= 1;
156     
157     // Toggle pattern for feedback bits
158     // Toggle, if MSB is 1
159     if (msb == 1)
160         currentValue ^= 0x1021;
161     
162     return currentValue;
163 }
164
165 static uint16_t calculate_checksum(uint32_t data) {
166     //initial value of linear feedback shift register
167     uint16_t mask = 0x3331; 
168     uint16_t csum = 0x0;
169     int i;
170     for(i = 0; i < 24; ++i) {
171         //data bit at current position is "1"
172         //do XOR with mask
173         if((data >> i) & 0x01)
174             csum ^= mask;
175         
176         mask = shiftreg(mask);
177     }
178     return csum;
179 }
180
181 static int maverick_et73x_callback(bitbuffer_t *bitbuffer) {
182     data_t *data;
183     char time_str[LOCAL_TIME_BUFLEN];
184     int32_t session_id;
185     char msg_hex_combined[27];
186     unsigned int msg_converted[26];
187
188     //we need an inverted bitbuffer
189     bitbuffer_invert(bitbuffer);
190
191     if(bitbuffer->num_rows != 1)
192         return 0;
193
194     //check correct data length
195     if(bitbuffer->bits_per_row[0] != MAV_MESSAGE_LENGTH)
196         return 0;
197
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)
200         return 0;
201     
202     //convert hex values into quardinary values
203     convert_bitbuffer(bitbuffer, msg_converted, msg_hex_combined);
204
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);
207     
208     if(debug_output)
209         fprintf(stderr, "checksum xor: %x\n", session_id);
210     
211     local_time_str(0, time_str);
212
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",
221                      NULL);
222     data_acquired_handler(data);
223
224     return bitbuffer->num_rows;
225 }
226
227 static char *output_fields[] = {
228     "time",
229     "brand"
230     "model"
231     "id"
232     "status",
233     "temperature_C1",
234     "temperature_C2",
235     "crc",
236     NULL
237 };
238
239
240 r_device maverick_et73x = {
241     .name           = "Maverick ET-732/733 BBQ Sensor",
242     .modulation     = OOK_PULSE_MANCHESTER_ZEROBIT,
243     .short_limit    = 230,
244     .long_limit     = 0, //not used
245     .reset_limit    = 4000,
246     .json_callback  = &maverick_et73x_callback,
247     .disabled       = 0,
248     .demod_arg      = 0,
249     .fields         = output_fields
250 };