ESPHome 2026.2.4
Loading...
Searching...
No Matches
ble_nus.cpp
Go to the documentation of this file.
1#ifdef USE_ZEPHYR
2#include "ble_nus.h"
3#include <zephyr/kernel.h>
4#include <bluetooth/services/nus.h>
5#include "esphome/core/log.h"
6#ifdef USE_LOGGER
9#endif
10#include <zephyr/sys/ring_buffer.h>
11
13
14constexpr size_t BLE_TX_BUF_SIZE = 2048;
15
16// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
18RING_BUF_DECLARE(global_ble_tx_ring_buf, BLE_TX_BUF_SIZE);
19// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
20
21static const char *const TAG = "ble_nus";
22
23size_t BLENUS::write_array(const uint8_t *data, size_t len) {
24 if (atomic_get(&this->tx_status_) == TX_DISABLED) {
25 return 0;
26 }
27 return ring_buf_put(&global_ble_tx_ring_buf, data, len);
28}
29
30void BLENUS::connected(bt_conn *conn, uint8_t err) {
31 if (err == 0) {
32 global_ble_nus->conn_.store(bt_conn_ref(conn));
33 }
34}
35
36void BLENUS::disconnected(bt_conn *conn, uint8_t reason) {
37 if (global_ble_nus->conn_) {
38 bt_conn_unref(global_ble_nus->conn_.load());
39 // Connection array is global static.
40 // Reference can be kept even if disconnected.
41 }
42}
43
44void BLENUS::tx_callback(bt_conn *conn) {
45 atomic_cas(&global_ble_nus->tx_status_, TX_BUSY, TX_ENABLED);
46 ESP_LOGVV(TAG, "Sent operation completed");
47}
48
49void BLENUS::send_enabled_callback(bt_nus_send_status status) {
50 switch (status) {
51 case BT_NUS_SEND_STATUS_ENABLED:
52 atomic_set(&global_ble_nus->tx_status_, TX_ENABLED);
53#ifdef USE_LOGGER
56 }
57#endif
58 ESP_LOGD(TAG, "NUS notification has been enabled");
59 break;
60 case BT_NUS_SEND_STATUS_DISABLED:
61 atomic_set(&global_ble_nus->tx_status_, TX_DISABLED);
62 ESP_LOGD(TAG, "NUS notification has been disabled");
63 break;
64 }
65}
66
67void BLENUS::rx_callback(bt_conn *conn, const uint8_t *const data, uint16_t len) {
68 ESP_LOGD(TAG, "Received %d bytes.", len);
69}
70
72 bt_nus_cb callbacks = {
73 .received = rx_callback,
74 .sent = tx_callback,
75 .send_enabled = send_enabled_callback,
76 };
77
78 bt_nus_init(&callbacks);
79
80 static bt_conn_cb conn_callbacks = {
81 .connected = BLENUS::connected,
82 .disconnected = BLENUS::disconnected,
83 };
84
85 bt_conn_cb_register(&conn_callbacks);
86
87 global_ble_nus = this;
88#ifdef USE_LOGGER
89 if (logger::global_logger != nullptr && this->expose_log_) {
91 }
92#endif
93}
94
95#ifdef USE_LOGGER
96void BLENUS::on_log(uint8_t level, const char *tag, const char *message, size_t message_len) {
97 (void) level;
98 (void) tag;
99 this->write_array(reinterpret_cast<const uint8_t *>(message), message_len);
100 const char c = '\n';
101 this->write_array(reinterpret_cast<const uint8_t *>(&c), 1);
102}
103#endif
104
106 ESP_LOGCONFIG(TAG,
107 "ble nus:\n"
108 " log: %s",
109 YESNO(this->expose_log_));
110 uint32_t mtu = 0;
111 bt_conn *conn = this->conn_.load();
112 if (conn) {
113 mtu = bt_nus_get_mtu(conn);
114 }
115 ESP_LOGCONFIG(TAG, " MTU: %u", mtu);
116}
117
119 if (ring_buf_is_empty(&global_ble_tx_ring_buf)) {
120 return;
121 }
122
123 if (!atomic_cas(&this->tx_status_, TX_ENABLED, TX_BUSY)) {
124 if (atomic_get(&this->tx_status_) == TX_DISABLED) {
125 ring_buf_reset(&global_ble_tx_ring_buf);
126 }
127 return;
128 }
129
130 bt_conn *conn = this->conn_.load();
131 if (conn) {
132 conn = bt_conn_ref(conn);
133 }
134
135 if (nullptr == conn) {
136 atomic_cas(&this->tx_status_, TX_BUSY, TX_ENABLED);
137 return;
138 }
139
140 uint32_t req_len = bt_nus_get_mtu(conn);
141
142 uint8_t *buf;
143 uint32_t size = ring_buf_get_claim(&global_ble_tx_ring_buf, &buf, req_len);
144
145 int err, err2;
146
147 err = bt_nus_send(conn, buf, size);
148 err2 = ring_buf_get_finish(&global_ble_tx_ring_buf, size);
149 if (err2) {
150 // It should no happen.
151 ESP_LOGE(TAG, "Size %u exceeds valid bytes in the ring buffer (%d error)", size, err2);
152 }
153 if (err == 0) {
154 ESP_LOGVV(TAG, "Sent %d bytes", size);
155 } else {
156 ESP_LOGE(TAG, "Failed to send %d bytes (%d error)", size, err);
157 atomic_cas(&this->tx_status_, TX_BUSY, TX_ENABLED);
158 }
159 bt_conn_unref(conn);
160}
161
162} // namespace esphome::ble_nus
163#endif
uint8_t status
Definition bl0942.h:8
static void disconnected(bt_conn *conn, uint8_t reason)
Definition ble_nus.cpp:36
static void rx_callback(bt_conn *conn, const uint8_t *data, uint16_t len)
Definition ble_nus.cpp:67
static void connected(bt_conn *conn, uint8_t err)
Definition ble_nus.cpp:30
void setup() override
Definition ble_nus.cpp:71
void loop() override
Definition ble_nus.cpp:118
std::atomic< bt_conn * > conn_
Definition ble_nus.h:42
static void tx_callback(bt_conn *conn)
Definition ble_nus.cpp:44
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override
Definition ble_nus.cpp:96
void dump_config() override
Definition ble_nus.cpp:105
size_t write_array(const uint8_t *data, size_t len)
Definition ble_nus.cpp:23
static void send_enabled_callback(bt_nus_send_status status)
Definition ble_nus.cpp:49
void add_log_listener(LogListener *listener)
Register a log listener to receive log messages.
Definition logger.h:191
const char * message
Definition component.cpp:38
BLENUS * global_ble_nus
Definition ble_nus.cpp:17
constexpr size_t BLE_TX_BUF_SIZE
Definition ble_nus.cpp:14
RING_BUF_DECLARE(global_ble_tx_ring_buf, BLE_TX_BUF_SIZE)
Logger * global_logger
Definition logger.cpp:279
std::string size_t len
Definition helpers.h:692
size_t size
Definition helpers.h:729
Application App
Global storage of Application pointer - only one Application can exist.