diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 31adcc08..63e0237f 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -320,7 +320,7 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo } void set_weather() { - const WeatherRxConfigureMessage message{}; + const SubGhzFPRxConfigureMessage message{0, 0}; send_message(&message); } diff --git a/firmware/baseband/fprotos/fprotogeneral.hpp b/firmware/baseband/fprotos/fprotogeneral.hpp new file mode 100644 index 00000000..96484ea3 --- /dev/null +++ b/firmware/baseband/fprotos/fprotogeneral.hpp @@ -0,0 +1,176 @@ +#ifndef __FPROTO_GENERAL_H__ +#define __FPROTO_GENERAL_H__ + +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) + +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +typedef enum { + ManchesterStateStart1 = 0, + ManchesterStateMid1 = 1, + ManchesterStateMid0 = 2, + ManchesterStateStart0 = 3 +} ManchesterState; +typedef enum { + ManchesterEventShortLow = 0, + ManchesterEventShortHigh = 2, + ManchesterEventLongLow = 4, + ManchesterEventLongHigh = 6, + ManchesterEventReset = 8 +} ManchesterEvent; + +class FProtoGeneral { + public: + static bool manchester_advance( + ManchesterState state, + ManchesterEvent event, + ManchesterState* next_state, + bool* data) { + bool result = false; + ManchesterState new_state; + + if (event == ManchesterEventReset) { + new_state = ManchesterStateMid1; + } else { + new_state = (ManchesterState)(transitions[state] >> event & 0x3); + if (new_state == state) { + new_state = ManchesterStateMid1; + } else { + if (new_state == ManchesterStateMid0) { + if (data) *data = false; + result = true; + } else if (new_state == ManchesterStateMid1) { + if (data) *data = true; + result = true; + } + } + } + + *next_state = new_state; + return result; + } + static uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for (size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; + } + static uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; + } + static uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for (size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; + } + static uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for (size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for (int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if ((data >> i) & 1) sum ^= key; + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if (key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; + } + static float locale_fahrenheit_to_celsius(float temp_f) { + return (temp_f - 32.f) / 1.8f; + } + + static uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while (size--) { + remainder ^= *message++; + for (bit = 0; bit < 8; bit++) { + if (remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs + } + static uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for (int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for (uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if ((data >> i) & 1) { + sum ^= key; + } + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if (key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; + } + static uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for (uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); + } + return reverse_key; + } + static uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for (size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for (uint8_t bit = 0; bit < 8; ++bit) { + if (remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; + } + + private: + static inline const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011}; +}; + +#endif \ No newline at end of file diff --git a/firmware/baseband/fprotos/fprotolistgeneral.hpp b/firmware/baseband/fprotos/fprotolistgeneral.hpp new file mode 100644 index 00000000..61d5b4ca --- /dev/null +++ b/firmware/baseband/fprotos/fprotolistgeneral.hpp @@ -0,0 +1,12 @@ +#ifndef __FPROTO_PROTOLISTGENERAL_H__ +#define __FPROTO_PROTOLISTGENERAL_H__ +#include + +class FProtoListGeneral { + public: + FProtoListGeneral() {} + virtual ~FProtoListGeneral() {} + virtual void feed(bool level, uint32_t duration) = 0; +}; + +#endif \ No newline at end of file diff --git a/firmware/baseband/fprotos/s-ansonic.hpp b/firmware/baseband/fprotos/s-ansonic.hpp new file mode 100644 index 00000000..8592a3dd --- /dev/null +++ b/firmware/baseband/fprotos/s-ansonic.hpp @@ -0,0 +1,126 @@ + +#ifndef __FPROTO_ANSONIC_H__ +#define __FPROTO_ANSONIC_H__ + +#include "subghzdbase.hpp" + +#define ANSONICDIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define ANSONICCNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +class FProtoSubGhzDAnsonic : public FProtoSubGhzDBase { + public: + FProtoSubGhzDAnsonic() { + sensorType = FPS_ANSONIC; + modulation = FPM_FM; + } + + void feed(bool level, uint32_t duration) { + switch (parser_step) { + case AnsonicDecoderStepReset: + if ((!level) && (DURATION_DIFF(duration, te_short * 35) < te_delta * 35)) { + // Found header Ansonic + parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if (!level) { + break; + } else if ( + DURATION_DIFF(duration, te_short) < te_delta) { + // Found start bit Ansonic + parser_step = AnsonicDecoderStepSaveDuration; + decode_data = 0; + decode_count_bit = 0; + } else { + parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if (!level) { // save interval + if (duration >= (te_short * 4)) { + parser_step = AnsonicDecoderStepFoundStartBit; + if (decode_count_bit >= + min_count_bit_for_found) { + serial = 0x0; + btn = 0x0; + data = decode_data; + data_count_bit = decode_count_bit; + if (callback) callback(this); + } + break; + } + te_last = duration; + parser_step = AnsonicDecoderStepCheckDuration; + } else { + parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if (level) { + if ((DURATION_DIFF(te_last, te_short) < + te_delta) && + (DURATION_DIFF(duration, te_long) < + te_delta)) { + subghz_protocol_blocks_add_bit(1); + parser_step = AnsonicDecoderStepSaveDuration; + } else if ( + (DURATION_DIFF(te_last, te_long) < + te_delta) && + (DURATION_DIFF(duration, te_short) < + te_delta)) { + subghz_protocol_blocks_add_bit(0); + parser_step = AnsonicDecoderStepSaveDuration; + } else + parser_step = AnsonicDecoderStepReset; + } else { + parser_step = AnsonicDecoderStepReset; + } + break; + } + } + void subghz_protocol_ansonic_check_remote_controller() { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + cnt = data & 0xFFF; + btn = ((data >> 1) & 0x3); + } + void get_string(char* output, size_t outSize) { + subghz_protocol_ansonic_check_remote_controller(); + snprintf( + output, outSize, + "%dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" ANSONICDIP_PATTERN "\r\n", + data_count_bit, + (uint32_t)(data & 0xFFFFFFFF), + btn, + ANSONICCNT_TO_DIP(cnt)); + } + + protected: + uint32_t te_short = 555; + uint32_t te_long = 1111; + uint32_t te_delta = 120; + uint32_t min_count_bit_for_found = 12; + + uint32_t crc = 0; +}; + +#endif diff --git a/firmware/baseband/fprotos/subghzdbase.hpp b/firmware/baseband/fprotos/subghzdbase.hpp new file mode 100644 index 00000000..109f06c2 --- /dev/null +++ b/firmware/baseband/fprotos/subghzdbase.hpp @@ -0,0 +1,61 @@ +/* +Base class for all weather protocols. +This and most of the weather protocols uses code from Flipper XTreme codebase ( https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/lib/subghz ). Thanks for their work! +For comments in a protocol implementation check w-nexus-th.hpp +*/ + +#ifndef __FPROTO_SBASE_H__ +#define __FPROTO_SBASE_H__ + +#include "fprotogeneral.hpp" +#include "subghztypes.hpp" + +#include +// default walues to indicate 'no value' +#define SD_NO_ID 0xFFFFFFFF + +class FProtoSubGhzDBase; +typedef void (*SubGhzDProtocolDecoderBaseRxCallback)(FProtoSubGhzDBase* instance); + +class FProtoSubGhzDBase { + public: + FProtoSubGhzDBase() {} + virtual ~FProtoSubGhzDBase() {} + virtual void feed(bool level, uint32_t duration) = 0; // need to be implemented on each protocol handler. + void setCallback(SubGhzDProtocolDecoderBaseRxCallback cb) { callback = cb; } // this is called when there is a hit. + + uint8_t getSensorType() { return sensorType; } + uint32_t getSensorId() { return id; } + + protected: + // Helper functions to keep it as compatible with flipper as we can, so adding new protos will be easy. + void subghz_protocol_blocks_add_bit(uint8_t bit) { + decode_data = decode_data << 1 | bit; + decode_count_bit++; + } + + // General weather data holder + uint8_t sensorType = FPS_Invalid; + uint32_t id = SD_NO_ID; + FPROTO_SUBGHZD_MODULATION modulation = FPM_AM; // override this, if FM + + // inner logic stuff, also for flipper compatibility. + SubGhzDProtocolDecoderBaseRxCallback callback = NULL; + uint16_t header_count = 0; + uint8_t parser_step = 0; + uint32_t te_last = 0; + uint64_t data = 0; + uint64_t data_2 = 0; + uint32_t serial = 0; + uint16_t data_count_bit = 0; + uint64_t decode_data = 0; + uint32_t decode_count_bit = 0; + uint8_t btn = 0; + uint32_t cnt = 0; + uint8_t cnt_2 = 0; + uint32_t seed = 0; + + ManchesterState manchester_saved_state = ManchesterStateMid1; +}; + +#endif \ No newline at end of file diff --git a/firmware/baseband/fprotos/subghzdprotos.hpp b/firmware/baseband/fprotos/subghzdprotos.hpp new file mode 100644 index 00000000..6710d010 --- /dev/null +++ b/firmware/baseband/fprotos/subghzdprotos.hpp @@ -0,0 +1,45 @@ +/* +This is the protocol list handler. It holds an instance of all known protocols. +So include here the .hpp, and add a new element to the protos vector in the constructor. That's all you need to do here if you wanna add a new proto. + @htotoo +*/ + +#include +#include +#include "portapack_shared_memory.hpp" + +#include "fprotolistgeneral.hpp" +#include "subghzdbase.hpp" +#include "s-ansonic.hpp" + +#ifndef __FPROTO_PROTOLISTSGZ_H__ +#define __FPROTO_PROTOLISTSGZ_H__ + +class SubGhzDProtos : public FProtoListGeneral { + public: + SubGhzDProtos() { + // add protos + protos.push_back(std::make_unique()); // 1 + + // set callback for them + for (const auto& obj : protos) { + obj->setCallback(callbackTarget); + } + } + + static void callbackTarget(FProtoSubGhzDBase* instance) { + SubGhzDDataMessage packet_message{instance->getSensorType(), instance->getSensorId()}; // TODO add get_string data too + shared_memory.application_queue.push(packet_message); + } + + void feed(bool level, uint32_t duration) { + for (const auto& obj : protos) { + obj->feed(level, duration); + } + } + + protected: + std::vector> protos{}; +}; + +#endif diff --git a/firmware/baseband/fprotos/subghztypes.hpp b/firmware/baseband/fprotos/subghztypes.hpp index f5fe5f97..3a263d6a 100644 --- a/firmware/baseband/fprotos/subghztypes.hpp +++ b/firmware/baseband/fprotos/subghztypes.hpp @@ -8,8 +8,14 @@ These values must be present on the protocol's constructor, like FProtoWeatherAc Also it must have a switch-case element in the getSubGhzDSensorTypeName() function, to display it's name. */ +enum FPROTO_SUBGHZD_MODULATION { + FPM_AM = 0, + FPM_FM = 1, +}; + enum FPROTO_SUBGHZD_SENSOR { FPS_Invalid = 0, + FPS_ANSONIC = 1, }; diff --git a/firmware/baseband/fprotos/w-acurite592txr.hpp b/firmware/baseband/fprotos/w-acurite592txr.hpp index e9747210..5727b710 100644 --- a/firmware/baseband/fprotos/w-acurite592txr.hpp +++ b/firmware/baseband/fprotos/w-acurite592txr.hpp @@ -130,9 +130,9 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase { static_cast(decode_data >> 16), static_cast(decode_data >> 8)}; - if ((subghz_protocol_blocks_add_bytes(msg, 6) == + if ((FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 6) == (uint8_t)(decode_data & 0xFF)) && - (!subghz_protocol_blocks_parity_bytes(&msg[2], 4))) { + (!FProtoGeneral::subghz_protocol_blocks_parity_bytes(&msg[2], 4))) { return true; } else { return false; diff --git a/firmware/baseband/fprotos/w-acurite606tx.hpp b/firmware/baseband/fprotos/w-acurite606tx.hpp index ba1cdee2..60584b27 100644 --- a/firmware/baseband/fprotos/w-acurite606tx.hpp +++ b/firmware/baseband/fprotos/w-acurite606tx.hpp @@ -102,7 +102,7 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase { static_cast(decode_data >> 16), static_cast(decode_data >> 8)}; - uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); return (crc == (decode_data & 0xFF)); } }; diff --git a/firmware/baseband/fprotos/w-acurite986.hpp b/firmware/baseband/fprotos/w-acurite986.hpp index 24dce719..3e4ba996 100644 --- a/firmware/baseband/fprotos/w-acurite986.hpp +++ b/firmware/baseband/fprotos/w-acurite986.hpp @@ -108,16 +108,16 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase { void ws_protocol_acurite_986_remote_controller() { int temp; - id = subghz_protocol_blocks_reverse_key(data >> 24, 8); - id = (id << 8) | subghz_protocol_blocks_reverse_key(data >> 16, 8); + id = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 24, 8); + id = (id << 8) | FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 16, 8); battery_low = (data >> 14) & 1; channel = ((data >> 15) & 1) + 1; - temp = subghz_protocol_blocks_reverse_key(data >> 32, 8); + temp = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 32, 8); if (temp & 0x80) { temp = -(temp & 0x7F); } - temp = locale_fahrenheit_to_celsius((float)temp); + temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)temp); btn = WS_NO_BTN; humidity = WS_NO_HUMIDITY; } @@ -130,7 +130,7 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase { (uint8_t)(decode_data >> 16), (uint8_t)(decode_data >> 8)}; - uint8_t crc = subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00); + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00); return (crc == (decode_data & 0xFF)); } }; diff --git a/firmware/baseband/fprotos/w-ambient.hpp b/firmware/baseband/fprotos/w-ambient.hpp index 20ec1ff4..7f37b1d8 100644 --- a/firmware/baseband/fprotos/w-ambient.hpp +++ b/firmware/baseband/fprotos/w-ambient.hpp @@ -33,7 +33,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase { } if (event != ManchesterEventReset) { bool data; - bool data_ok = manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); + bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); if (data_ok) { decode_data = (decode_data << 1) | !data; @@ -53,7 +53,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase { } else { decode_data = 0; decode_count_bit = 0; - manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); + FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); } } @@ -71,7 +71,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase { static_cast(decode_data >> 16), static_cast(decode_data >> 8)}; - uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64; + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64; return (crc == (uint8_t)(decode_data & 0xFF)); } @@ -79,7 +79,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase { id = (data >> 32) & 0xFF; battery_low = (data >> 31) & 1; channel = ((data >> 28) & 0x07) + 1; - temp = locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f); + temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f); humidity = (data >> 8) & 0xFF; btn = WS_NO_BTN; } diff --git a/firmware/baseband/fprotos/w-infactory.hpp b/firmware/baseband/fprotos/w-infactory.hpp index 1e70709b..bc5e2343 100644 --- a/firmware/baseband/fprotos/w-infactory.hpp +++ b/firmware/baseband/fprotos/w-infactory.hpp @@ -118,19 +118,16 @@ class FProtoWeatherInfactory : public FProtoWeatherBase { static_cast(decode_data >> 8), static_cast(decode_data)}; - uint8_t crc = - subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 - crc ^= msg[4] >> 4; // last nibble is only XORed + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 + crc ^= msg[4] >> 4; // last nibble is only XORed return (crc == ((decode_data >> 28) & 0x0F)); } void ws_protocol_infactory_remote_controller() { id = data >> 32; battery_low = (data >> 26) & 1; btn = WS_NO_BTN; - temp = - locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f); - humidity = - (((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH + temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f); + humidity = (((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH channel = data & 0x03; } }; diff --git a/firmware/baseband/fprotos/w-lacrosse-tx.hpp b/firmware/baseband/fprotos/w-lacrosse-tx.hpp index 684cbbe1..6bbcd93a 100644 --- a/firmware/baseband/fprotos/w-lacrosse-tx.hpp +++ b/firmware/baseband/fprotos/w-lacrosse-tx.hpp @@ -155,7 +155,7 @@ class FProtoWeatherLaCrosseTx : public FProtoWeatherBase { static_cast((decode_data >> 8) & 0x0F), static_cast((decode_data >> 4) & 0x0F)}; - uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9); + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 9); return ((crc & 0x0F) == (decode_data & 0x0F)); } }; diff --git a/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp b/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp index d726267f..72d3c494 100644 --- a/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp +++ b/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp @@ -104,7 +104,7 @@ class FProtoWeatherLaCrosseTx141thbv2 : public FProtoWeatherBase { } uint8_t msg[] = {static_cast(data >> 32), static_cast(data >> 24), static_cast(data >> 16), static_cast(data >> 8)}; - uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); return (crc == (data & 0xFF)); } void ws_protocol_lacrosse_tx141thbv2_remote_controller() { diff --git a/firmware/baseband/fprotos/w-oregon2.hpp b/firmware/baseband/fprotos/w-oregon2.hpp index a17bfebb..1267903b 100644 --- a/firmware/baseband/fprotos/w-oregon2.hpp +++ b/firmware/baseband/fprotos/w-oregon2.hpp @@ -77,7 +77,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase { decode_data = 0UL; decode_count_bit = 0; } - if (manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data)) { + if (FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data)) { if (have_bit) { if (!prev_bit && data) { subghz_protocol_blocks_add_bit(1); @@ -166,7 +166,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase { parser_step = Oregon2DecoderStepReset; decode_data = 0UL; decode_count_bit = 0; - manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); + FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); have_bit = false; var_data = 0; var_bits = 0; diff --git a/firmware/baseband/fprotos/w-oregon3.hpp b/firmware/baseband/fprotos/w-oregon3.hpp index 5572208b..629d3474 100644 --- a/firmware/baseband/fprotos/w-oregon3.hpp +++ b/firmware/baseband/fprotos/w-oregon3.hpp @@ -47,7 +47,7 @@ class FProtoWeatherOregon3 : public FProtoWeatherBase { decode_data = 0UL; decode_count_bit = 0; } - if (manchester_advance( + if (FProtoGeneral::manchester_advance( manchester_saved_state, event, &manchester_saved_state, &prev_bit)) { subghz_protocol_blocks_add_bit(prev_bit); } diff --git a/firmware/baseband/fprotos/w-oregonv1.hpp b/firmware/baseband/fprotos/w-oregonv1.hpp index 932ed48c..5e8b615e 100644 --- a/firmware/baseband/fprotos/w-oregonv1.hpp +++ b/firmware/baseband/fprotos/w-oregonv1.hpp @@ -64,11 +64,7 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase { // found all the necessary patterns decode_data = 0; decode_count_bit = 1; - manchester_advance( - manchester_saved_state, - ManchesterEventReset, - &manchester_saved_state, - NULL); + FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); parser_step = Oregon_V1DecoderStepParse; if (duration < te_short * 4) { first_bit = 1; @@ -114,19 +110,14 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase { } decode_data = 0; decode_count_bit = 0; - manchester_advance( - manchester_saved_state, - ManchesterEventReset, - &manchester_saved_state, - NULL); + FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL); } else { parser_step = Oregon_V1DecoderStepReset; } } if (event != ManchesterEventReset) { bool data; - bool data_ok = manchester_advance( - manchester_saved_state, event, &manchester_saved_state, &data); + bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); if (data_ok) { decode_data = (decode_data << 1) | !data; @@ -149,13 +140,13 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase { bool ws_protocol_oregon_v1_check() { if (!decode_data) return false; - uint64_t data = subghz_protocol_blocks_reverse_key(decode_data, 32); + uint64_t data = FProtoGeneral::subghz_protocol_blocks_reverse_key(decode_data, 32); uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff); crc = (crc & 0xff) + ((crc >> 8) & 0xff); return (crc == ((data >> 24) & 0xFF)); } void ws_protocol_oregon_v1_remote_controller() { - uint64_t data2 = subghz_protocol_blocks_reverse_key(data, 32); + uint64_t data2 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 32); id = data2 & 0xFF; channel = ((data2 >> 6) & 0x03) + 1; diff --git a/firmware/baseband/fprotos/w-wendox-w6726.hpp b/firmware/baseband/fprotos/w-wendox-w6726.hpp index 95667b0c..f6629c33 100644 --- a/firmware/baseband/fprotos/w-wendox-w6726.hpp +++ b/firmware/baseband/fprotos/w-wendox-w6726.hpp @@ -128,7 +128,7 @@ class FProtoWeatherWendoxW6726 : public FProtoWeatherBase { static_cast(decode_data >> 12), static_cast(decode_data >> 4)}; - uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); + uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); return (crc == (decode_data & 0x0F)); } void ws_protocol_wendox_w6726_remote_controller() { diff --git a/firmware/baseband/fprotos/weatherbase.hpp b/firmware/baseband/fprotos/weatherbase.hpp index aa65940d..4f2c3ea4 100644 --- a/firmware/baseband/fprotos/weatherbase.hpp +++ b/firmware/baseband/fprotos/weatherbase.hpp @@ -7,8 +7,7 @@ For comments in a protocol implementation check w-nexus-th.hpp #ifndef __FPROTO_BASE_H__ #define __FPROTO_BASE_H__ -#define bit_read(value, bit) (((value) >> (bit)) & 0x01) - +#include "fprotogeneral.hpp" #include "weathertypes.hpp" #include @@ -20,20 +19,6 @@ For comments in a protocol implementation check w-nexus-th.hpp #define WS_NO_BTN 0xFF #define WS_NO_TEMPERATURE -273.0f -#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) -typedef enum { - ManchesterStateStart1 = 0, - ManchesterStateMid1 = 1, - ManchesterStateMid0 = 2, - ManchesterStateStart0 = 3 -} ManchesterState; -typedef enum { - ManchesterEventShortLow = 0, - ManchesterEventShortHigh = 2, - ManchesterEventLongLow = 4, - ManchesterEventLongHigh = 6, - ManchesterEventReset = 8 -} ManchesterEvent; class FProtoWeatherBase; typedef void (*SubGhzProtocolDecoderBaseRxCallback)(FProtoWeatherBase* instance); @@ -49,7 +34,6 @@ class FProtoWeatherBase { float getTemp() { return temp; } uint8_t getHumidity() { return humidity; } uint8_t getBattLow() { return battery_low; } - uint32_t getTimestamp() { return timestamp; } uint8_t getChannel() { return channel; } uint8_t getButton() { return btn; } @@ -59,149 +43,6 @@ class FProtoWeatherBase { decode_data = decode_data << 1 | bit; decode_count_bit++; } - uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { - uint32_t result = 0; - for (size_t i = 0; i < size; ++i) { - result += message[i]; - } - return (uint8_t)result; - } - uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x6996 >> byte) & 1; - } - uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { - uint8_t result = 0; - for (size_t i = 0; i < size; ++i) { - result ^= subghz_protocol_blocks_parity8(message[i]); - } - return result; - } - uint8_t subghz_protocol_blocks_lfsr_digest8( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key) { - uint8_t sum = 0; - for (size_t byte = 0; byte < size; ++byte) { - uint8_t data = message[byte]; - for (int i = 7; i >= 0; --i) { - // XOR key into sum if data bit is set - if ((data >> i) & 1) sum ^= key; - // roll the key right (actually the LSB is dropped here) - // and apply the gen (needs to include the dropped LSB as MSB) - if (key & 1) - key = (key >> 1) ^ gen; - else - key = (key >> 1); - } - } - return sum; - } - float locale_fahrenheit_to_celsius(float temp_f) { - return (temp_f - 32.f) / 1.8f; - } - bool manchester_advance( - ManchesterState state, - ManchesterEvent event, - ManchesterState* next_state, - bool* data) { - bool result = false; - ManchesterState new_state; - - if (event == ManchesterEventReset) { - new_state = manchester_reset_state; - } else { - new_state = (ManchesterState)(transitions[state] >> event & 0x3); - if (new_state == state) { - new_state = manchester_reset_state; - } else { - if (new_state == ManchesterStateMid0) { - if (data) *data = false; - result = true; - } else if (new_state == ManchesterStateMid1) { - if (data) *data = true; - result = true; - } - } - } - - *next_state = new_state; - return result; - } - uint8_t subghz_protocol_blocks_crc4( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = init << 4; // LSBs are unused - uint8_t poly = polynomial << 4; - uint8_t bit; - - while (size--) { - remainder ^= *message++; - for (bit = 0; bit < 8; bit++) { - if (remainder & 0x80) { - remainder = (remainder << 1) ^ poly; - } else { - remainder = (remainder << 1); - } - } - } - return remainder >> 4 & 0x0f; // discard the LSBs - } - uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key) { - uint8_t sum = 0; - // Process message from last byte to first byte (reflected) - for (int byte = size - 1; byte >= 0; --byte) { - uint8_t data = message[byte]; - // Process individual bits of each byte (reflected) - for (uint8_t i = 0; i < 8; ++i) { - // XOR key into sum if data bit is set - if ((data >> i) & 1) { - sum ^= key; - } - // roll the key left (actually the LSB is dropped here) - // and apply the gen (needs to include the dropped lsb as MSB) - if (key & 0x80) - key = (key << 1) ^ gen; - else - key = (key << 1); - } - } - return sum; - } - uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { - uint64_t reverse_key = 0; - for (uint8_t i = 0; i < bit_count; i++) { - reverse_key = reverse_key << 1 | bit_read(key, i); - } - return reverse_key; - } - uint8_t subghz_protocol_blocks_crc8( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = init; - - for (size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte]; - for (uint8_t bit = 0; bit < 8; ++bit) { - if (remainder & 0x80) { - remainder = (remainder << 1) ^ polynomial; - } else { - remainder = (remainder << 1); - } - } - } - return remainder; - } // General weather data holder uint8_t sensorType = FPW_Invalid; @@ -209,11 +50,10 @@ class FProtoWeatherBase { float temp = WS_NO_TEMPERATURE; uint8_t humidity = WS_NO_HUMIDITY; uint8_t battery_low = WS_NO_BATT; - uint32_t timestamp = 0; uint8_t channel = WS_NO_CHANNEL; uint8_t btn = WS_NO_BTN; - // inner logic stuff, also for flipper compatibility. //todo revork a bit, so won't have dupes (decode_data + data, ..), but check if any of the protos uses it in the same time or not. (shouldn't) + // inner logic stuff, also for flipper compatibility. SubGhzProtocolDecoderBaseRxCallback callback = NULL; uint16_t header_count = 0; uint8_t parser_step = 0; @@ -223,8 +63,6 @@ class FProtoWeatherBase { uint64_t decode_data = 0; uint32_t decode_count_bit = 0; ManchesterState manchester_saved_state = ManchesterStateMid1; - static const ManchesterState manchester_reset_state = ManchesterStateMid1; - static inline const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011}; }; #endif \ No newline at end of file diff --git a/firmware/baseband/fprotos/weatherprotos.hpp b/firmware/baseband/fprotos/weatherprotos.hpp index dcbccf54..ab0ca1d5 100644 --- a/firmware/baseband/fprotos/weatherprotos.hpp +++ b/firmware/baseband/fprotos/weatherprotos.hpp @@ -3,6 +3,9 @@ This is the protocol list handler. It holds an instance of all known protocols. So include here the .hpp, and add a new element to the protos vector in the constructor. That's all you need to do here if you wanna add a new proto. @htotoo */ + +#include "fprotolistgeneral.hpp" + #include "w-nexus-th.hpp" #include "w-acurite592txr.hpp" #include "w-acurite606tx.hpp" @@ -26,10 +29,10 @@ So include here the .hpp, and add a new element to the protos vector in the cons #include #include "portapack_shared_memory.hpp" -#ifndef __FPROTO_PROTOLIST_H__ -#define __FPROTO_PROTOLIST_H__ +#ifndef __FPROTO_PROTOLISTWTH_H__ +#define __FPROTO_PROTOLISTWTH_H__ -class WeatherProtos { +class WeatherProtos : public FProtoListGeneral { public: WeatherProtos() { // add protos diff --git a/firmware/baseband/proc_weather.cpp b/firmware/baseband/proc_weather.cpp index d8010c6e..45570499 100644 --- a/firmware/baseband/proc_weather.cpp +++ b/firmware/baseband/proc_weather.cpp @@ -37,7 +37,7 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) { { if (currentDuration < UINT32_MAX) currentDuration += usperTick; } else { // called on change, so send the last duration and dir. - protoList.feed(currentHiLow, currentDuration / 1000); + if (protoList) protoList->feed(currentHiLow, currentDuration / 1000); currentDuration = usperTick; currentHiLow = meashl; } @@ -54,11 +54,23 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) { void WeatherProcessor::on_message(const Message* const message) { if (message->id == Message::ID::WeatherRxConfigure) - configure(*reinterpret_cast(message)); + configure(*reinterpret_cast(message)); } -void WeatherProcessor::configure(const WeatherRxConfigureMessage& message) { +void WeatherProcessor::configure(const SubGhzFPRxConfigureMessage& message) { (void)message; + + modulation = message.modulation; // NIY + + if (protoMode != message.protoMode) { + // change it. + FProtoListGeneral* tmp = protoList; + protoList = NULL; + protoMode = message.protoMode; + if (tmp) free(tmp); // takes some time + if (protoMode == 0) protoList = new WeatherProtos(); + if (protoMode == 1) protoList = new SubGhzDProtos(); + } configured = true; } diff --git a/firmware/baseband/proc_weather.hpp b/firmware/baseband/proc_weather.hpp index 61eeb83a..2bcf2aa4 100644 --- a/firmware/baseband/proc_weather.hpp +++ b/firmware/baseband/proc_weather.hpp @@ -32,6 +32,7 @@ #include "message.hpp" #include "fprotos/weatherprotos.hpp" +#include "fprotos/subghzdprotos.hpp" class WeatherProcessor : public BasebandProcessor { public: @@ -46,12 +47,14 @@ class WeatherProcessor : public BasebandProcessor { bool currentHiLow = false; bool configured{false}; - // for debug + uint8_t modulation = 255; // 0 AM, 1 FM 255 = Not set + uint8_t protoMode = 255; // 0 weather, 1 subghzd, 255 = Not set + // for threshold uint32_t cnt = 0; uint32_t tm = 0; - WeatherProtos protoList{}; // holds all the protocols we can parse - void configure(const WeatherRxConfigureMessage& message); + FProtoListGeneral* protoList = NULL; // holds all the protocols we can parse + void configure(const SubGhzFPRxConfigureMessage& message); /* NB: Threads should be the last members in the class definition. */ BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive}; diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 3b0c0503..950c82d9 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -117,6 +117,7 @@ class Message { BTLETxConfigure = 59, WeatherRxConfigure = 60, WeatherData = 61, + SubGhzDData = 62, MAX }; @@ -1238,12 +1239,14 @@ class SpectrumPainterBufferConfigureResponseMessage : public Message { SpectrumPainterFIFO* fifo{nullptr}; }; -class WeatherRxConfigureMessage : public Message { +class SubGhzFPRxConfigureMessage : public Message { public: - constexpr WeatherRxConfigureMessage() - : Message{ID::WeatherRxConfigure} { + constexpr SubGhzFPRxConfigureMessage(uint8_t protoMode = 0, uint8_t modulation = 0) + : Message{ID::WeatherRxConfigure}, protoMode{protoMode}, modulation{modulation} { // todoh give some more info } + uint8_t protoMode = 0; // 0 weather, 1 subhgzd + uint8_t modulation = 0; // 0 am, 1 fm }; class WeatherDataMessage : public Message { @@ -1274,4 +1277,17 @@ class WeatherDataMessage : public Message { uint8_t btn = 0xFF; }; +class SubGhzDDataMessage : public Message { + public: + constexpr SubGhzDDataMessage( + uint8_t sensorType = 0, + uint32_t id = 0xFFFFFFFF) + : Message{ID::SubGhzDData}, + sensorType{sensorType}, + id{id} { + } + uint8_t sensorType = 0; + uint32_t id = 0xFFFFFFFF; // todo add results too! +}; + #endif /*__MESSAGE_H__*/