From 7895cd92cd8d05d72fdb6631bd17151aff207e2e Mon Sep 17 00:00:00 2001 From: cvwillegen Date: Tue, 12 Apr 2022 21:39:38 +0200 Subject: [PATCH] Remote base pronto receive (#2826) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../remote_base/pronto_protocol.cpp | 101 +++++++++++++++++- .../components/remote_base/pronto_protocol.h | 12 ++- 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 4f6ace720c..a2b1a16e07 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -40,6 +40,24 @@ namespace remote_base { static const char *const TAG = "remote.pronto"; +bool ProntoData::operator==(const ProntoData &rhs) const { + std::vector data1 = encode_pronto(data); + std::vector data2 = encode_pronto(rhs.data); + + uint32_t total_diff = 0; + // Don't need to check the last one, it's the large gap at the end. + for (std::vector::size_type i = 0; i < data1.size() - 1; ++i) { + int diff = data2[i] - data1[i]; + diff *= diff; + if (diff > 9) + return false; + + total_diff += diff; + } + + return total_diff <= data1.size() * 3; +} + // DO NOT EXPORT from this file static const uint16_t MICROSECONDS_T_MAX = 0xFFFFU; static const uint16_t LEARNED_TOKEN = 0x0000U; @@ -52,6 +70,7 @@ static const uint32_t REFERENCE_FREQUENCY = 4145146UL; static const uint16_t FALLBACK_FREQUENCY = 64767U; // To use with frequency = 0; static const uint32_t MICROSECONDS_IN_SECONDS = 1000000UL; static const uint16_t PRONTO_DEFAULT_GAP = 45000; +static const uint16_t MARK_EXCESS_MICROS = 20; static uint16_t to_frequency_k_hz(uint16_t code) { if (code == 0) @@ -107,7 +126,7 @@ void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::vector encode_pronto(const std::string &str) { size_t len = str.length() / (DIGITS_IN_PRONTO_NUMBER + 1) + 1; std::vector data; const char *p = str.c_str(); @@ -122,12 +141,90 @@ void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::string &st data.push_back(x); // If input is conforming, there can be no overflow! p = *endptr; } + + return data; +} + +void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::string &str) { + std::vector data = encode_pronto(str); send_pronto_(dst, data); } void ProntoProtocol::encode(RemoteTransmitData *dst, const ProntoData &data) { send_pronto_(dst, data.data); } -optional ProntoProtocol::decode(RemoteReceiveData src) { return {}; } +uint16_t ProntoProtocol::effective_frequency_(uint16_t frequency) { + return frequency > 0 ? frequency : FALLBACK_FREQUENCY; +} + +uint16_t ProntoProtocol::to_timebase_(uint16_t frequency) { + return MICROSECONDS_IN_SECONDS / effective_frequency_(frequency); +} + +uint16_t ProntoProtocol::to_frequency_code_(uint16_t frequency) { + return REFERENCE_FREQUENCY / effective_frequency_(frequency); +} + +std::string ProntoProtocol::dump_digit_(uint8_t x) { + return std::string(1, (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)))); +} + +std::string ProntoProtocol::dump_number_(uint16_t number, bool end /* = false */) { + std::string num; + + for (uint8_t i = 0; i < DIGITS_IN_PRONTO_NUMBER; ++i) { + uint8_t shifts = BITS_IN_HEXADECIMAL * (DIGITS_IN_PRONTO_NUMBER - 1 - i); + num += dump_digit_((number >> shifts) & HEX_MASK); + } + + if (!end) + num += ' '; + + return num; +} + +std::string ProntoProtocol::dump_duration_(uint32_t duration, uint16_t timebase, bool end /* = false */) { + return dump_number_((duration + timebase / 2) / timebase, end); +} + +std::string ProntoProtocol::compensate_and_dump_sequence_(std::vector *data, uint16_t timebase) { + std::string out; + + for (std::vector::size_type i = 0; i < data->size() - 1; i++) { + int32_t t_length = data->at(i); + uint32_t t_duration; + if (t_length > 0) { + // Mark + t_duration = t_length - MARK_EXCESS_MICROS; + } else { + t_duration = -t_length + MARK_EXCESS_MICROS; + } + out += dump_duration_(t_duration, timebase); + } + + // append minimum gap + out += dump_duration_(PRONTO_DEFAULT_GAP, timebase, true); + + return out; +} + +optional ProntoProtocol::decode(RemoteReceiveData src) { + ProntoData out; + + uint16_t frequency = 38000U; + std::vector *data = src.get_raw_data(); + std::string prontodata; + + prontodata += dump_number_(frequency > 0 ? LEARNED_TOKEN : LEARNED_NON_MODULATED_TOKEN); + prontodata += dump_number_(to_frequency_code_(frequency)); + prontodata += dump_number_((data->size() + 1) / 2); + prontodata += dump_number_(0); + uint16_t timebase = to_timebase_(frequency); + prontodata += compensate_and_dump_sequence_(data, timebase); + + out.data = prontodata; + + return out; +} void ProntoProtocol::dump(const ProntoData &data) { ESP_LOGD(TAG, "Received Pronto: data=%s", data.data.c_str()); } diff --git a/esphome/components/remote_base/pronto_protocol.h b/esphome/components/remote_base/pronto_protocol.h index e96511383f..291bb8a99b 100644 --- a/esphome/components/remote_base/pronto_protocol.h +++ b/esphome/components/remote_base/pronto_protocol.h @@ -6,10 +6,12 @@ namespace esphome { namespace remote_base { +std::vector encode_pronto(const std::string &str); + struct ProntoData { std::string data; - bool operator==(const ProntoData &rhs) const { return data == rhs.data; } + bool operator==(const ProntoData &rhs) const; }; class ProntoProtocol : public RemoteProtocol { @@ -17,6 +19,14 @@ class ProntoProtocol : public RemoteProtocol { void send_pronto_(RemoteTransmitData *dst, const std::vector &data); void send_pronto_(RemoteTransmitData *dst, const std::string &str); + uint16_t effective_frequency_(uint16_t frequency); + uint16_t to_timebase_(uint16_t frequency); + uint16_t to_frequency_code_(uint16_t frequency); + std::string dump_digit_(uint8_t x); + std::string dump_number_(uint16_t number, bool end = false); + std::string dump_duration_(uint32_t duration, uint16_t timebase, bool end = false); + std::string compensate_and_dump_sequence_(std::vector *data, uint16_t timebase); + public: void encode(RemoteTransmitData *dst, const ProntoData &data) override; optional decode(RemoteReceiveData src) override;