5 /* Conrad Electronics S3318P outdoor sensor
7 * Transmit Interval: every ~50s
8 * Message Format: 40 bits (10 nibbles)
11 * Nibble: 1 2 3 4 5 6 7 8 9 10
12 * Type: PP IIIIIIII ??CCTTTT TTTTTTTT HHHHHHHH XB?????? PP
13 * BIT/8 00 01234567 01234567 01234567 01234567 01234567 00
14 * BIT/A 00 01234567 89012345 57890123 45678901 23456789 00
16 * I = sensor ID (changes on battery change)
20 * X = tx-button pressed
26 * [01] {42} 04 15 66 e2 a1 00 : 00000100 00010101 01100110 11100010 10100001 00 ---> Temp/Hum/Ch:23.2/46/1
29 * Sensor sends data in °F, lowest supported value is 90°F
30 * 12 bit uingned and scaled by 10 (Nibbles: 6,5,4)
31 * in this case "011001100101" = 1637/10 - 90 = 73.7 °F (23.17 °C)
34 * 8 bit unsigned (Nibbles 8,7)
35 * in this case "00101110" = 46
37 * Channel number: (Bits 10,11) + 1
38 * in this case "00" --> "00" +1 = Channel1
40 * Battery status: (Bit 33) (0 normal, 1 voltage is below ~2.7 V)
41 * TX-Button: (Bit 32) (0 indicates regular transmission, 1 indicates requested by pushbutton)
43 * Rolling Code / Device ID: (Nibble 1)
44 * changes on every battery change
46 * Unknown1: (Bits 8,9) changes not so often
47 * Unknown2: (Bits 36-39) changes with every packet, probably checksum
48 * Unknown3: (Bits 34,35) changes not so often, mayby also part of the checksum
53 static int s3318p_callback(bitbuffer_t *bitbuffer) {
54 bitrow_t *bb = bitbuffer->bb;
56 char time_str[LOCAL_TIME_BUFLEN];
59 local_time_str(0, time_str);
61 /* Reject codes of wrong length */
62 if ( 42 != bitbuffer->bits_per_row[1])
65 /* shift all the bits left 2 to align the fields */
67 for (i = 0; i < BITBUF_COLS-1; i++) {
68 uint8_t bits1 = bb[1][i] << 2;
69 uint8_t bits2 = (bb[1][i+1] & 0xC0) >> 6;
79 uint16_t temperature_with_offset;
82 /* IIIIIIII ??CCTTTT TTTTTTTT HHHHHHHH XB?????? PP */
83 humidity = (uint8_t)(((bb[1][3] & 0x0F) << 4) | ((bb[1][3] & 0xF0) >> 4));
84 button = (uint8_t)(bb[1][4] >> 7);
85 battery_low = (uint8_t)((bb[1][4] & 0x40) >> 6);
86 channel = (uint8_t)(((bb[1][1] & 0x30) >> 4) + 1);
87 sensor_id = (uint8_t)(bb[1][0]);
89 temperature_with_offset = ((bb[1][2] & 0x0F) << 8) | (bb[1][2] & 0xF0) | (bb[1][1] & 0x0F);
90 temperature_f = (temperature_with_offset - 900) / 10.0;
93 bitbuffer_print(bitbuffer);
94 fprintf(stderr, "Sensor ID = %2x\n", sensor_id);
95 fprintf(stdout, "Bitstream HEX = %02x %02x %02x %02x %02x %02x\n",bb[1][0],bb[1][1],bb[1][2],bb[1][3],bb[1][4],bb[1][5]);
96 fprintf(stdout, "Humidity HEX = %02x\n", bb[1][3]);
97 fprintf(stdout, "Humidity DEC = %u\n", humidity);
98 fprintf(stdout, "Button = %d\n", button);
99 fprintf(stdout, "Battery Low = %d\n", battery_low);
100 fprintf(stdout, "Channel HEX = %02x\n", bb[1][1]);
101 fprintf(stdout, "Channel = %u\n", channel);
102 fprintf(stdout, "temp_with_offset HEX = %02x\n", temperature_with_offset);
103 fprintf(stdout, "temp_with_offset = %d\n", temperature_with_offset);
104 fprintf(stdout, "TemperatureF = %.1f\n", temperature_f);
107 data = data_make("time", "", DATA_STRING, time_str,
108 "model", "", DATA_STRING, "S3318P Temperature & Humidity Sensor",
109 "id", "House Code", DATA_INT, sensor_id,
110 "channel", "Channel", DATA_INT, channel,
111 "battery", "Battery", DATA_STRING, battery_low ? "LOW" : "OK",
112 "button", "Button", DATA_INT, button,
113 "temperature_F", "Temperature", DATA_FORMAT, "%.02f F", DATA_DOUBLE, temperature_f,
114 "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
117 data_acquired_handler(data);
122 static char *output_fields[] = {
136 .name = "S3318P Temperature & Humidity Sensor",
137 .modulation = OOK_PULSE_PPM_RAW,
141 .json_callback = &s3318p_callback,
144 .fields = output_fields