Skip to content

Commit

Permalink
Ensure message delivery for NASA packets
Browse files Browse the repository at this point in the history
* Queue outgoing packets and send them during loop()
* Only send packets during relatively quiet time
* Do not send new packets until we receive an ack for the one already sent
* Retry sending packet if no ack received
* Make absolutely sure that there is no incoming data available or there is no more data expected to arrive before sending
* Attempt to recover packets from incoming data if stars aligned in a way that all of the above failed
* Modify logging to be able to easily change log levels during development
* Optimize passing data buffers back and forth to prevent extra copies
  • Loading branch information
atanasenko committed Feb 16, 2024
1 parent 102a957 commit d6747cb
Show file tree
Hide file tree
Showing 12 changed files with 359 additions and 245 deletions.
9 changes: 9 additions & 0 deletions components/samsung_ac/log.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace esphome
{
namespace samsung_ac
{
bool debug_log_packets = false;
bool debug_log_raw_bytes = false;

} // namespace samsung_ac
} // namespace esphome
28 changes: 28 additions & 0 deletions components/samsung_ac/log.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "esphome/core/log.h"
#include "util.h"

#define LOGV(...) ESP_LOGV(TAG, __VA_ARGS__)
#define LOGD(...) ESP_LOGD(TAG, __VA_ARGS__)
#define LOGI(...) ESP_LOGI(TAG, __VA_ARGS__)
#define LOGW(...) ESP_LOGW(TAG, __VA_ARGS__)
#define LOGE(...) ESP_LOGE(TAG, __VA_ARGS__)
#define LOGC(...) ESP_LOGCONFIG(TAG, __VA_ARGS__)

#define LOG_STATE(...) LOGI(__VA_ARGS__)
#define LOG_RAW_SEND(inter, ...) ({ if (debug_log_raw_bytes) LOGW("<< +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); })
#define LOG_RAW(inter, ...) ({ if (debug_log_raw_bytes) LOGD(">> +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); })
#define LOG_RAW_DISCARDED(inter, ...) ({if (debug_log_raw_bytes) LOGV(">> +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); })
#define LOG_PACKET_SEND(msg, packet) LOGI("%s: %s", msg, packet.to_string().c_str())
#define LOG_PACKET_RECV(msg, packet) ({ if (debug_log_packets) LOGD("%s: %s", msg, packet.to_string().c_str()); })

namespace esphome
{
namespace samsung_ac
{
extern bool debug_log_packets;
extern bool debug_log_raw_bytes;

} // namespace samsung_ac
} // namespace esphome
69 changes: 25 additions & 44 deletions components/samsung_ac/protocol.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <algorithm>
#include "esphome/core/log.h"
#include "protocol.h"
#include "util.h"
Expand All @@ -8,62 +9,42 @@ namespace esphome
{
namespace samsung_ac
{
bool debug_log_packets = false;
bool debug_log_raw_bytes = false;
uint16_t skip_data(std::vector<uint8_t> &data, int from)
{
// Skip over filler data or broken packets
// Example:
// 320037d8fedbff81cb7ffbfd808d00803f008243000082350000805e008031008248ffff801a0082d400000b6a34 f9f6f1f9f9 32000e200002
// Note that first part is a mangled packet, then regular filler data, then start of a new packet
// and that one new proper packet will continue with the next data read

// find next value of 0x32, and retry with that one
return std::find(data.begin() + from, data.end(), 0x32) - data.begin();
}

// This functions is designed to run after a new value was added
// to the data vector. One by one.
DataResult process_data(std::vector<uint8_t> &data, MessageTarget *target)
DecodeResult process_data(std::vector<uint8_t> &data, MessageTarget *target)
{
if (data.size() > 1500)
{
ESP_LOGV(TAG, "current packat exceeds the size limits: %s", bytes_to_hex(data).c_str());
return DataResult::Clear;
}
if (*data.begin() != 0x32)
return { DecodeResultType::Discard, skip_data(data, 0) };

// Check if its a decodeable NonNASA packat
if (data.size() == 7 /* duplicate addr package */ || data.size() == 14 /* generic package */)
auto result = try_decode_non_nasa_packet(data);
if (result.type == DecodeResultType::Processed)
{
const auto result = try_decode_non_nasa_packet(data);
if (result == DecodeResult::Ok)
{
if (debug_log_raw_bytes)
{
ESP_LOGW(TAG, "RAW: %s", bytes_to_hex(data).c_str());
}

process_non_nasa_packet(target);
return DataResult::Clear;
}
process_non_nasa_packet(target);
return result;
}

const auto result = try_decode_nasa_packet(data);
if (result == DecodeResult::SizeDidNotMatch || result == DecodeResult::UnexpectedSize)
return DataResult::Fill;

if (debug_log_raw_bytes)
result = try_decode_nasa_packet(data);
if (result.type == DecodeResultType::Processed)
{
ESP_LOGV(TAG, "RAW: %s", bytes_to_hex(data).c_str());
process_nasa_packet(target);
}

if (result == DecodeResult::InvalidStartByte)
{
ESP_LOGV(TAG, "invalid start byte: %s", bytes_to_hex(data).c_str());
return DataResult::Clear;
}
else if (result == DecodeResult::InvalidEndByte)
else if(result.type == DecodeResultType::Discard)
{
ESP_LOGV(TAG, "invalid end byte: %s", bytes_to_hex(data).c_str());
return DataResult::Clear;
return { DecodeResultType::Discard, skip_data(data, 1) };
}
else if (result == DecodeResult::CrcError)
{
// is logge dwithin decoder
return DataResult::Clear;
}

process_nasa_packet(target);
return DataResult::Clear;
return result;
}

bool is_nasa_address(const std::string &address)
Expand Down
29 changes: 12 additions & 17 deletions components/samsung_ac/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ namespace esphome
{
namespace samsung_ac
{
extern bool debug_log_packets;
extern bool debug_log_raw_bytes;
enum class DecodeResultType
{
Fill = 1,
Discard = 2,
Processed = 3
};

enum class DecodeResult
struct DecodeResult
{
Ok = 0,
InvalidStartByte = 1,
InvalidEndByte = 2,
SizeDidNotMatch = 3,
UnexpectedSize = 4,
CrcError = 5
DecodeResultType type;
uint16_t bytes; // when Processed
};

enum class Mode
Expand Down Expand Up @@ -63,7 +63,8 @@ namespace esphome
{
public:
virtual uint32_t get_miliseconds() = 0;
virtual void publish_data(std::vector<uint8_t> &data) = 0;
virtual void publish_data(uint8_t id, std::vector<uint8_t> &&data) = 0;
virtual void ack_data(uint8_t id) = 0;
virtual void register_address(const std::string address) = 0;
virtual void set_power(const std::string address, bool value) = 0;
virtual void set_room_temperature(const std::string address, float value) = 0;
Expand Down Expand Up @@ -95,13 +96,7 @@ namespace esphome
virtual void publish_request(MessageTarget *target, const std::string &address, ProtocolRequest &request) = 0;
};

enum class DataResult
{
Fill = 0,
Clear = 1
};

DataResult process_data(std::vector<uint8_t> &data, MessageTarget *target);
DecodeResult process_data(std::vector<uint8_t> &data, MessageTarget *target);

Protocol *get_protocol(const std::string &address);

Expand Down
Loading

0 comments on commit d6747cb

Please sign in to comment.