1 /* Shenzhen Calibeur Industries Co. Ltd Wireless Thermometer RF-104 Temperature/Humidity sensor
2 * aka Biltema Art. 84-056 (Sold in Denmark)
5 * NB. Only 3 unique sensors can be detected!
7 * Update (LED flash) each 2:53
9 * Pulse Width Modulation with fixed rate and startbit
10 * Startbit = 390 samples = 1560 µs
11 * Short pulse = 190 samples = 760 µs = Logic 0
12 * Long pulse = 560 samples = 2240 µs = Logic 1
13 * Pulse rate = 740 samples = 2960 µs
14 * Burst length = 81000 samples = 324 ms
16 * Sequence of 5 times 21 bit separated by start bit (total of 111 pulses)
17 * S 21 S 21 S 21 S 21 S 21 S
19 * Channel number is encoded into fractional temperature
20 * Temperature is oddly arranged and offset for negative temperatures = <6543210> - 41 C
21 * Allways an odd number of 1s (odd parity)
24 * f = fractional temperature + <ch no> * 10
25 * 0-6 = integer temperature + 41C
27 * H = Most significant bits of humidity [5:6]
28 * h = Least significant bits of humidity [0:4]
31 * ffffff45 01236pHH hhhhh Encoding
37 //static int calibeur_rf104_callback(uint8_t bb[BITBUF_ROWS][BITBUF_COLS], int16_t bits_per_row[BITBUF_ROWS]) {
38 static int calibeur_rf104_callback(bitbuffer_t *bitbuffer) {
40 char time_str[LOCAL_TIME_BUFLEN];
45 bitrow_t *bb = bitbuffer->bb;
47 // Validate package (row [0] is empty due to sync bit)
48 if ((bitbuffer->bits_per_row[1] == 21) // Dont waste time on a long/short package
49 && (crc8(bb[1], 3, 0x80, 0) != 0) // It should be odd parity
50 && (memcmp(bb[1], bb[2], 3) == 0) // We want at least two messages in a row
55 bits = ((bb[1][0] & 0x80) >> 7); // [0]
56 bits |= ((bb[1][0] & 0x40) >> 5); // [1]
57 bits |= ((bb[1][0] & 0x20) >> 3); // [2]
58 bits |= ((bb[1][0] & 0x10) >> 1); // [3]
59 bits |= ((bb[1][0] & 0x08) << 1); // [4]
60 bits |= ((bb[1][0] & 0x04) << 3); // [5]
62 temperature = (float)(bits % 10) / 10.0;
64 bits = ((bb[1][0] & 0x02) << 3); // [4]
65 bits |= ((bb[1][0] & 0x01) << 5); // [5]
66 bits |= ((bb[1][1] & 0x80) >> 7); // [0]
67 bits |= ((bb[1][1] & 0x40) >> 5); // [1]
68 bits |= ((bb[1][1] & 0x20) >> 3); // [2]
69 bits |= ((bb[1][1] & 0x10) >> 1); // [3]
70 bits |= ((bb[1][1] & 0x08) << 3); // [6]
71 temperature += (float)bits - 41.0;
73 bits = ((bb[1][1] & 0x02) << 4); // [5]
74 bits |= ((bb[1][1] & 0x01) << 6); // [6]
75 bits |= ((bb[1][2] & 0x80) >> 7); // [0]
76 bits |= ((bb[1][2] & 0x40) >> 5); // [1]
77 bits |= ((bb[1][2] & 0x20) >> 3); // [2]
78 bits |= ((bb[1][2] & 0x10) >> 1); // [3]
79 bits |= ((bb[1][2] & 0x08) << 1); // [4]
82 local_time_str(0, time_str);
83 data = data_make("time", "", DATA_STRING, time_str,
84 "model", "", DATA_STRING, "Calibeur RF-104",
85 "id", "ID", DATA_INT, ID,
86 "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature,
87 "humidity", "Humidity", DATA_FORMAT, "%2.0f %%", DATA_DOUBLE, humidity,
89 data_acquired_handler(data);
95 static char *output_fields[] = {
104 r_device calibeur_RF104 = {
105 .name = "Calibeur RF-104 Sensor",
106 .modulation = OOK_PULSE_PWM_TERNARY,
107 .short_limit = 1160, // Short pulse 760µs, Startbit 1560µs, Long pulse 2240µs
108 .long_limit = 1900, // Maximum pulse period (long pulse + fixed gap)
109 .reset_limit = 3200, // Longest gap (2960-760µs)
110 .json_callback = &calibeur_rf104_callback,
112 .demod_arg = 1 // Startbit is middle bit