Bugfixes
[rtl-433.git] / src / devices / oregon_scientific_sl109h.c
1 //some hints from http://www.osengr.org/WxShield/Downloads/OregonScientific-RF-Protocols-II.pdf
2
3 #include "rtl_433.h"
4 #include "util.h"
5
6 #define SL109H_MESSAGE_LENGTH 38
7 #define CHECKSUM_BYTE_COUNT 4
8 #define CHECKSUM_START_POSITION 6
9 #define HUMIDITY_START_POSITION 6
10 #define HUMIDITY_BIT_COUNT 8
11 #define TEMPERATURE_START_POSITION 14
12 #define TEMPERATURE_BIT_COUNT 12
13 #define ID_START_POSITION 30
14
15 static uint8_t calculate_humidity(bitbuffer_t *bitbuffer, unsigned row_index)
16 {
17     int units_digit, tens_digit;
18
19     uint8_t out[1] = {0};
20     bitbuffer_extract_bytes(bitbuffer, row_index, HUMIDITY_START_POSITION, out, HUMIDITY_BIT_COUNT);
21
22     units_digit = out[0] & 0x0f;
23
24     tens_digit = (out[0] & 0xf0) >> 4;
25
26     return 10 * tens_digit + units_digit;
27 }
28
29 static int calculate_centigrade_decidegrees(bitbuffer_t *bitbuffer, unsigned row_index)
30 {
31     int decidegrees = 0;
32
33     uint8_t out[2] = {0};
34     bitbuffer_extract_bytes(bitbuffer, row_index, TEMPERATURE_START_POSITION, out, TEMPERATURE_BIT_COUNT);
35
36     if(out[0] & 0x80) decidegrees = (~decidegrees << TEMPERATURE_BIT_COUNT);
37
38     decidegrees |= (out[0] << 4) | ((out[1] & 0xf0) >> 4);
39
40     return decidegrees;
41 }
42
43 static uint8_t get_channel_bits(uint8_t first_byte)
44 {
45     return (first_byte & 0x0c) >> 2;
46 }
47 static int calculate_channel(uint8_t channel_bits)
48 {
49     return (channel_bits % 3) ? channel_bits : 3;
50 }
51
52 //rolling code: changes when thermometer reset button is pushed/battery is changed
53 static uint8_t get_id(bitbuffer_t *bitbuffer, unsigned row_index)
54 {
55     uint8_t out[1] = {0};
56     bitbuffer_extract_bytes(bitbuffer, row_index, ID_START_POSITION, out, SL109H_MESSAGE_LENGTH - ID_START_POSITION);
57
58     return out[0];
59 }
60
61 //there may be more specific information here; not currently certain what information is encoded here
62 static int get_status(uint8_t fourth_byte)
63 {
64     return (fourth_byte & 0x3C) >> 2;
65 }
66
67 static int calculate_checksum(bitbuffer_t *bitbuffer, unsigned row_index, int channel)
68 {
69     uint8_t calculated_checksum, actual_checksum;
70     uint8_t sum = 0;
71     int  actual_expected_comparison;
72
73     uint8_t out[CHECKSUM_BYTE_COUNT] = {0};
74     bitbuffer_extract_bytes(bitbuffer, row_index, CHECKSUM_START_POSITION, out, SL109H_MESSAGE_LENGTH - CHECKSUM_START_POSITION);
75
76     for(int i = 0; i < CHECKSUM_BYTE_COUNT; i++) sum += (out[i] & 0x0f) + (out[i] >> 4);
77
78     calculated_checksum = ((sum + channel) % 16);
79     actual_checksum     = bitbuffer->bb[row_index][0] >> 4;
80     actual_expected_comparison = (calculated_checksum == actual_checksum);
81
82     if(debug_output & !actual_expected_comparison) {
83         fprintf(stderr, "Checksum error in Oregon Scientific SL109H message.  Expected: %01x  Calculated: %01x\n", actual_checksum, calculated_checksum);
84         fprintf(stderr, "Message: ");
85         bitbuffer_print(bitbuffer);
86     }
87
88     return actual_expected_comparison;
89 }
90
91
92 static int oregon_scientific_callback_sl109h(bitbuffer_t *bitbuffer)
93 {
94     data_t *data;
95     uint8_t *msg;
96     uint8_t channel_bits;
97
98     float temp_c;
99     uint8_t humidity;
100     int channel;
101     uint8_t id;
102     int status;
103     char time_str[LOCAL_TIME_BUFLEN];
104
105     for(int row_index = 0; row_index < bitbuffer->num_rows; row_index++) {
106         msg = bitbuffer->bb[row_index];
107
108         channel_bits = get_channel_bits(msg[0]);
109
110         if( !((bitbuffer->bits_per_row[row_index] == SL109H_MESSAGE_LENGTH)
111               && calculate_checksum(bitbuffer, row_index, channel_bits)) ) continue;
112
113         local_time_str(0, time_str);
114
115         temp_c = calculate_centigrade_decidegrees(bitbuffer, row_index) / 10.0;
116
117         humidity = calculate_humidity(bitbuffer, row_index);
118
119         id = get_id(bitbuffer, row_index);
120
121         channel = calculate_channel(channel_bits);
122
123         status = get_status(msg[3]);
124
125         data = data_make("time",          "",           DATA_STRING,                         time_str,
126                          "temperature_C", "Celcius",    DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp_c,
127                          "humidity",      "Humidity",   DATA_FORMAT, "%u %%",   DATA_INT,    humidity,
128                          "channel",       "Channel",                            DATA_INT,    channel,
129                          "id",            "Id",                                 DATA_INT,    id,
130                          "status",        "Status",                             DATA_INT,    status,
131                          NULL);
132         data_acquired_handler(data);
133         return 1;
134     }
135
136     return 0;
137 }
138
139 static char *output_fields[] = {
140     "time",
141     "id",
142     "channel",
143     "status",
144     "temperature_C",
145     "humidity",
146     NULL
147 };
148
149 r_device oregon_scientific_sl109h = {
150     .name           = "Oregon Scientific SL109H Remote Thermal Hygro Sensor",
151     .modulation     = OOK_PULSE_PPM_RAW,
152     .short_limit    = 2800/*760*/,
153     .long_limit     = 4400/*1050*/,
154     .reset_limit    = 8000/*2240*/,
155     .json_callback  = &oregon_scientific_callback_sl109h,
156     .disabled       = 0,
157     .demod_arg      = 0,
158     .fields         = output_fields
159 };