Bugfixes
[rtl-433.git] / src / devices / ambient_weather.c
1 #include "rtl_433.h"
2 #include "data.h"
3 #include "util.h"
4
5 static float
6 get_temperature (uint8_t * msg)
7 {
8   int temp_f = msg[4];
9   temp_f <<= 4;
10   temp_f |= ((msg[5] & 0xf0) >> 4);
11   temp_f -= 400;
12   return (temp_f / 10.0f);
13 }
14
15 static uint8_t
16 get_humidity (uint8_t * msg)
17 {
18   uint8_t humidity = ( (msg[5] & 0x0f) << 4) | ((msg[6] & 0xf0) >> 4);
19   return humidity;
20 }
21
22 static uint8_t 
23 calculate_checksum (uint8_t *buff, int length)
24 {
25   uint8_t mask = 0x7C;
26   uint8_t checksum = 0x64;
27   uint8_t data;
28   int byteCnt;  
29
30   for (byteCnt=0; byteCnt < length; byteCnt++)
31   {
32     int bitCnt;
33     data = buff[byteCnt];
34
35     for (bitCnt = 7; bitCnt >= 0 ; bitCnt--)
36     {
37       uint8_t bit;
38
39       // Rotate mask right
40       bit = mask & 1;
41       mask =  (mask >> 1 ) | (mask << 7);
42       if ( bit )
43       {
44         mask ^= 0x18;
45       }
46
47       // XOR mask into checksum if data bit is 1        
48       if ( data & 0x80 )
49       {
50         checksum ^= mask;
51       }
52       data <<= 1; 
53     }
54   }
55   return checksum;
56 }
57
58 static int
59 validate_checksum (uint8_t * msg, int len)
60 {
61   uint8_t expected = ((msg[6] & 0x0f) << 4) | ((msg[7] & 0xf0) >> 4);
62   
63   uint8_t pkt[5];
64   pkt[0] = ((msg[1] & 0x0f) << 4) | ((msg[2] & 0xf0) >> 4);
65   pkt[1] = ((msg[2] & 0x0f) << 4) | ((msg[3] & 0xf0) >> 4);
66   pkt[2] = ((msg[3] & 0x0f) << 4) | ((msg[4] & 0xf0) >> 4);
67   pkt[3] = ((msg[4] & 0x0f) << 4) | ((msg[5] & 0xf0) >> 4);
68   pkt[4] = ((msg[5] & 0x0f) << 4) | ((msg[6] & 0xf0) >> 4);
69   uint8_t calculated = calculate_checksum (pkt, 5);
70
71   if (expected == calculated)
72     return 0;
73   else {
74       if (debug_output >= 1) {
75           fprintf(stderr, "Checksum error in Ambient Weather message.  Expected: %02x  Calculated: %02x\n", expected, calculated);
76           fprintf(stderr, "Message: "); int i; for (i=0; i<len; i++) fprintf(stderr, "%02x ", msg[i]); fprintf(stderr, "\n\n");
77       }
78     return -1;
79   }
80 }
81
82 static uint16_t
83 get_device_id (uint8_t * msg)
84 {
85   uint16_t deviceID = ( (msg[2] & 0x0f) << 4) | ((msg[3] & 0xf0)  >> 4);
86   return deviceID;
87 }
88
89 static uint16_t
90 get_channel (uint8_t * msg)
91 {
92   uint16_t channel = (msg[3] & 0x07) + 1;
93   return channel;
94 }
95
96 static uint8_t
97 get_battery_status(uint8_t * msg)
98 {
99   uint8_t status = (msg[3] & 8) != 0;
100   return status; // if not zero, battery is low
101
102 }
103
104 static int
105 ambient_weather_parser (bitbuffer_t *bitbuffer)
106 {
107   bitrow_t *bb = bitbuffer->bb;
108   float temperature;
109   uint8_t humidity;
110   uint16_t channel;
111   uint16_t deviceID;
112   uint8_t isBatteryLow;
113
114   char time_str[LOCAL_TIME_BUFLEN];
115   data_t *data;
116   local_time_str(0, time_str);
117
118   if(bitbuffer->bits_per_row[0] != 195) // There seems to be 195 bits in a correct message
119     return 0;
120
121   /* shift all the bits left 1 to align the fields */
122   int i;
123   for (i = 0; i < BITBUF_COLS-1; i++) {
124     uint8_t bits1 = bb[0][i] << 1;
125     uint8_t bits2 = (bb[0][i+1] & 0x80) >> 7;
126     bits1 |= bits2;
127     bb[0][i] = bits1;
128   }
129
130   /* DEBUG: print out the received packet */
131   /*
132   fprintf(stderr, "\n! ");
133   for (i = 0 ; i < BITBUF_COLS ; i++) {
134     fprintf (stderr, "%02x ", bb[0][i]);
135   }
136   fprintf (stderr,"\n\n");
137   */
138
139   if ( ((bb[0][0] == 0x00) && (bb[0][1] == 0x14) && (bb[0][2] & 0x50)) ||
140        ((bb[0][0] == 0xff) && (bb[0][1] == 0xd4) && (bb[0][2] & 0x50)) ) {
141
142     if (validate_checksum (bb[0], BITBUF_COLS)) {
143       return 0;
144     }
145
146     temperature = get_temperature (bb[0]);
147     humidity = get_humidity (bb[0]);
148     channel = get_channel (bb[0]);
149     deviceID = get_device_id (bb[0]);
150     isBatteryLow = get_battery_status(bb[0]);
151
152     data = data_make("time", "", DATA_STRING, time_str,
153                         "model",        "",     DATA_STRING,    "Ambient Weather F007TH Thermo-Hygrometer",
154                      "device", "House Code", DATA_INT, deviceID,
155                      "channel", "Channel", DATA_INT, channel,
156                      "battery", "Battery", DATA_STRING, isBatteryLow ? "Low" : "Ok",
157                      "temperature_F", "Temperature", DATA_FORMAT, "%.1f", DATA_DOUBLE, temperature,
158                      "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
159                      NULL);
160     data_acquired_handler(data);
161
162     return 1;
163   }
164
165   return 0;
166 }
167
168 static int
169 ambient_weather_callback (bitbuffer_t *bitbuffer)
170 {
171   return ambient_weather_parser (bitbuffer);
172 }
173
174 static char *output_fields[] = {
175         "time",
176         "device",
177         "channel",
178         "temperature_F",
179         "humidity",
180         NULL
181 };
182
183 r_device ambient_weather = {
184     .name           = "Ambient Weather Temperature Sensor",
185     .modulation     = OOK_PULSE_MANCHESTER_ZEROBIT,
186     .short_limit    = 500,
187     .long_limit     = 0, // not used
188     .reset_limit    = 2400,
189     .json_callback  = &ambient_weather_callback,
190     .disabled       = 0,
191     .demod_arg      = 0,
192     .fields         = output_fields
193 };