diff --git a/CMakeLists.txt b/CMakeLists.txt index dfd8949..759bee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ idf_component_register( INCLUDE_DIRS "include" "." REQUIRES lvgl + blk_box_common PRIV_REQUIRES led_strip esp_driver_gpio diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index e47f745..89d431c 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -1,5 +1,5 @@ set(SOURCES - "bbnow.cpp" + "bbnow_old.cpp" "char_lcd_headers.cpp" "char_lcd.cpp" "helpers.cpp" @@ -12,6 +12,7 @@ set(SOURCES "ssegs.cpp" "tft.cpp" "tm1640.cpp" + "wifi.cpp" ) target_sources(${COMPONENT_LIB} PRIVATE ${SOURCES}) diff --git a/drivers/bbnow.cpp b/drivers/bbnow.cpp deleted file mode 100644 index 0ed8e84..0000000 --- a/drivers/bbnow.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "blk_box_drivers/bbnow.hpp" -#include "esp_now.h" -#include "esp_log.h" -#include "esp_mac.h" -#include - -const static char TAG[] = "bbnow"; - -static void example_espnow_send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status); -static void example_espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len); - -/* ESPNOW sending or receiving callback function is called in WiFi task. - * Users should not do lengthy operations from this task. Instead, post - * necessary data to a queue and handle it from a lower priority task. */ -static void example_espnow_send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) -{ - if (tx_info == NULL) { - ESP_LOGE(TAG, "Send cb arg error"); - return; - } - ESP_LOGI(TAG, "sending espnow packet of size: %d to " MACSTR, tx_info->data_len, MAC2STR(tx_info->des_addr)); -} - -static void example_espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len) -{ - uint8_t * src_addr = recv_info->src_addr; - uint8_t * des_addr = recv_info->des_addr; - - if (src_addr == NULL || data == NULL || len <= 0) { - ESP_LOGE(TAG, "Receive cb arg error"); - return; - } - - ESP_LOGI(TAG, "got espnow packet of size: %d from " MACSTR " to " MACSTR, MAC2STR(src_addr), MAC2STR(des_addr)); - - // if (IS_BROADCAST_ADDR(des_addr)) { - // /* If added a peer with encryption before, the receive packets may be - // * encrypted as peer-to-peer message or unencrypted over the broadcast channel. - // * Users can check the destination address to distinguish it. - // */ - // ESP_LOGD(TAG, "Receive broadcast ESPNOW data"); - // } else { - // ESP_LOGD(TAG, "Receive unicast ESPNOW data"); - // } -} - - -// TODO: add "initializing _____..." -// and "____ initialized!" logs to all driver init functions. -void init_espnow(void) { - ESP_ERROR_CHECK( esp_now_init() ); - ESP_ERROR_CHECK( esp_now_register_send_cb(example_espnow_send_cb) ); - ESP_ERROR_CHECK( esp_now_register_recv_cb(example_espnow_recv_cb) ); - - /* Set primary master key. */ - // TODO: add provisions for encryption - // ESP_ERROR_CHECK( esp_now_set_pmk(ESPNOW_PMK)); - - // Add broadcast peer information to peer list, allowing us to send to it - esp_now_peer_info_t peer = {0}; - peer.channel = BBNOW_DEFAULT_CHANNEL; - peer.ifidx = WIFI_IF_AP; - peer.encrypt = false; - memcpy(peer.peer_addr, BROADCAST_MAC, ESP_NOW_ETH_ALEN); - ESP_ERROR_CHECK( esp_now_add_peer(&peer) ); -} diff --git a/drivers/bbnow_old.cpp b/drivers/bbnow_old.cpp new file mode 100644 index 0000000..6644034 --- /dev/null +++ b/drivers/bbnow_old.cpp @@ -0,0 +1,234 @@ +#include "blk_box_drivers/bbnow_old.hpp" +#include "esp_now.h" +#include "esp_log.h" +#include "esp_mac.h" +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +// TODO: remove this file and the hpp file associated with it. + +const static char TAG[] = "bbnow"; + +#define BBNOW_QUEUE_LEN 16 +#define BBNOW_TASK_STACK (4096) +#define BBNOW_TASK_PRIO 5 + +static QueueHandle_t g_bbnow_queue = NULL; +static TaskHandle_t g_bbnow_task_handle = NULL; + +// Wire-format bytes of the pub magic number (big-endian), used to classify +// received packets without depending on host byte order of the uint32_t. +static const uint8_t k_pub_magic_bytes[4] = { + static_cast((BBNowPubPacket::PUB_MAGIC_NUMBER >> 24) & 0xFF), + static_cast((BBNowPubPacket::PUB_MAGIC_NUMBER >> 16) & 0xFF), + static_cast((BBNowPubPacket::PUB_MAGIC_NUMBER >> 8) & 0xFF), + static_cast((BBNowPubPacket::PUB_MAGIC_NUMBER >> 0) & 0xFF), +}; + +static void espnow_send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) +{ + if (tx_info == NULL) { + ESP_LOGE(TAG, "Send cb arg error"); + return; + } + ESP_LOGI(TAG, "sending espnow packet of size: %d to " MACSTR, tx_info->data_len, MAC2STR(tx_info->des_addr)); +} + +static void espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len) +{ + uint8_t * src_addr = recv_info->src_addr; + uint8_t * des_addr = recv_info->des_addr; + + if (src_addr == NULL || data == NULL || len <= 0) { + ESP_LOGE(TAG, "Receive cb arg error"); + return; + } + + ESP_LOGI(TAG, "got espnow packet of size: %d from " MACSTR " to " MACSTR, len, MAC2STR(src_addr), MAC2STR(des_addr)); + + // Hand the raw bytes to the queue; the bbnow task will classify and + // deserialize them. If the queue isn't up yet, just drop it. + if (g_bbnow_queue != NULL) { + bbnow_enqueue_received(src_addr, data, static_cast(len)); + } +} + +esp_err_t bbnow_enqueue_received(const uint8_t* src, const uint8_t* data, size_t len) { + if (g_bbnow_queue == NULL) { + return ESP_ERR_INVALID_STATE; + } + if (data == NULL || len == 0 || len > ESP_NOW_MAX_DATA_LEN) { + return ESP_ERR_INVALID_ARG; + } + + BBNowQueueItem item{}; + item.type = BBNowQueueItemType::RX; + item.len = len; + memcpy(item.payload, data, len); + if (src != NULL) { + memcpy(item.addr, src, ESP_NOW_ETH_ALEN); + } else { + memcpy(item.addr, BROADCAST_MAC, ESP_NOW_ETH_ALEN); + } + + // Classify by sniffing the leading magic number so the handler knows + // whether to treat the payload as a pub or (future) priv packet. + if (len >= sizeof(k_pub_magic_bytes) && + memcmp(data, k_pub_magic_bytes, sizeof(k_pub_magic_bytes)) == 0) { + item.kind = BBNowPacketKind::PUB; + } else { + item.kind = BBNowPacketKind::PRIV; + } + + if (xQueueSend(g_bbnow_queue, &item, pdMS_TO_TICKS(100)) != pdTRUE) { + ESP_LOGE(TAG, "failed to enqueue received packet"); + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t bbnow_send_pub(const BBNowPubPacket& pkt, const uint8_t* dest) { + if (dest == NULL) { + dest = BROADCAST_MAC; + } + if (g_bbnow_queue == NULL) { + ESP_LOGE(TAG, "bbnow not ready; call bbnow_start_task() first"); + return ESP_ERR_INVALID_STATE; + } + + auto buf = pkt.serialize(); + if (buf.empty()) { + ESP_LOGE(TAG, "pub packet failed to serialize"); + return ESP_ERR_INVALID_ARG; + } + if (buf.size() > ESP_NOW_MAX_DATA_LEN) { + ESP_LOGE(TAG, "pub packet too large for esp_now (%u > %d)", buf.size(), ESP_NOW_MAX_DATA_LEN); + return ESP_ERR_INVALID_SIZE; + } + + BBNowQueueItem item{}; + item.type = BBNowQueueItemType::TX; + item.kind = BBNowPacketKind::PUB; + item.len = buf.size(); + memcpy(item.payload, buf.data(), buf.size()); + memcpy(item.addr, dest, ESP_NOW_ETH_ALEN); + + if (xQueueSend(g_bbnow_queue, &item, pdMS_TO_TICKS(100)) != pdTRUE) { + ESP_LOGE(TAG, "failed to enqueue pub packet for tx"); + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t bbnow_send_priv(const uint8_t* dest) { + // SCAFFOLD: BBNOW private packets are not implemented yet. + // + // Planned implementation mirrors `bbnow_send_pub`: serialize a + // `BBNowPrivPacket` (once defined in blk_box_common) into `item.payload`, + // set `item.kind = BBNowPacketKind::PRIV`, and enqueue a TX item. The + // task and RX handler below already route `PRIV` items correctly, so only + // this function and the `BBNowPacketKind::PRIV` branch of + // `bbnow_handle_received` need filling in. + (void)dest; + ESP_LOGE(TAG, "bbnow_send_priv: not implemented"); + return ESP_ERR_NOT_SUPPORTED; +} + +/// Handles a packet that arrived over esp_now. Routing on `item.kind` already +/// accounts for private packets even though their handling is still scaffolded. +static void bbnow_handle_received(const BBNowQueueItem& item) { + switch (item.kind) { + case BBNowPacketKind::PUB: { + auto pkt = BBNowPubPacket::deserialize(std::span(item.payload, item.len)); + if (!pkt.has_value()) { + ESP_LOGW(TAG, "received pub packet failed to deserialize"); + return; + } + ESP_LOGI(TAG, "received PUB packet: type=%d", static_cast(pkt->packet_type)); + if (pkt->packet_type == BBNowPubPacketType::DISCOVERY) { + ESP_LOGI(TAG, " DISCOVERY expantion=%d", static_cast(pkt->packet_data.discovery.expantion_type)); + } + break; + } + case BBNowPacketKind::PRIV: + // SCAFFOLD: private packet handling not implemented yet. + ESP_LOGW(TAG, "received PRIV packet (%u bytes) -- handling not implemented", item.len); + break; + } +} + +static void bbnow_task(void* pv) { + (void)pv; + BBNowQueueItem item; + for (;;) { + if (xQueueReceive(g_bbnow_queue, &item, portMAX_DELAY) != pdTRUE) { + continue; + } + switch (item.type) { + case BBNowQueueItemType::TX: { + esp_err_t err = esp_now_send(item.addr, item.payload, item.len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_now_send failed: %s", esp_err_to_name(err)); + } + break; + } + case BBNowQueueItemType::RX: + bbnow_handle_received(item); + break; + } + } +} + +void bbnow_start_task() { + if (g_bbnow_queue == NULL) { + g_bbnow_queue = xQueueCreate(BBNOW_QUEUE_LEN, sizeof(BBNowQueueItem)); + if (g_bbnow_queue == NULL) { + ESP_LOGE(TAG, "failed to create bbnow queue"); + return; + } + } + if (g_bbnow_task_handle == NULL) { + BaseType_t rc = xTaskCreate(bbnow_task, "bbnow_task", BBNOW_TASK_STACK, NULL, BBNOW_TASK_PRIO, &g_bbnow_task_handle); + if (rc != pdPASS) { + ESP_LOGE(TAG, "failed to create bbnow task"); + } else { + ESP_LOGI(TAG, "bbnow task started"); + } + } +} + +void init_espnow(void) { + ESP_LOGI(TAG, "Intializing espnow..."); + ESP_ERROR_CHECK( esp_now_init() ); + ESP_ERROR_CHECK( esp_now_register_send_cb(espnow_send_cb) ); + ESP_ERROR_CHECK( esp_now_register_recv_cb(espnow_recv_cb) ); + + /* Set primary master key. */ + // TODO: add provisions for encryption + // ESP_ERROR_CHECK( esp_now_set_pmk(ESPNOW_PMK)); + + // Add broadcast peer information to peer list, allowing us to send to it + esp_now_peer_info_t peer = { + .peer_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, + .lmk = {}, + .channel = 0, + .ifidx = WIFI_IF_AP, + .encrypt = false, + .priv = NULL, + }; + ESP_ERROR_CHECK( esp_now_add_peer(&peer) ); + + // Create the queue used to hand packets between callers, the esp_now + // callbacks, and the bbnow task. The task itself is started separately + // (see bbnow_start_task) so its lifecycle is explicit. + if (g_bbnow_queue == NULL) { + g_bbnow_queue = xQueueCreate(BBNOW_QUEUE_LEN, sizeof(BBNowQueueItem)); + if (g_bbnow_queue == NULL) { + ESP_LOGE(TAG, "failed to create bbnow queue"); + } + } +} diff --git a/drivers/wifi.cpp b/drivers/wifi.cpp new file mode 100644 index 0000000..616b12a --- /dev/null +++ b/drivers/wifi.cpp @@ -0,0 +1,3 @@ +#include "blk_box_drivers/wifi.hpp" + + diff --git a/idf_component.yml b/idf_component.yml index 8347ad9..7ccc8ec 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -5,7 +5,7 @@ dependencies: version: '>=6.0.0' espressif/led_strip: ^3.0.3 - # atanisoft/esp_lcd_ili9488: ^1.1.1 - atanisoft/esp_lcd_ili9488: - path: ../../../esp_lcd_ili9488 + atanisoft/esp_lcd_ili9488: ^1.1.1 + # atanisoft/esp_lcd_ili9488: + # path: ../../../esp_lcd_ili9488 lvgl/lvgl: ^8.4 diff --git a/include/blk_box_drivers/bbnow.hpp b/include/blk_box_drivers/bbnow.hpp deleted file mode 100644 index 3c3d934..0000000 --- a/include/blk_box_drivers/bbnow.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef BBNOW_HPP -#define BBNOW_HPP - -#include "esp_now.h" - -/// The channel to use if not on a WIFI network -#define BBNOW_DEFAULT_CHANNEL 6 -const static uint8_t BROADCAST_MAC[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - -void init_espnow(); - -#endif /* BBNOW_HPP */ diff --git a/include/blk_box_drivers/bbnow_old.hpp b/include/blk_box_drivers/bbnow_old.hpp new file mode 100644 index 0000000..ef9f375 --- /dev/null +++ b/include/blk_box_drivers/bbnow_old.hpp @@ -0,0 +1,59 @@ +#ifndef _BBNOW_OLD_HPP +#define _BBNOW_OLD_HPP + +#include +#include "esp_now.h" +#include "bbnow_pub.hpp" // BBNowPubPacket, BBNowPubPacketType + +/// The channel to use if not on a WIFI network +#define BBNOW_DEFAULT_CHANNEL 6 +const static uint8_t BROADCAST_MAC[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +/// Whether a queue item carries a packet to transmit or one that was received. +enum class BBNowQueueItemType : uint8_t { + TX = 0, /// a packet to send over esp_now + RX = 1, /// a packet received over esp_now +}; + +/// The high-level protocol family of a packet flowing through the queue. +/// `PRIV` is plumbed end-to-end but not yet implemented (see `bbnow_send_priv`). +enum class BBNowPacketKind : uint8_t { + PUB = 0, /// a BBNOW public packet + PRIV = 1, /// a BBNOW private packet (handling not implemented yet) +}; + +/// Item handed between callers, the esp_now callbacks, and the bbnow task. +/// A fixed-size buffer is used (rather than `std::vector`) so the item can be +/// copied by value through a FreeRTOS queue without dangling ownership. +struct BBNowQueueItem { + BBNowQueueItemType type{}; + BBNowPacketKind kind{}; + uint8_t addr[ESP_NOW_ETH_ALEN]{}; /// dest MAC for TX, src MAC for RX + uint8_t payload[ESP_NOW_MAX_DATA_LEN]{}; + size_t len{}; +}; + +/// Initializes esp_now and the bbnow packet queue. +void init_espnow(); + +/// Serializes and enqueues a public BBNOW packet for transmission. +/// \return ESP_OK if queued, or an error if the packet failed to serialize +/// or the bbnow task/queue is not running. +esp_err_t bbnow_send_pub(const BBNowPubPacket& pkt, const uint8_t* dest = BROADCAST_MAC); + +/// Serializes and enqueues a private BBNOW packet for transmission. +/// \note SCAFFOLD: not yet implemented. Returns ESP_ERR_NOT_SUPPORTED. +/// When implemented it will mirror `bbnow_send_pub`, setting +/// `kind = BBNowPacketKind::PRIV` so the task and RX handler already +/// route it correctly. +esp_err_t bbnow_send_priv(const uint8_t* dest); + +/// Creates the bbnow task that drains the queue: it sends TX items via +/// esp_now and dispatches RX items to the packet handlers. Idempotent. +void bbnow_start_task(); + +/// Hands a received packet to the bbnow queue for handling. +/// Used by the esp_now receive callback and for demos/tests of the RX path. +esp_err_t bbnow_enqueue_received(const uint8_t* src, const uint8_t* data, size_t len); + +#endif /* _BBNOW_OLD_HPP */ diff --git a/include/blk_box_drivers/wifi.hpp b/include/blk_box_drivers/wifi.hpp new file mode 100644 index 0000000..c446cdd --- /dev/null +++ b/include/blk_box_drivers/wifi.hpp @@ -0,0 +1,6 @@ +#ifndef _WIFI_HPP +#define _WIFI_HPP + + + +#endif /* _WIFI_HPP */