Bugfixes
[rtl-433.git] / src / devices / radiohead_ask.c
1 #include "rtl_433.h"
2 #include "pulse_demod.h"
3 #include "util.h"
4
5 // Transmitter speed in bits per seconds
6 #define RH_ASK_SPEED 2000
7 #define RH_ASK_BIT_LEN (int)1e6/RH_ASK_SPEED
8
9 // Maximum message length (including the headers, byte count and FCS) we are willing to support
10 // This is pretty arbitrary
11 #define RH_ASK_MAX_PAYLOAD_LEN 67
12 #define RH_ASK_HEADER_LEN 4
13 #ifndef RH_ASK_MAX_MESSAGE_LEN
14  #define RH_ASK_MAX_MESSAGE_LEN (RH_ASK_MAX_PAYLOAD_LEN - RH_ASK_HEADER_LEN - 3)
15 #endif
16
17 uint8_t payload[RH_ASK_MAX_PAYLOAD_LEN] = {0};
18 int data_payload[RH_ASK_MAX_MESSAGE_LEN];
19
20 // Note: all tje "4to6 code" came from RadioHead source code.
21 // see: http://www.airspayce.com/mikem/arduino/RadioHead/index.html
22
23 // 4 bit to 6 bit symbol converter table
24 // Used to convert the high and low nybbles of the transmitted data
25 // into 6 bit symbols for transmission. Each 6-bit symbol has 3 1s and 3 0s 
26 // with at most 3 consecutive identical bits
27 static uint8_t symbols[] = {
28     0xd,  0xe,  0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 
29     0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34
30 };
31
32 // Convert a 6 bit encoded symbol into its 4 bit decoded equivalent
33 uint8_t symbol_6to4(uint8_t symbol)
34 {
35     uint8_t i;
36     // Linear search :-( Could have a 64 byte reverse lookup table?
37     // There is a little speedup here courtesy Ralph Doncaster:
38     // The shortcut works because bit 5 of the symbol is 1 for the last 8
39     // symbols, and it is 0 for the first 8.
40     // So we only have to search half the table
41     for (i = (symbol>>2) & 8; i < 16 ; i++){
42         if (symbol == symbols[i]) return i;
43     }
44     return 0xFF; // Not found
45 }
46
47
48 static int radiohead_ask_callback(bitbuffer_t *bitbuffer) {
49     // Get time
50     char time_str[LOCAL_TIME_BUFLEN];
51     data_t *data;
52     local_time_str(0, time_str);
53
54     uint8_t row = 0; // we are considering only first row
55     unsigned int len = bitbuffer->bits_per_row[row];
56
57     uint8_t msg_len = RH_ASK_MAX_MESSAGE_LEN;
58     unsigned int pos, nb_bytes;
59     uint8_t rxBits[2] = {0};
60
61     uint16_t crc, crc_recompute;
62     uint8_t data_len, header_to, header_from, header_id, header_flags;
63
64
65     // Looking for preamble
66     uint8_t init_pattern[] = {
67       0x55, // 8
68       0x55, // 16
69       0x55, // 24
70       0x51, // 32
71       0xcd, // 40
72     };
73     // The first 0 is ignored by the decoder, so we look only for 28 bits of "01"
74     // and not 32. Also "0x1CD" is 0xb38 (RH_ASK_START_SYMBOL) with LSBit first.
75     uint8_t init_pattern_len = 40;
76
77     pos = bitbuffer_search(bitbuffer, row, 0, init_pattern, init_pattern_len);
78     if(pos == len){
79         if(debug_output) {
80             printf("RH ASK preamble not found\n");
81         }
82         return 0;
83     }
84
85     // read "bytes" of 12 bit
86     nb_bytes=0;
87     pos += init_pattern_len;
88     for(; pos < len && nb_bytes < msg_len; pos += 12){
89         bitbuffer_extract_bytes(bitbuffer, row, pos, rxBits, /*len=*/16);
90         // ^ we should read 16 bits and not 12, elsewhere last 4bits are ignored
91         rxBits[0] = reverse8(rxBits[0]);
92         rxBits[1] = reverse8(rxBits[1]);
93         rxBits[1] = ((rxBits[1] & 0x0F)<<2) + (rxBits[0]>>6);
94         rxBits[0] &= 0x3F;
95         uint8_t hi_nibble = symbol_6to4(rxBits[0]);
96         if(hi_nibble > 0xF){
97             if(debug_output){
98                 fprintf(stdout, "Error on 6to4 decoding high nibble: %X\n", rxBits[0]);
99             }
100             return 0;
101         }
102         uint8_t lo_nibble = symbol_6to4(rxBits[1]);
103         if(lo_nibble > 0xF){
104             if(debug_output){
105                 fprintf(stdout, "Error on 6to4 decoding low nibble: %X\n", rxBits[1]);
106             }
107             return 0;
108         }
109         uint8_t byte =  hi_nibble<<4 | lo_nibble;
110         payload[nb_bytes] = byte;
111         if(nb_bytes == 0){
112             msg_len = byte;
113         }
114         nb_bytes++;
115     }
116
117     // Get header
118     data_len = msg_len - RH_ASK_HEADER_LEN - 3;
119     header_to = payload[1];
120     header_from = payload[2];
121     header_id = payload[3];
122     header_flags = payload[4];
123
124     // Check CRC
125     crc = payload[5 + data_len] + (payload[5 + data_len + 1]<<8);
126     crc_recompute = ~crc16(payload, msg_len-2, 0x8408, 0xFFFF);
127     if(crc_recompute != crc){
128         if(debug_output){
129             fprintf(stdout, "CRC error: %04X != %04X\n", crc_recompute, crc);
130         }
131         return 0;
132     }
133
134     // Format data
135     for(int j=0; j<msg_len; j++){
136         data_payload[j] = (int)payload[5+j];
137     }
138     data = data_make("time", "",              DATA_STRING, time_str,
139             "model",         "",              DATA_STRING, "RadioHead ASK",
140             "crc",           "",              DATA_STRING, "OK",
141             "len",           "Data len",      DATA_INT, data_len,
142             "to",            "To",            DATA_INT, header_to,
143             "from",          "From",          DATA_INT, header_from,
144             "id",            "Id",            DATA_INT, header_id,
145             "flags",         "Flags",         DATA_INT, header_flags,
146             "payload",       "Payload",       DATA_ARRAY, data_array(data_len, DATA_INT, data_payload),
147     NULL);
148     data_acquired_handler(data);
149
150     return 0;
151 }
152
153 static char *output_fields[] = {
154     "time",
155     "model",
156     "crc",
157     "len",
158     "to",
159     "from",
160     "id",
161     "flags",
162     "payload",
163     NULL
164 };
165
166 r_device radiohead_ask = {
167     .name           = "Radiohead ASK",
168     .modulation     = OOK_PULSE_PCM_RZ,
169     .short_limit    = RH_ASK_BIT_LEN,
170     .long_limit     = RH_ASK_BIT_LEN,
171     .reset_limit    = RH_ASK_BIT_LEN*10,
172     .json_callback  = &radiohead_ask_callback,
173     .fields         = output_fields,
174 };