From ff98d5903997fe1fb1c9cc6b2b2f418ca33c288e Mon Sep 17 00:00:00 2001 From: Mitchell Date: Tue, 30 Jun 2026 15:03:00 -0500 Subject: [PATCH] init --- CMakeLists.txt | 5 ++ blk_box_common.cpp | 132 +++++++++++++++++++++++++++++++++++++ idf_component.yml | 17 +++++ include/blk_box_common.hpp | 62 +++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 blk_box_common.cpp create mode 100644 idf_component.yml create mode 100644 include/blk_box_common.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bcf55fa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "blk_box_common.cpp" + INCLUDE_DIRS "include" "." + # PRIV_REQUIRES +) diff --git a/blk_box_common.cpp b/blk_box_common.cpp new file mode 100644 index 0000000..8a77466 --- /dev/null +++ b/blk_box_common.cpp @@ -0,0 +1,132 @@ +#include "blk_box_common.hpp" +#include +#include +#include +#include +#include +#include +#include + +const static char TAG[] = "blk_box_common"; + +size_t BlkBoxNowPubPacket::serialize_size() { + switch (this->packet_type) { + case BlkBoxNowPubPacketType::NONE: + return -1; + case BlkBoxNowPubPacketType::DISCOVERY: + return BlkBoxNowPubPacket::DISCOVERY_PACKET_SIZE; + } + return -1; +} + +std::vector BlkBoxNowPubPacket::serialize() { + size_t size = this->serialize_size(); + if (size == -1) { + return std::vector(0); + } + auto buf = std::vector(); + buf.reserve(size); + + this->serialize_into(buf); + return buf; +} + +/// Serializes the packet into the given vector. +size_t BlkBoxNowPubPacket::serialize_into(std::vector& buf) { + size_t initial_size = buf.size(); + size_t size = this->serialize_size(); + if (size == -1) { + return 0; + } + buf.reserve(initial_size + size); + + // push magic number + static_assert(sizeof(PUB_MAGIC_NUMBER) == 4, "expect magic number to be 4 bytes"); + buf.push_back((PUB_MAGIC_NUMBER >> 24) & 0xFF); + buf.push_back((PUB_MAGIC_NUMBER >> 16) & 0xFF); + buf.push_back((PUB_MAGIC_NUMBER >> 8) & 0xFF); + buf.push_back((PUB_MAGIC_NUMBER >> 0) & 0xFF); + + // push packet type + static_assert(sizeof(this->packet_type) == 1, "expect type field to be 1 byte"); + buf.push_back(static_cast(this->packet_type)); + + switch (this->packet_type) { + case BlkBoxNowPubPacketType::NONE: + break; + case BlkBoxNowPubPacketType::DISCOVERY: + static_assert(sizeof(this->packet_data.discovery) == 2, "expect discovery data to be 2 byte"); + uint16_t data = static_cast(this->packet_data.discovery.expantion_type); + buf.push_back((data >> 8) & 0xFF); + buf.push_back((data >> 0) & 0xFF); + break; + } + + assert(buf.size() - initial_size == size); + return size; +} + +/// Deserializes a packet from the given bytes. +std::optional BlkBoxNowPubPacket::deserialize(std::span bytes) { + // first check for magic number and type + if (bytes.size() < 5) { + // not enough room for magic number and type + return std::nullopt; + } + uint32_t magic_number = + (uint32_t)(bytes[0] << 24) | + (uint32_t)(bytes[1] << 16) | + (uint32_t)(bytes[2] << 8) | + (uint32_t)(bytes[3] << 0); + + if (magic_number != PUB_MAGIC_NUMBER) { + ESP_LOGD(TAG, "packet missing magic number. discarding"); + return std::nullopt; + } + + // magic number matches! this is a blk_box pub packet! + BlkBoxNowPubPacket packet; + + uint8_t packet_type = bytes[4]; + packet.packet_type = static_cast(packet_type); + if (packet.packet_type > BlkBoxNowPubPacketType::MAX) { + ESP_LOGW(TAG, "BlkBoxPubPacketType was NONE! discarding"); + return std::nullopt; + } + if (packet.packet_type > BlkBoxNowPubPacketType::MAX) { + ESP_LOGW(TAG, "BlkBoxPubPacketType not in range! discarding"); + return std::nullopt; + } + + // right now, the size is dependent only on the `packet_type` field, so we can + // check to see if the packet is big enough now. + size_t expected_size = packet.serialize_size(); + if (bytes.size() < expected_size) { + ESP_LOGW(TAG, "packet not long enough for pub packet of type %d", packet_type); + return std::nullopt; + } + if (bytes.size() > expected_size) { + ESP_LOGW(TAG, "pub packet longer than expected for packet of type %d. decoding anyway", packet_type); + } + + switch (packet.packet_type) { + case BlkBoxNowPubPacketType::NONE: + break; + case BlkBoxNowPubPacketType::DISCOVERY: + uint16_t device = ((uint16_t)bytes[5] << 8) | ((uint16_t)bytes[6]); + packet.packet_data.discovery.expantion_type = static_cast(device); + if (packet.packet_data.discovery.expantion_type > BlkBoxExpantion::MAX) { + ESP_LOGW(TAG, "BlkBoxExpantion not in range! discarding"); + return std::nullopt; + } + break; + } + return packet; +} + +BlkBoxNowPubPacket BlkBoxNowPubPacket::new_discovery(BlkBoxNowPubDiscoveryData data) { + BlkBoxNowPubPacket packet; + packet.packet_type = BlkBoxNowPubPacketType::DISCOVERY; + packet.packet_data.discovery = data; + return packet; +} diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..e143244 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,17 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: '>=6.0.0' + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true + \ No newline at end of file diff --git a/include/blk_box_common.hpp b/include/blk_box_common.hpp new file mode 100644 index 0000000..8be6db1 --- /dev/null +++ b/include/blk_box_common.hpp @@ -0,0 +1,62 @@ +/// Types common across the blk_box ecosystem + +#ifndef _BLK_BOX_COMMON_H +#define _BLK_BOX_COMMON_H + +#include +#include +#include +#include +#include + +/// The different expantions to the BLK_BOX. +enum class BlkBoxExpantion: uint16_t { + NONE = 0, + WIRES = 1, + TENS = 2, + MAX = 2, +}; + +/// The packet type for `BlkBoxNowPubPacket`s. +enum class BlkBoxNowPubPacketType: uint8_t { + NONE = 0, + DISCOVERY = 1, + MAX = 1, +}; + +/// The data associated with the `DISCOVERY` type. +struct BlkBoxNowPubDiscoveryData { + BlkBoxExpantion expantion_type{}; +}; + +union BlkBoxNowPubPacketData { + BlkBoxNowPubDiscoveryData discovery; +}; + +struct BlkBoxNowPubPacket { + const static uint32_t PUB_MAGIC_NUMBER = 0x8934'DBE3; + // DISCOVERY packet size + const static size_t DISCOVERY_PACKET_SIZE = sizeof(PUB_MAGIC_NUMBER) + sizeof(BlkBoxNowPubPacketType) + sizeof(BlkBoxNowPubDiscoveryData); + static_assert(DISCOVERY_PACKET_SIZE == 7, "Discovery packet should be 7 bytes"); + + + /// The type of packet this is. + BlkBoxNowPubPacketType packet_type{}; + /// The data held by the packet. + BlkBoxNowPubPacketData packet_data{}; + + /// The size of the serialized packet (in bytes). + size_t serialize_size(); + /// Serializes the packet to a vector. + std::vector serialize(); + /// Serializes the packet into the given vector. + size_t serialize_into(std::vector& buf); + /// Deserializes a packet from the given bytes. + static std::optional deserialize(std::span bytes); + + /// Constructs an empty packet. + BlkBoxNowPubPacket() = default; + static BlkBoxNowPubPacket new_discovery(BlkBoxNowPubDiscoveryData data); +}; + +#endif /* _BLK_BOX_COMMON_H */