Add CRC to Hideki

This commit is contained in:
Christian W. Zuckschwerdt
2018-12-03 12:29:52 +01:00
parent d3be834d1c
commit add141fa34

View File

@@ -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,