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