1 //some hints from http://www.osengr.org/WxShield/Downloads/OregonScientific-RF-Protocols-II.pdf
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
15 static uint8_t calculate_humidity(bitbuffer_t *bitbuffer, unsigned row_index)
17 int units_digit, tens_digit;
20 bitbuffer_extract_bytes(bitbuffer, row_index, HUMIDITY_START_POSITION, out, HUMIDITY_BIT_COUNT);
22 units_digit = out[0] & 0x0f;
24 tens_digit = (out[0] & 0xf0) >> 4;
26 return 10 * tens_digit + units_digit;
29 static int calculate_centigrade_decidegrees(bitbuffer_t *bitbuffer, unsigned row_index)
34 bitbuffer_extract_bytes(bitbuffer, row_index, TEMPERATURE_START_POSITION, out, TEMPERATURE_BIT_COUNT);
36 if(out[0] & 0x80) decidegrees = (~decidegrees << TEMPERATURE_BIT_COUNT);
38 decidegrees |= (out[0] << 4) | ((out[1] & 0xf0) >> 4);
43 static uint8_t get_channel_bits(uint8_t first_byte)
45 return (first_byte & 0x0c) >> 2;
47 static int calculate_channel(uint8_t channel_bits)
49 return (channel_bits % 3) ? channel_bits : 3;
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)
56 bitbuffer_extract_bytes(bitbuffer, row_index, ID_START_POSITION, out, SL109H_MESSAGE_LENGTH - ID_START_POSITION);
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)
64 return (fourth_byte & 0x3C) >> 2;
67 static int calculate_checksum(bitbuffer_t *bitbuffer, unsigned row_index, int channel)
69 uint8_t calculated_checksum, actual_checksum;
71 int actual_expected_comparison;
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);
76 for(int i = 0; i < CHECKSUM_BYTE_COUNT; i++) sum += (out[i] & 0x0f) + (out[i] >> 4);
78 calculated_checksum = ((sum + channel) % 16);
79 actual_checksum = bitbuffer->bb[row_index][0] >> 4;
80 actual_expected_comparison = (calculated_checksum == actual_checksum);
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);
88 return actual_expected_comparison;
92 static int oregon_scientific_callback_sl109h(bitbuffer_t *bitbuffer)
103 char time_str[LOCAL_TIME_BUFLEN];
105 for(int row_index = 0; row_index < bitbuffer->num_rows; row_index++) {
106 msg = bitbuffer->bb[row_index];
108 channel_bits = get_channel_bits(msg[0]);
110 if( !((bitbuffer->bits_per_row[row_index] == SL109H_MESSAGE_LENGTH)
111 && calculate_checksum(bitbuffer, row_index, channel_bits)) ) continue;
113 local_time_str(0, time_str);
115 temp_c = calculate_centigrade_decidegrees(bitbuffer, row_index) / 10.0;
117 humidity = calculate_humidity(bitbuffer, row_index);
119 id = get_id(bitbuffer, row_index);
121 channel = calculate_channel(channel_bits);
123 status = get_status(msg[3]);
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,
132 data_acquired_handler(data);
139 static char *output_fields[] = {
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,
158 .fields = output_fields