mirror of
https://github.com/esphome/esphome.git
synced 2025-09-04 12:22:20 +01:00
add ble nus
This commit is contained in:
@@ -40,16 +40,23 @@ void Logger::write_header_(int level, const char *tag, int line) {
|
|||||||
const char *color = LOG_LEVEL_COLORS[level];
|
const char *color = LOG_LEVEL_COLORS[level];
|
||||||
const char *letter = LOG_LEVEL_LETTERS[level];
|
const char *letter = LOG_LEVEL_LETTERS[level];
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
void *current_task = xTaskGetCurrentTaskHandle();
|
void * current_task = xTaskGetCurrentTaskHandle();
|
||||||
|
#elif defined(USE_ZEPHYR)
|
||||||
|
k_tid_t current_task = k_current_get();
|
||||||
|
#else
|
||||||
|
void * current_task = nullptr;
|
||||||
|
#endif
|
||||||
if (current_task == main_task_) {
|
if (current_task == main_task_) {
|
||||||
#endif
|
|
||||||
this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
|
this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
} else {
|
} else {
|
||||||
this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
|
#ifdef USE_ARDUINO
|
||||||
ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), pcTaskGetName(current_task), color);
|
const char *thread_name = pcTaskGetName(current_task);
|
||||||
}
|
#elif defined(USE_ZEPHYR)
|
||||||
|
const char *thread_name = k_thread_name_get(current_task);
|
||||||
#endif
|
#endif
|
||||||
|
this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
|
||||||
|
ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
|
||||||
@@ -149,6 +156,8 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
|
|||||||
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
this->main_task_ = xTaskGetCurrentTaskHandle();
|
this->main_task_ = xTaskGetCurrentTaskHandle();
|
||||||
|
#elif defined(USE_ZEPHYR)
|
||||||
|
this->main_task_ = k_current_get();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -77,6 +77,9 @@ void Logger::pre_setup() {
|
|||||||
|
|
||||||
#ifdef USE_ZEPHYR
|
#ifdef USE_ZEPHYR
|
||||||
void HOT Logger::write_msg_(const char *msg) {
|
void HOT Logger::write_msg_(const char *msg) {
|
||||||
|
#ifdef CONFIG_PRINTK
|
||||||
|
printk("%s\n", msg);
|
||||||
|
#endif
|
||||||
if (nullptr == uart_dev_) {
|
if (nullptr == uart_dev_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
29
esphome/components/zephyr_ble_nus/__init__.py
Normal file
29
esphome/components/zephyr_ble_nus/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_LOG,
|
||||||
|
)
|
||||||
|
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||||
|
|
||||||
|
DEPENDENCIES = ["zephyr_ble_server"]
|
||||||
|
|
||||||
|
zephyr_ble_nus_ns = cg.esphome_ns.namespace("zephyr_ble_nus")
|
||||||
|
BLENUS = zephyr_ble_nus_ns.class_("BLENUS", cg.Component)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(BLENUS),
|
||||||
|
cv.Optional(CONF_LOG, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
cv.only_with_zephyr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
zephyr_add_prj_conf("BT_NUS", True)
|
||||||
|
cg.add(var.set_expose_log(config[CONF_LOG]))
|
||||||
|
await cg.register_component(var, config)
|
135
esphome/components/zephyr_ble_nus/ble_nus.cpp
Normal file
135
esphome/components/zephyr_ble_nus/ble_nus.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "ble_nus.h"
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <bluetooth/services/nus.h>
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#ifdef USE_LOGGER
|
||||||
|
#include "esphome/components/logger/logger.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace zephyr_ble_nus {
|
||||||
|
|
||||||
|
BLENUS *global_ble_nus; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
|
static const char *const TAG = "zephyr_ble_nus";
|
||||||
|
|
||||||
|
size_t BLENUS::write_array(const uint8_t *data, size_t len) {
|
||||||
|
if (atomic_get(&tx_status_) == TX_DISABLED) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ring_buf_put(&tx_ringbuf_, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::connected_(bt_conn *conn, uint8_t err) {
|
||||||
|
if (err == 0) {
|
||||||
|
global_ble_nus->conn_ = bt_conn_ref(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::disconnected_(bt_conn *conn, uint8_t reason) {
|
||||||
|
bt_conn_unref(global_ble_nus->conn_);
|
||||||
|
global_ble_nus->conn_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::tx_callback_(bt_conn *conn) {
|
||||||
|
atomic_cas(&global_ble_nus->tx_status_, TX_BUSY, TX_ENABLED);
|
||||||
|
ESP_LOGVV(TAG, "Sent operation completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::send_enabled_callback_(bt_nus_send_status status) {
|
||||||
|
switch (status) {
|
||||||
|
case BT_NUS_SEND_STATUS_ENABLED:
|
||||||
|
atomic_set(&global_ble_nus->tx_status_, TX_ENABLED);
|
||||||
|
#ifdef USE_LOGGER
|
||||||
|
App.schedule_dump_config();
|
||||||
|
#endif
|
||||||
|
ESP_LOGD(TAG, "NUS notification has been enabled");
|
||||||
|
break;
|
||||||
|
case BT_NUS_SEND_STATUS_DISABLED:
|
||||||
|
atomic_set(&global_ble_nus->tx_status_, TX_DISABLED);
|
||||||
|
ESP_LOGD(TAG, "NUS notification has been disabled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::rx_callback_(bt_conn *conn, const uint8_t *const data, uint16_t len) {
|
||||||
|
ESP_LOGD(TAG, "Received %d bytes.", len);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLENUS::BLENUS(size_t buffer_size) {
|
||||||
|
uint8_t *buffer = new uint8_t[buffer_size];
|
||||||
|
ring_buf_init(&tx_ringbuf_, buffer_size, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::setup() {
|
||||||
|
bt_nus_cb callbacks = {
|
||||||
|
.received = rx_callback_,
|
||||||
|
.sent = tx_callback_,
|
||||||
|
.send_enabled = send_enabled_callback_,
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_nus_init(&callbacks);
|
||||||
|
|
||||||
|
static bt_conn_cb conn_callbacks = {
|
||||||
|
.connected = BLENUS::connected_,
|
||||||
|
.disconnected = BLENUS::disconnected_,
|
||||||
|
};
|
||||||
|
|
||||||
|
bt_conn_cb_register(&conn_callbacks);
|
||||||
|
|
||||||
|
global_ble_nus = this;
|
||||||
|
#ifdef USE_LOGGER
|
||||||
|
if (logger::global_logger != nullptr && this->expose_log_) {
|
||||||
|
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
||||||
|
this->write_array(reinterpret_cast<const uint8_t *>(message), strlen(message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ble nus:");
|
||||||
|
ESP_LOGCONFIG(TAG, " log: %s", YESNO(this->expose_log_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BLENUS::loop() {
|
||||||
|
if (ring_buf_is_empty(&tx_ringbuf_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atomic_cas(&tx_status_, TX_ENABLED, TX_BUSY)) {
|
||||||
|
ring_buf_reset(&tx_ringbuf_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_conn *conn = bt_conn_ref(conn_);
|
||||||
|
|
||||||
|
if (nullptr == conn) {
|
||||||
|
atomic_cas(&tx_status_, TX_BUSY, TX_ENABLED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t req_len = bt_nus_get_mtu(conn);
|
||||||
|
|
||||||
|
uint8_t *buf;
|
||||||
|
uint32_t size = ring_buf_get_claim(&tx_ringbuf_, &buf, req_len);
|
||||||
|
|
||||||
|
int err, err2;
|
||||||
|
|
||||||
|
err = bt_nus_send(conn, buf, size);
|
||||||
|
err2 = ring_buf_get_finish(&tx_ringbuf_, size);
|
||||||
|
if (err2) {
|
||||||
|
ESP_LOGE(TAG, "Failed to ring buf finish (%d error)", err2);
|
||||||
|
}
|
||||||
|
if (err == 0) {
|
||||||
|
ESP_LOGVV(TAG, "Sent %d bytes", size);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Failed to send %d bytes (%d error)", size, err);
|
||||||
|
atomic_cas(&tx_status_, TX_BUSY, TX_ENABLED);
|
||||||
|
}
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace zephyr_ble_nus
|
||||||
|
} // namespace esphome
|
40
esphome/components/zephyr_ble_nus/ble_nus.h
Normal file
40
esphome/components/zephyr_ble_nus/ble_nus.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include <zephyr/sys/ring_buffer.h>
|
||||||
|
#include <shell/shell_bt_nus.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace zephyr_ble_nus {
|
||||||
|
|
||||||
|
class BLENUS : public Component {
|
||||||
|
enum tx_status {
|
||||||
|
TX_DISABLED,
|
||||||
|
TX_ENABLED,
|
||||||
|
TX_BUSY,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BLENUS(size_t buffer_size = 1024);
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
void loop() override;
|
||||||
|
size_t write_array(const uint8_t *data, size_t len);
|
||||||
|
void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void send_enabled_callback_(bt_nus_send_status status);
|
||||||
|
static void tx_callback_(bt_conn *conn);
|
||||||
|
static void rx_callback_(bt_conn *conn, const uint8_t *const data, uint16_t len);
|
||||||
|
static void connected_(bt_conn *conn, uint8_t err);
|
||||||
|
static void disconnected_(bt_conn *conn, uint8_t reason);
|
||||||
|
|
||||||
|
bt_conn *conn_ = nullptr;
|
||||||
|
ring_buf tx_ringbuf_;
|
||||||
|
bool expose_log_ = false;
|
||||||
|
atomic_t tx_status_ = ATOMIC_INIT(TX_DISABLED);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace zephyr_ble_nus
|
||||||
|
} // namespace esphome
|
@@ -10,21 +10,28 @@ namespace zephyr_ble_server {
|
|||||||
static const char *const TAG = "zephyr_ble_server";
|
static const char *const TAG = "zephyr_ble_server";
|
||||||
|
|
||||||
static struct k_work advertise_work;
|
static struct k_work advertise_work;
|
||||||
static const struct bt_data AD[] = {
|
|
||||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
|
||||||
#ifdef USE_OTA
|
|
||||||
|
|
||||||
|
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
|
||||||
|
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
|
||||||
|
|
||||||
|
static const struct bt_data AD[] = {
|
||||||
|
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||||
|
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct bt_data SD[] = {
|
||||||
|
#ifdef USE_OTA
|
||||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d,
|
BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d,
|
||||||
0xdc, 0x53, 0x8d),
|
0xdc, 0x53, 0x8d),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct bt_le_adv_param* const ADV_PARAM = BT_LE_ADV_CONN_NAME;
|
const struct bt_le_adv_param* const ADV_PARAM = BT_LE_ADV_CONN;
|
||||||
|
|
||||||
static void advertise(struct k_work *work) {
|
static void advertise(struct k_work *work) {
|
||||||
bt_le_adv_stop();
|
bt_le_adv_stop();
|
||||||
|
|
||||||
int rc = bt_le_adv_start(ADV_PARAM, AD, ARRAY_SIZE(AD), NULL, 0);
|
int rc = bt_le_adv_start(ADV_PARAM, AD, ARRAY_SIZE(AD), SD, ARRAY_SIZE(SD));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc);
|
ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc);
|
||||||
return;
|
return;
|
||||||
|
@@ -30,11 +30,6 @@ interval:
|
|||||||
then:
|
then:
|
||||||
- switch.toggle: gpio_15
|
- switch.toggle: gpio_15
|
||||||
|
|
||||||
# sensor:
|
|
||||||
# - platform: uptime
|
|
||||||
# name: Uptime Sensor
|
|
||||||
# update_interval: 5s
|
|
||||||
|
|
||||||
output:
|
output:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin:
|
pin:
|
||||||
@@ -71,3 +66,6 @@ ota:
|
|||||||
- logger.log: "OTA start"
|
- logger.log: "OTA start"
|
||||||
|
|
||||||
zephyr_ble_server:
|
zephyr_ble_server:
|
||||||
|
|
||||||
|
zephyr_ble_nus:
|
||||||
|
log: true
|
||||||
|
Reference in New Issue
Block a user