Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / devices / s3318p.c
1 #include "rtl_433.h"
2 #include "data.h"
3 #include "util.h"
4
5 /* Conrad Electronics S3318P outdoor sensor
6  *
7  * Transmit Interval: every ~50s
8  * Message Format: 40 bits (10 nibbles)
9  *
10  *
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
15  *            0          1          2          3
16  * I = sensor ID (changes on battery change)
17  * C = channel number
18  * T = temperature
19  * H = humidity
20  * X = tx-button pressed
21  * B = low battery
22  * P = Pre-/Postamble
23  * ? = unknown meaning
24  *
25  *
26  * [01] {42} 04 15 66 e2 a1 00 : 00000100 00010101 01100110 11100010 10100001 00 ---> Temp/Hum/Ch:23.2/46/1
27  *
28  * Temperature:
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)
32  *
33  * Humidity:
34  * 8 bit unsigned (Nibbles 8,7)
35  * in this case "00101110" = 46
36  *
37  * Channel number: (Bits 10,11) + 1
38  * in this case "00" --> "00" +1 = Channel1
39  *
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)
42  *
43  * Rolling Code / Device ID: (Nibble 1)
44  * changes on every battery change
45  *
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
49  *
50  */
51
52
53 static int s3318p_callback(bitbuffer_t *bitbuffer) {
54     bitrow_t *bb = bitbuffer->bb;
55     data_t *data;
56     char time_str[LOCAL_TIME_BUFLEN];
57
58     /* Get time now */
59     local_time_str(0, time_str);
60
61     /* Reject codes of wrong length */
62     if ( 42 != bitbuffer->bits_per_row[1])
63       return 0;
64
65     /* shift all the bits left 2 to align the fields */
66     int i;
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;
70       bits1 |= bits2;
71       bb[1][i] = bits1;
72     }
73
74     uint8_t humidity;
75     uint8_t button;
76     uint8_t battery_low;
77     uint8_t channel;
78     uint8_t sensor_id;
79     uint16_t temperature_with_offset;
80     float temperature_f;
81
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]);
88
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;
91
92     if (debug_output) {
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);
105     }
106
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,
115                       NULL);
116
117     data_acquired_handler(data);
118
119     return 0;
120 }
121
122 static char *output_fields[] = {
123     "time",
124     "model",
125     "id",
126     "channel",
127     "battery",
128     "button",
129     "temperature_C",
130     "humidity",
131     NULL
132 };
133
134
135 r_device s3318p = {
136     .name           = "S3318P Temperature & Humidity Sensor",
137     .modulation     = OOK_PULSE_PPM_RAW,
138     .short_limit    = 2800,
139     .long_limit     = 4400,
140     .reset_limit    = 8000,
141     .json_callback  = &s3318p_callback,
142     .disabled       = 0,
143     .demod_arg      = 0,
144     .fields         = output_fields
145 };
146