mirror of
https://github.com/merbanan/rtl_433.git
synced 2026-04-22 18:46:58 -04:00
Add CRC to Hideki
This commit is contained in:
@@ -1,121 +1,178 @@
|
||||
/* Hideki Temperature, Humidity, Wind, Rain sensor
|
||||
*
|
||||
* The received bits are inverted.
|
||||
* Every 8 bits are stuffed with a (even) parity bit.
|
||||
* The payload (excluding the header) has an byte parity (XOR) check
|
||||
* The payload (excluding the header) has CRC-8, poly 0x07 init 0x00 check
|
||||
* The payload bytes are reflected (LSB first / LSB last) after the CRC check
|
||||
*
|
||||
* 11111001 0 11110101 0 01110011 1 01111010 1 11001100 0 01000011 1 01000110 1 00111111 0 00001001 0 00010111 0
|
||||
* SYNC+HEAD P RC cha P LEN P Nr.? P .1° 1° P 10° BV P 1% 10% P ? P XOR P CRC P
|
||||
*
|
||||
* TS04:
|
||||
* 00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999
|
||||
* SYNC+HEAD cha RC LEN Nr.? 1° .1° VB 10° 10% 1% ? XOR CRC
|
||||
*
|
||||
* Wind:
|
||||
* 00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999 AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
|
||||
* SYNC+HEAD cha RC LEN Nr.? 1° .1° VB 10° 1° .1° VB 10° 1W .1W .1G 10W 10G 1G w° AA XOR CRC
|
||||
*
|
||||
* Rain:
|
||||
* 00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888
|
||||
* SYNC+HEAD cha RC B LEN Nr.? RAIN_L RAIN_H 0x66 XOR CRC
|
||||
*
|
||||
*/
|
||||
|
||||
#include "decoder.h"
|
||||
|
||||
#define HIDEKI_MAX_BYTES_PER_ROW 14
|
||||
|
||||
// 11111001 0 11110101 0 01110011 1 01111010 1 11001100 0 01000011 1 01000110 1 00111111 0 00001001 0 00010111 0
|
||||
// SYNC+HEAD P RC cha P P Nr.? P .1° 1° P 10° BV P 1% 10% P ????SYNC -------Check?------- P
|
||||
|
||||
//TS04:
|
||||
// 00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999
|
||||
// SYNC+HEAD cha RC Nr.? 1° .1° VB 10° 10% 1% SYNC???? -----Check?------
|
||||
|
||||
//Wind:
|
||||
// 00000000 11111111 22222222 33333333 44444444 55555555 66666666 77777777 88888888 99999999 AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
|
||||
// SYNC+HEAD cha RC Nr.? 1° .1° VB 10° 1° .1° VB 10° .1mh 1mh ?? 10mh ???? w° ?? ???? ????
|
||||
|
||||
enum sensortypes { HIDEKI_UNKNOWN, HIDEKI_TEMP, HIDEKI_TS04, HIDEKI_WIND, HIDEKI_RAIN };
|
||||
|
||||
static int hideki_ts04_callback(r_device *decoder, bitbuffer_t *bitbuffer) {
|
||||
data_t *data;
|
||||
uint8_t *b = bitbuffer->bb[0]; // TODO: handle the 3 row, need change in PULSE_CLOCK decoding
|
||||
uint8_t packet[HIDEKI_MAX_BYTES_PER_ROW];
|
||||
int sensortype = HIDEKI_WIND; // default for 14 valid bytes
|
||||
uint8_t channel, humidity, rc, battery_ok;
|
||||
int temp, wind_strength, wind_direction, rain_units;
|
||||
int sensortype, chk;
|
||||
int channel, rc, battery_ok;
|
||||
int temp, humidity, rain_units;
|
||||
int wind_strength, gust_speed, wind_direction;
|
||||
|
||||
// Transform incoming data:
|
||||
// * reverse MSB/LSB
|
||||
// * invert all bits
|
||||
// * Remove (and check) parity bit
|
||||
// TODO this may be factorise as a bitbuffer method (as for bitbuffer_manchester_decode)
|
||||
for (int i = 0; i < HIDEKI_MAX_BYTES_PER_ROW; i++) {
|
||||
// Expect 8, 9, 10, or 14 unstuffed bytes
|
||||
int unstuffed_len = bitbuffer->bits_per_row[0] / 9;
|
||||
if (unstuffed_len == 14)
|
||||
sensortype = HIDEKI_WIND;
|
||||
else if (unstuffed_len == 10)
|
||||
sensortype = HIDEKI_TS04;
|
||||
else if (unstuffed_len == 9)
|
||||
sensortype = HIDEKI_RAIN;
|
||||
else if (unstuffed_len == 8)
|
||||
sensortype = HIDEKI_TEMP;
|
||||
else
|
||||
return 0;
|
||||
|
||||
// Invert all bits
|
||||
bitbuffer_invert(bitbuffer);
|
||||
|
||||
// Strip (unstuff) and check parity bit
|
||||
// TODO: refactor to util function
|
||||
for (int i = 0; i < unstuffed_len; ++i) {
|
||||
unsigned int offset = i/8;
|
||||
packet[i] = (b[i+offset] << (i%8)) | (b[i+offset+1] >> (8 - i%8));
|
||||
packet[i] = reverse8(packet[i]); // reverse LSB first to LSB last
|
||||
packet[i] ^= 0xFF; // invert bits
|
||||
// check parity
|
||||
uint8_t parity = ((b[i+offset+1] >> (7 - i%8)) ^ 0xFF) & 0x01;
|
||||
if (parity != byteParity(packet[i]))
|
||||
{
|
||||
if (i == 10) {
|
||||
sensortype = HIDEKI_TS04;
|
||||
break;
|
||||
}
|
||||
if (i == 9) {
|
||||
sensortype = HIDEKI_RAIN;
|
||||
break;
|
||||
}
|
||||
if (i == 8) {
|
||||
sensortype = HIDEKI_TEMP;
|
||||
break;
|
||||
}
|
||||
uint8_t parity = (b[i+offset+1] >> (7 - i%8)) & 1;
|
||||
if (parity != byteParity(packet[i])) {
|
||||
if (decoder->verbose)
|
||||
fprintf(stderr, "%s: Parity error at %d\n", __func__, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Read data
|
||||
// XOR check all bytes
|
||||
chk = 0;
|
||||
for (int i = 1; i < unstuffed_len - 1; ++i) {
|
||||
chk ^= packet[i];
|
||||
}
|
||||
if (chk) {
|
||||
if (decoder->verbose)
|
||||
fprintf(stderr, "%s: XOR error\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CRC-8 poly=0x07 init=0x00
|
||||
if (crc8(&packet[1], unstuffed_len - 1, 0x07, 0x00)) {
|
||||
if (decoder->verbose)
|
||||
fprintf(stderr, "%s: CRC error\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reflect LSB first to LSB last
|
||||
for (int i = 0; i < unstuffed_len; ++i)
|
||||
packet[i] = reverse8(packet[i]);
|
||||
|
||||
// Parse data
|
||||
if (packet[0] != 0x9f) // NOTE: other valid ids might exist
|
||||
return 0;
|
||||
|
||||
int pkt_len = (packet[2] >> 1) & 0x1f;
|
||||
int pkt_seq = packet[3] >> 6;
|
||||
int pkt_type = packet[3] & 0x1f;
|
||||
// 0x0C Anemometer
|
||||
// 0x0D UV sensor
|
||||
// 0x0E Rain level meter
|
||||
// 0x1E Thermo/hygro-sensor
|
||||
|
||||
if (pkt_len +3 != unstuffed_len) {
|
||||
if (decoder->verbose)
|
||||
fprintf(stderr, "%s: LEN error\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
channel = (packet[1] >> 5) & 0x0F;
|
||||
if (channel >= 5) channel -= 1;
|
||||
rc = packet[1] & 0x0F;
|
||||
temp = (packet[5] & 0x0F) * 100 + ((packet[4] & 0xF0) >> 4) * 10 + (packet[4] & 0x0F);
|
||||
if (((packet[5]>>7) & 0x01) == 0) {
|
||||
if (((packet[5]>>7) & 1) == 0) {
|
||||
temp = -temp;
|
||||
}
|
||||
battery_ok = (packet[5]>>6) & 0x01;
|
||||
battery_ok = (packet[5]>>6) & 1;
|
||||
|
||||
if (sensortype == HIDEKI_TS04) {
|
||||
humidity = ((packet[6] & 0xF0) >> 4) * 10 + (packet[6] & 0x0F);
|
||||
data = data_make(
|
||||
"model", "", DATA_STRING, "HIDEKI TS04 sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp/10.f,
|
||||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
|
||||
NULL);
|
||||
"model", "", DATA_STRING, "HIDEKI TS04 sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp/10.f,
|
||||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
|
||||
"mic", "MIC", DATA_STRING, "CRC",
|
||||
NULL);
|
||||
decoder_output_data(decoder, data);
|
||||
return 1;
|
||||
}
|
||||
if (sensortype == HIDEKI_WIND) {
|
||||
const uint8_t wd[] = { 0, 15, 13, 14, 9, 10, 12, 11, 1, 2, 4, 3, 8, 7, 5, 6 };
|
||||
wind_direction = wd[((packet[11] & 0xF0) >> 4)] * 225;
|
||||
wind_strength = (packet[9] & 0x0F) * 100 + ((packet[8] & 0xF0) >> 4) * 10 + (packet[8] & 0x0F);
|
||||
wind_strength = (packet[9] & 0x0F) * 100 + (packet[8] >> 4) * 10 + (packet[8] & 0x0F);
|
||||
|
||||
data = data_make(
|
||||
"model", "", DATA_STRING, "HIDEKI Wind sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp/10.f,
|
||||
"windstrength", "Wind Strength", DATA_FORMAT, "%.02f km/h", DATA_DOUBLE, wind_strength*0.160934f,
|
||||
"winddirection", "Direction", DATA_FORMAT, "%.01f °", DATA_DOUBLE, wind_direction/10.f,
|
||||
NULL);
|
||||
"model", "", DATA_STRING, "HIDEKI Wind sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp * 0.1f,
|
||||
"windstrength", "Wind Strength", DATA_FORMAT, "%.02f km/h", DATA_DOUBLE, wind_strength * 0.160934f,
|
||||
"winddirection", "Direction", DATA_FORMAT, "%.01f °", DATA_DOUBLE, wind_direction * 0.1f,
|
||||
"mic", "MIC", DATA_STRING, "CRC",
|
||||
NULL);
|
||||
decoder_output_data(decoder, data);
|
||||
return 1;
|
||||
}
|
||||
if (sensortype == HIDEKI_TEMP) {
|
||||
data = data_make(
|
||||
"model", "", DATA_STRING, "HIDEKI Temperature sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp/10.f,
|
||||
NULL);
|
||||
"model", "", DATA_STRING, "HIDEKI Temperature sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp * 0.1f,
|
||||
"mic", "MIC", DATA_STRING, "CRC",
|
||||
NULL);
|
||||
decoder_output_data(decoder, data);
|
||||
return 1;
|
||||
}
|
||||
if (sensortype == HIDEKI_RAIN) {
|
||||
rain_units = (packet[5] << 8) + packet[4];
|
||||
battery_ok = (packet[2]>>6) & 0x01;
|
||||
rain_units = (packet[5] << 8) | packet[4];
|
||||
battery_ok = (packet[2] >> 6) & 1;
|
||||
|
||||
data = data_make(
|
||||
"model", "", DATA_STRING, "HIDEKI Rain sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"rain", "Rain", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_units*0.7f,
|
||||
NULL);
|
||||
"model", "", DATA_STRING, "HIDEKI Rain sensor",
|
||||
"rc", "Rolling Code", DATA_INT, rc,
|
||||
"channel", "Channel", DATA_INT, channel,
|
||||
"battery", "Battery", DATA_STRING, battery_ok ? "OK": "LOW",
|
||||
"rain", "Rain", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_units * 0.7f,
|
||||
"mic", "MIC", DATA_STRING, "CRC",
|
||||
NULL);
|
||||
decoder_output_data(decoder, data);
|
||||
return 1;
|
||||
}
|
||||
@@ -132,6 +189,7 @@ static char *output_fields[] = {
|
||||
"windstrength",
|
||||
"winddirection",
|
||||
"rain",
|
||||
"mic",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -141,7 +199,7 @@ r_device hideki_ts04 = {
|
||||
.short_width = 520, // half-bit width 520 us
|
||||
.long_width = 1040, // bit width 1040 us
|
||||
.reset_limit = 4000,
|
||||
.tolerance = 240, // us
|
||||
.tolerance = 240,
|
||||
.decode_fn = &hideki_ts04_callback,
|
||||
.disabled = 0,
|
||||
.fields = output_fields,
|
||||
|
||||
Reference in New Issue
Block a user