Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Rosstech Digital Control Unit DCU-706/Sundance/Jacuzzi #2612

Merged
merged 10 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
[262] Fine Offset Electronics WH46 air quality sensor
[263] Vevor Wireless Weather Station 7-in-1
[264] Arexx Multilogger IP-HA90, IP-TH78EXT, TSN-70E
[265] Rosstech Digital Control Unit DCU-706/Sundance/Jacuzzi

* Disabled by default, use -R n or a conf file to enable

Expand Down
1 change: 1 addition & 0 deletions conf/rtl_433.example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ convert si
protocol 262 # Fine Offset Electronics WH46 air quality sensor
protocol 263 # Vevor Wireless Weather Station 7-in-1
protocol 264 # Arexx Multilogger IP-HA90, IP-TH78EXT, TSN-70E
protocol 265 # Rosstech Digital Control Unit DCU-706/Sundance/Jacuzzi

## Flex devices (command line option "-X")

Expand Down
1 change: 1 addition & 0 deletions include/rtl_433_devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
DECL(fineoffset_wh46) \
DECL(vevor_7in1) \
DECL(arexx_ml) \
DECL(rosstech_dcu706) \

/* Add new decoders here. */

Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ add_library(r_433 STATIC
devices/revolt_nc5462.c
devices/rftech.c
devices/rojaflex.c
devices/rosstech_dcu706.c
devices/rubicson.c
devices/rubicson_48659.c
devices/rubicson_pool_48942.c
Expand Down
139 changes: 139 additions & 0 deletions src/devices/rosstech_dcu706.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/** @file
Rosstech Digital Control Unit DCU-706/Sundance.

Copyright (C) 2023 suaveolent

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/

#include "decoder.h"

/** @fn int rosstech_dcu706_decode(r_device *decoder, bitbuffer_t *bitbuffer)
Rosstech Digital Control Unit DCU-706/Sundance/Jacuzzi.

Supported Models:
Sundance DCU-6560-131, SD-880 Series, PN 6560-131
Jacuzzi DCU-2560-131, Jac-J300/J400 and SD-780 series, PN 6560-132/2560-131

Data layout:

SS IIII TT CC

- S: 8 bit sync byte and type of transmission
- I: 16 bit ID
- T: 8 bit temp packet in degrees F
- C: 8 bit Checksum: Count 1s for each bit of each element:
Set bit to 1 if number is even 0 if odd

11 bits/byte: 1 start bit, 0 stop bits and odd parity

*/

static uint8_t calculateChecksum(const uint8_t *data, size_t size)
{
uint8_t checksum = 0;

for (int bit = 0; bit < 8; bit++) {
int count = 0;

for (size_t i = 0; i < size; i++) {
count += (data[i] >> bit) & 1;
}

if (count % 2 == 0) {
checksum |= (1 << bit);
}
}

return checksum;
}

suaveolent marked this conversation as resolved.
Show resolved Hide resolved
static int rosstech_dcu706_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
uint8_t const preamble_data_transmission[] = {0xDD, 0x40};
// The Bond command also contains the temperature
uint8_t const preamble_Bond[] = {0xCD, 0x00};
int const preamble_length = 11;

// We need 55 bits
uint8_t msg[7];

if (bitbuffer->num_rows != 1
|| bitbuffer->bits_per_row[0] < 55
|| bitbuffer->bits_per_row[0] > 300) {
decoder_logf(decoder, 2, __func__, "bit_per_row %u out of range", bitbuffer->bits_per_row[0]);
return DECODE_ABORT_EARLY; // Unrecognized data
}

unsigned start_pos = bitbuffer_search(bitbuffer, 0, 0, preamble_data_transmission, preamble_length);

if (start_pos == bitbuffer->bits_per_row[0]) {

start_pos = bitbuffer_search(bitbuffer, 0, 0, preamble_Bond, preamble_length);

if (start_pos == bitbuffer->bits_per_row[0]) {
return DECODE_ABORT_LENGTH;
}
}

if (start_pos + 55 > bitbuffer->bits_per_row[0]) {
return DECODE_ABORT_LENGTH;
}

bitbuffer_extract_bytes(bitbuffer, 0, start_pos, msg, sizeof(msg) * 8);

uint8_t msg_type = (msg[0] << 1) | (msg[1] >> 7); // S
uint8_t id_high = (msg[1] << 4) | (msg[2] >> 4);
uint8_t id_low = (msg[2] << 7) | (msg[3] >> 1);
uint16_t id = (id_high << 8) | id_low; // I
uint8_t temp_f = (msg[4] << 2) | (msg[5] >> 6); // T
uint8_t checksum = (msg[5] << 5) | (msg[6] >> 3); // C

// Create a uint8_t array to hold the extracted values
uint8_t extracted_data[4];
extracted_data[0] = msg_type;
extracted_data[1] = id_high;
extracted_data[2] = id_low;
extracted_data[3] = temp_f;

uint8_t calculated_checksum = calculateChecksum(extracted_data, sizeof(extracted_data) / sizeof(extracted_data[0]));
if (calculated_checksum != checksum) {
decoder_logf(decoder, 2, __func__, "Checksum failed. Expected: %02x, Calculated: %02x.", checksum, calculated_checksum);
return DECODE_FAIL_MIC;
}

/* clang-format off */
data_t *data = data_make(
"model", "Model", DATA_STRING, "Rosstech-Spa",
"id", "ID", DATA_FORMAT, "%04x", DATA_INT, id,
"msg_type", "Transmission Type", DATA_STRING, msg_type == 0xba ? "Data" : "Bond",
"temperature_F", "Temperature", DATA_FORMAT, "%d F", DATA_INT, temp_f,
"mic", "Integrity", DATA_STRING, "CHECKSUM",
NULL);
/* clang-format on */

decoder_output_data(decoder, data);
return 1;
}

static char const *const output_fields[] = {
"model",
"id",
"msg_type",
"temperature_F",
"mic",
NULL,
};

r_device const rosstech_dcu706 = {
.name = "Rosstech Digital Control Unit DCU-706/Sundance/Jacuzzi",
.modulation = OOK_PULSE_PCM,
.short_width = 200,
.long_width = 200,
.reset_limit = 2000,
.decode_fn = &rosstech_dcu706_decode,
.fields = output_fields,
};
Loading