3#include <zephyr/kernel.h>
4#include <bluetooth/services/nus.h>
10#include <zephyr/sys/ring_buffer.h>
17#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
22static const char *
const TAG =
"ble_nus";
25 if (atomic_get(&this->
tx_status_) == TX_DISABLED) {
31 if (ring_buf_space_get(&global_ble_tx_ring_buf) <
len) {
32 ESP_LOGE(TAG,
"TX dropping %u bytes",
len);
35 ring_buf_put(&global_ble_tx_ring_buf, data,
len);
36#ifdef USE_UART_DEBUGGER
37 for (
size_t i = 0; i <
len; i++) {
44#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
63#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
73#ifdef USE_UART_DEBUGGER
84 if (ring_buf_get(&global_ble_rx_ring_buf, data,
len) !=
len) {
85 ESP_LOGE(TAG,
"UART BLE unexpected size");
88#ifdef USE_UART_DEBUGGER
89 for (
size_t i = 0; i <
len; i++) {
100#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
101 uint32_t size = ring_buf_size_get(&global_ble_rx_ring_buf);
102 ESP_LOGVV(TAG,
"UART BLE available %u",
size);
110 constexpr uint32_t timeout_500ms = 500;
112 while (atomic_get(&this->
tx_status_) != TX_DISABLED && !ring_buf_is_empty(&global_ble_tx_ring_buf)) {
113 if (
millis() - start > timeout_500ms) {
114 ESP_LOGW(TAG,
"Flush timeout");
140 ESP_LOGVV(TAG,
"Sent operation completed");
145 case BT_NUS_SEND_STATUS_ENABLED:
152 ESP_LOGD(TAG,
"NUS notification has been enabled");
154 case BT_NUS_SEND_STATUS_DISABLED:
156 ESP_LOGD(TAG,
"NUS notification has been disabled");
161 ESP_LOGV(TAG,
"Received %d bytes.",
len);
162#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
163 auto recv_len = ring_buf_put(&global_ble_rx_ring_buf, data,
len);
164 if (recv_len <
len) {
165 ESP_LOGE(TAG,
"RX dropping %u bytes",
len - recv_len);
170#ifdef ESPHOME_BLE_NUS_RX_RING_BUFFER_SIZE
173 bt_nus_cb callbacks = {
179 bt_nus_init(&callbacks);
181 static bt_conn_cb conn_callbacks = {
186 bt_conn_cb_register(&conn_callbacks);
192 this, [](
void *self, uint8_t level,
const char *
tag,
const char *
message,
size_t message_len) {
204 if (ring_buf_space_get(&global_ble_tx_ring_buf) < message_len + 1) {
209 this->
write_array(
reinterpret_cast<const uint8_t *
>(&c), 1);
215 bt_conn *conn = this->
conn_.load();
217 mtu = bt_nus_get_mtu(conn);
228 if (ring_buf_is_empty(&global_ble_tx_ring_buf)) {
232 if (!atomic_cas(&this->
tx_status_, TX_ENABLED, TX_BUSY)) {
233 if (atomic_get(&this->
tx_status_) == TX_DISABLED) {
234 ring_buf_reset(&global_ble_tx_ring_buf);
239 bt_conn *conn = this->
conn_.load();
241 conn = bt_conn_ref(conn);
244 if (
nullptr == conn) {
245 atomic_cas(&this->
tx_status_, TX_BUSY, TX_ENABLED);
249 uint32_t req_len = bt_nus_get_mtu(conn);
252 uint32_t size = ring_buf_get_claim(&global_ble_tx_ring_buf, &buf, req_len);
256 err = bt_nus_send(conn, buf,
size);
257 err2 = ring_buf_get_finish(&global_ble_tx_ring_buf,
size);
260 ESP_LOGE(TAG,
"Size %u exceeds valid bytes in the ring buffer (%d error)",
size, err2);
263 ESP_LOGVV(TAG,
"Sent %d bytes",
size);
265 ESP_LOGE(TAG,
"Failed to send %d bytes (%d error)",
size, err);
266 atomic_cas(&this->
tx_status_, TX_BUSY, TX_ENABLED);
void schedule_dump_config()
static void disconnected(bt_conn *conn, uint8_t reason)
static void rx_callback(bt_conn *conn, const uint8_t *data, uint16_t len)
size_t available() override
static void connected(bt_conn *conn, uint8_t err)
uart::UARTFlushResult flush() override
bool read_array(uint8_t *data, size_t len) override
std::atomic< bt_conn * > conn_
static void tx_callback(bt_conn *conn)
void write_array(const uint8_t *data, size_t len) override
bool peek_byte(uint8_t *data) override
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len)
void dump_config() override
std::atomic< bool > connected_
static void send_enabled_callback(bt_nus_send_status status)
void add_log_callback(void *instance, void(*fn)(void *, uint8_t, const char *, const char *, size_t))
Register a log callback to receive log messages.
bool read_byte(uint8_t *data)
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
const LogString * message
RING_BUF_DECLARE(global_ble_tx_ring_buf, ESPHOME_BLE_NUS_TX_RING_BUFFER_SIZE)
UARTFlushResult
Result of a flush() call.
@ UART_FLUSH_RESULT_SUCCESS
Confirmed: all bytes left the TX FIFO.
@ UART_FLUSH_RESULT_TIMEOUT
Confirmed: timed out before TX completed.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.